From 85aebfd5f8a0af56bf2bea4d3b2c51238fe3590c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 5 Apr 2023 16:14:54 +0100 Subject: [PATCH 001/331] Added first attempt to implement explicit Full-F collisions using the RMJ form of the Landau ion-ion collision operator. Problems are to be expected from a division by 1/vperp^2. Tests are to follow. --- src/fokker_planck.jl | 259 +++++++++++++++++++++++++++++++++++ src/input_structs.jl | 3 + src/moment_kinetics.jl | 5 +- src/moment_kinetics_input.jl | 4 +- src/time_advance.jl | 36 +++-- 5 files changed, 292 insertions(+), 15 deletions(-) create mode 100644 src/fokker_planck.jl diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl new file mode 100644 index 000000000..5bc9cb779 --- /dev/null +++ b/src/fokker_planck.jl @@ -0,0 +1,259 @@ +""" +module for including the Full-F Fokker-Planck Collision Operator +""" +module fokker_planck + + +export init_fokker_planck_collisions +export explicit_fokker_planck_collisions! + +using SpecialFunctions: ellipk, ellipe +using ..type_definitions: mk_float, mk_int +using ..array_allocation: allocate_float, allocate_shared_float +using ..communication: Array +using ..velocity_moments: integrate_over_vspace +using ..calculus: derivative!, second_derivative! +using ..looping +""" +a struct of dummy arrays and precalculated coefficients +for the Fokker-Planck collision operator +""" + +struct fokkerplanck_arrays_struct + elliptic_integral_E_factor::Array{mk_float,4} + elliptic_integral_K_factor::Array{mk_float,4} + Rosenbluth_G::Array{mk_float,2} + Rosenbluth_H::Array{mk_float,2} + buffer_vpavperp_1::Array{mk_float,2} + buffer_vpavperp_2::Array{mk_float,2} + #Cssp_result_vpavperp::Array{mk_float,2} +end + +""" +allocate the required ancilliary arrays +""" + +function allocate_fokkerplanck_arrays(vperp,vpa) + nvpa = vpa.n + nvperp = vperp.n + + elliptic_integral_E_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) + elliptic_integral_K_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) + Rosenbluth_G = allocate_float(nvpa,nvperp) + Rosenbluth_H = allocate_float(nvpa,nvperp) + buffer_vpavperp_1 = allocate_float(nvpa,nvperp) + buffer_vpavperp_2 = allocate_float(nvpa,nvperp) + #Cssp_result_vpavperp = allocate_float(nvpa,nvperp) + + return fokkerplanck_arrays_struct(elliptic_integral_E_factor,elliptic_integral_K_factor, + Rosenbluth_G,Rosenbluth_H, + buffer_vpavperp_1,buffer_vpavperp_2) + #Cssp_result_vpavperp) +end + + +# initialise the elliptic integral factor arrays +# note the definitions of ellipe & ellipk +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipk` +# `ellipe(m) = \int^{\pi/2}\_0 \sqrt{ 1 - m \sin^2(\theta)} d \theta` +# `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` + +function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_integral_K_factor, vperp, vpa) + + # must loop over vpa, vperp, vpa', vperp' + # hence mix of looping macros for unprimed variables + # & standard local `for' loop for primed variables + nvperp = vperp.n + nvpa = vpa.n + zero = 1.0e-10 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + for ivperp in 1:nvperp + for ivpa in 1:nvpa + # the argument of the elliptic integrals + # mm = 4 vperp vperp' / ( (vpa- vpa')^2 + (vperp + vperp')) + denom = (vpa.grid[ivpa] - vpa.grid[ivpap])^2 + (vperp.grid[ivperp] + vperp.grid[ivperpp])^2 + if denom < zero + #then vpa = vpa' = vperp' = vperp = 0 + mm = 0.0 + prefac = 0.0 # because vperp' wgt = 0 here + else + mm = 4.0*vperp.grid[ivperp]*vperp.grid[ivperpp]/denom + prefac = sqrt(denom) + end + elliptic_integral_E_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipe(mm)*prefac/pi + elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipk(mm)/(pi*prefac) + end + end + end + end + +end + +""" +function that initialises the arrays needed for Fokker Planck collisions +""" + +function init_fokker_planck_collisions(vperp,vpa) + fokkerplanck_arrays = allocate_fokkerplanck_arrays(vperp,vpa) + @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, + fokkerplanck_arrays.elliptic_integral_K_factor, + vperp,vpa) + return fokkerplanck_arrays +end + +""" +calculates the (normalised) Rosenbluth potential G +""" +# G(vpa,vperp) = \int^\infty_0 \int^{\infty}_{-\infty} ((vpa- vpa')^2 + (vperp + vperp'))^{1/2} +# * (2 ellipe(mm)/ \pi) F(vpa',vperp') (2 vperp'/\sqrt{\pi}) d vperp' d vpa' + + +function calculate_Rosenbluth_potentials!(Rosenbluth_G,Rosenbluth_H,fsp_in, + elliptic_integral_E_factor,elliptic_integral_K_factor,buffer_vpavperp,vperp,vpa) + + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + # G + @views @. buffer_vpavperp[:,:] = fsp_in*elliptic_integral_E_factor[ivpa,ivperp,:,:] + @views Rosenbluth_G[ivpa,ivperp] = integrate_over_vspace(buffer_vpavperp, vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + # H + @views @. buffer_vpavperp[:,:] = fsp_in*elliptic_integral_K_factor[ivpa,ivperp,:,:] + @views Rosenbluth_H[ivpa,ivperp] = integrate_over_vspace(buffer_vpavperp, vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + end + end + +end + + +""" +returns (normalised) C[Fs,Fs'] + +""" +#returns (normalised) C[F_s,F_s'] = C[F_s,F_s'](vpa,vperp) given inputs +#distribution F_s = F_s(vpa,vperp) +#distribution F_s' = F_s'(vpa,vperp) +#mass m_s +#mass m_s' +#collision frequency nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 +#with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 +function evaluate_RMJ_collision_operator!(Cssp_out,fs_in,fsp_in,ms,msp,cfreqssp, fokkerplanck_arrays::fokkerplanck_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral) + # calculate the Rosenbluth potentials + # and store in fokkerplanck_arrays_struct + @views calculate_Rosenbluth_potentials!(fokkerplanck_arrays.Rosenbluth_G,fokkerplanck_arrays.Rosenbluth_H,fsp_in, + fokkerplanck_arrays.elliptic_integral_E_factor, + fokkerplanck_arrays.elliptic_integral_K_factor, + fokkerplanck_arrays.buffer_vpavperp_1,vperp,vpa) + + # short names for buffer arrays + buffer_1 = fokkerplanck_arrays.buffer_vpavperp_1 + buffer_2 = fokkerplanck_arrays.buffer_vpavperp_2 + Rosenbluth_G = fokkerplanck_arrays.Rosenbluth_G + Rosenbluth_H = fokkerplanck_arrays.Rosenbluth_G + nvperp = vperp.n + nvpa = vpa.n + # zero Cssp to prepare for addition of collision terms + Cssp_out .= 0.0 + + # + d^2 F_s / d vpa^2 * d^2 G_sp / d vpa^2 + for ivperp in 1:nvperp + vpa.scratch2 .= 1.0 # remove Q argument from second_derivative! as never different from 1? + @views second_derivative!(vpa.scratch, fs_in[:,ivperp], vpa.scratch2, vpa, vpa_spectral) + @views @. buffer_1[:,ivperp] = vpa.scratch + @views second_derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa.scratch2, vpa, vpa_spectral) + @views @. buffer_2[:,ivperp] = vpa.scratch + end + @views @. Cssp_out += buffer_1*buffer_2 + + # + 2 d^2 F_s / d vpa d vperp * d^2 G_sp / d vpa d vperp + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @views @. buffer_1[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa, vpa_spectral) + @views @. buffer_2[:,ivperp] = vpa.scratch + end + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, buffer_1[ivpa,:], vperp, vperp_spectral) + @views @. buffer_1[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch, buffer_2[ivpa,:], vperp, vperp_spectral) + @views @. buffer_2[ivpa,:] = vperp.scratch + end + @views @. Cssp_out += 2.0*buffer_1*buffer_2 + + # + d^2 F_s / d vperp^2 * d^2 G_sp / d vperp^2 + for ivpa in 1:nvpa + vperp.scratch2 .= 1.0 # remove Q argument from second_derivative! as never different from 1? + @views second_derivative!(vperp.scratch, fs_in[ivpa,:], vperp.scratch2, vperp, vperp_spectral) + @views @. buffer_1[ivpa,:] = vperp.scratch + @views second_derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp.scratch2, vperp, vperp_spectral) + @views @. buffer_2[ivpa,:] = vperp.scratch + end + @views @. Cssp_out += buffer_1*buffer_2 + + # + ( 1/vperp^2) d F_s / d vperp * d G_sp / d vperp + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) + @views @. buffer_1[ivpa,:] = vperp.scratch/(vperp.grid^2) # MRH this line causes divide by zero! + @views derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp, vperp_spectral) + @views @. buffer_2[ivpa,:] = vperp.scratch + end + @views @. Cssp_out += buffer_1*buffer_2 + + # + 2( 1 - ms/msp) * d F_s / d vpa * d H_sp / d vpa + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @views @. buffer_1[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch, Rosenbluth_H[:,ivperp], vpa, vpa_spectral) + @views @. buffer_2[:,ivperp] = vpa.scratch + end + @views @. Cssp_out += 2.0*(1.0 - ms/msp)*buffer_1*buffer_2 + + # + 2( 1 - ms/msp) * d F_s / d vperp * d H_sp / d vperp + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) + @views @. buffer_1[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch, Rosenbluth_H[ivpa,:], vperp, vperp_spectral) + @views @. buffer_2[ivpa,:] = vperp.scratch + end + @views @. Cssp_out += 2.0*(1.0 - ms/msp)*buffer_1*buffer_2 + + # + (8 ms / \sqrt{\pi} msp ) F_s F_sp + @views @. Cssp_out += ((8.0*ms)/(sqrt(pi)*msp))*fs_in*fsp_in + + # multiply by overall collision frequency + @views @. Cssp_out = cfreqssp*Cssp_out +end + +function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + n_ion_species = composition.n_ion_species + @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) + @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) + @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) + @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + Cssp_result_vpavperp = scratch_dummy.dummy_vpavperp + mi = 1.0 # generalise this to an Array with size n_ion_species + cfreqii = collisions.nuii # generalise this to an Array with size (n_ion_species,n_ion_species) + + begin_r_z_region() + # serial in s vperp vpa for now + for is in 1:n_ion_species + for isp in 1:n_ion_species + @loop_r_z ir iz begin + @views evaluate_RMJ_collision_operator!(Cssp_result_vpavperp,pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,isp], + mi, mi, cfreqii, fokkerplanck_arrays, vperp, vpa, + vperp_spectral, vpa_spectral) + @views @. pdf_out[:,:,iz,ir,is] += dt*Cssp_result_vpavperp[:,:] + end + end + end +end + +end diff --git a/src/input_structs.jl b/src/input_structs.jl index 210c66b46..47287a8ce 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -65,6 +65,7 @@ mutable struct advance_info manufactured_solns_test::Bool r_diffusion::Bool #flag to control how r bc is imposed when r diffusion terms are present vpa_diffusion::Bool #flag to control how vpa bc is imposed when vpa diffusion terms are present + explicit_fp_collisions::Bool end """ @@ -295,6 +296,8 @@ mutable struct collisions_input ionization::mk_float # if constant_ionization_rate = true, use an ionization term that is constant in z constant_ionization_rate::Bool + # ion-ion self collision frequency + nuii::mk_float end """ diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index fbd84df37..910520f56 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -33,6 +33,7 @@ include("bgk.jl") include("manufactured_solns.jl") # MRH Here? include("initial_conditions.jl") #include("semi_lagrange.jl") +include("fokker_planck.jl") include("advection.jl") include("vpa_advection.jl") include("z_advection.jl") @@ -219,7 +220,7 @@ function setup_moment_kinetics(input_dict::Dict; # the main time advance loop -- including normalisation of f by density if requested moments, fields, spectral_objects, advect_objects, - scratch, advance, scratch_dummy, manufactured_source_list = setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, + scratch, advance, fp_arrays, scratch_dummy, manufactured_source_list = setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, drive_input, moments, t_input, collisions, species, geometry, boundary_distributions, num_diss_params) # setup i/o ascii_io, io_moments, io_dfns = setup_file_io(io_input, vz, vr, vzeta, vpa, vperp, z, r, composition, collisions) @@ -238,7 +239,7 @@ function setup_moment_kinetics(input_dict::Dict; return pdf, scratch, code_time, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, - num_diss_params, advance, scratch_dummy, manufactured_source_list, + num_diss_params, advance, fp_arrays, scratch_dummy, manufactured_source_list, ascii_io, io_moments, io_dfns end diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 36756a142..282133c13 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -182,6 +182,7 @@ function mk_input(scan_input=Dict()) collisions.charge_exchange = get(scan_input, "charge_exchange_frequency", 2.0*sqrt(species.charged[1].initial_temperature)) collisions.ionization = get(scan_input, "ionization_frequency", collisions.charge_exchange) collisions.constant_ionization_rate = get(scan_input, "constant_ionization_rate", false) + collisions.nuii = get(scan_input, "nuii", 0.0) # parameters related to the time stepping nstep = get(scan_input, "nstep", 5) @@ -859,7 +860,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # ionization collision frequency ionization = 0.0 constant_ionization_rate = false - collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate) + nuii = 0.0 + collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength diff --git a/src/time_advance.jl b/src/time_advance.jl index 6cfe5b805..3d2bb961b 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -41,6 +41,7 @@ using ..continuity: continuity_equation! using ..force_balance: force_balance! using ..energy_equation: energy_equation! using ..em_fields: setup_em_fields, update_phi! +using ..fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! #using ..semi_lagrange: setup_semi_lagrange using Dates @@ -203,14 +204,19 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance_continuity = false advance_force_balance = false advance_energy = false + if collisions.nuii > 0.0 + explicit_fp_collisions = true + else + explicit_fp_collisions = false + 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) # 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) + vpa_diffusion = (advance_numerical_dissipation && num_diss_params.vpa_dissipation_coefficient > 0.0 || explicit_fp_collisions) advance = advance_info(advance_vpa_advection, advance_z_advection, advance_r_advection, advance_neutral_z_advection, advance_neutral_r_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, advance_ionization_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, advance_energy, rk_coefs, - manufactured_solns_test, r_diffusion, vpa_diffusion) + manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions) if z.discretization == "chebyshev_pseudospectral" @@ -249,7 +255,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, vpa.duniform_dgrid .= 1.0 end - # MRH CONSIDER REMOVING -> vperp_spectral never used if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp # and create the plans for the forward and backward fast Chebyshev transforms @@ -305,6 +310,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, 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 + fp_arrays = init_fokker_planck_collisions(vperp,vpa) # create the "fields" structure that contains arrays # for the electrostatic potential phi and eventually the electromagnetic fields fields = setup_em_fields(z.n, r.n, drive_input.force_phi, drive_input.amplitude, drive_input.frequency, drive_input.force_Er_zero_at_wall) @@ -448,7 +455,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, _block_synchronize() return moments, fields, spectral_objects, advect_objects, - scratch, advance, scratch_dummy, manufactured_source_list + scratch, advance, fp_arrays, scratch_dummy, manufactured_source_list end function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies_ion,nspecies_neutral) @@ -625,7 +632,7 @@ time integrator can be used without severe CFL condition function time_advance!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, - num_diss_params, advance, scratch_dummy, + num_diss_params, advance, fp_arrays, scratch_dummy, manufactured_source_list, ascii_io, io_moments, io_dfns) @debug_detect_redundant_block_synchronize begin @@ -648,7 +655,7 @@ function time_advance!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyro time_advance_no_splitting!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, - num_diss_params, advance, scratch_dummy, manufactured_source_list, i) + num_diss_params, advance, fp_arrays, scratch_dummy, manufactured_source_list, i) # update the time t += t_input.dt # write moments data to file every nwrite_moments time steps @@ -713,7 +720,7 @@ end function time_advance_no_splitting!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, - num_diss_params, advance, scratch_dummy, manufactured_source_list, istep) + num_diss_params, advance, fp_arrays, scratch_dummy, manufactured_source_list, istep) #pdf_in = pdf.unnorm #get input pdf values in case we wish to impose a constant-in-time boundary condition in r #use pdf.norm for this fn for now. @@ -722,7 +729,7 @@ function time_advance_no_splitting!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, num_diss_params, - advance, scratch_dummy, manufactured_source_list, istep)#pdf_in, + advance, fp_arrays, scratch_dummy, manufactured_source_list, istep)#pdf_in, else euler_time_advance!(scratch, scratch, pdf, fields, moments, advect_objects, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, t, @@ -810,7 +817,7 @@ end function ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, moments, fields, spectral_objects, advect_objects, composition, collisions, geometry, boundary_distributions, num_diss_params, - advance, scratch_dummy, manufactured_source_list, istep)#pdf_in, + advance, fp_arrays, scratch_dummy, manufactured_source_list, istep)#pdf_in, begin_s_r_z_region() @@ -850,7 +857,7 @@ function ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, t_input, spectral_objects, composition, collisions, geometry, boundary_distributions, scratch_dummy, manufactured_source_list, - num_diss_params, advance, istage) #pdf_in, + num_diss_params, advance, fp_arrays, istage) #pdf_in, @views rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, r, advance.rk_coefs[:,istage], istage, composition, spectral_objects.z_spectral, spectral_objects.r_spectral, scratch_dummy) end @@ -934,7 +941,7 @@ with fvec_in an input and fvec_out the output function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, advect_objects, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, t, t_input, spectral_objects, composition, collisions, geometry, boundary_distributions, - scratch_dummy, manufactured_source_list, num_diss_params, advance, istage) #pdf_in, + scratch_dummy, manufactured_source_list, num_diss_params, advance, fp_arrays, istage) #pdf_in, # define some abbreviated variables for tidiness n_ion_species = composition.n_ion_species n_neutral_species = composition.n_neutral_species @@ -944,7 +951,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, # only charged 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, r_spectral, z_spectral = spectral_objects.vpa_spectral, spectral_objects.r_spectral, spectral_objects.z_spectral + vpa_spectral, vperp_spectral, r_spectral, z_spectral = spectral_objects.vpa_spectral, spectral_objects.vperp_spectral, spectral_objects.r_spectral, spectral_objects.z_spectral vpa_advect, r_advect, z_advect = advect_objects.vpa_advect, advect_objects.r_advect, advect_objects.z_advect neutral_z_advect, neutral_r_advect = advect_objects.neutral_z_advect, advect_objects.neutral_r_advect @@ -1020,6 +1027,11 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, r_dissipation!(fvec_out.pdf, fvec_in.pdf, r, r_spectral, dt, num_diss_params, scratch_dummy) end + + if advance.explicit_fp_collisions + explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf,composition,collisions,dt,fp_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + end # enforce boundary conditions in r, z and vpa on the charged particle distribution function enforce_boundary_conditions!(fvec_out.pdf, boundary_distributions.pdf_rboundary_charged, From 409c48b9b6d07784655fad011c792bfe255848f2 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Apr 2023 08:14:04 +0100 Subject: [PATCH 002/331] Cherry-picked Gauss-Chebyshev-Radau grid from (vpa,mu) branch --- src/chebyshev.jl | 276 ++++++++++++++++++++++++++++++++++++++++++--- src/coordinates.jl | 10 +- 2 files changed, 267 insertions(+), 19 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index e1af135c7..8284ae939 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -7,18 +7,20 @@ export update_df_chebyshev! export chebyshev_derivative! export setup_chebyshev_pseudospectral export scaled_chebyshev_grid +export scaled_chebyshev_radau_grid export chebyshev_spectral_derivative! export chebyshev_info +export chebyshev_base_info using FFTW -using ..type_definitions: mk_float +using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_complex using ..clenshaw_curtis: clenshawcurtisweights import ..interpolation: interpolate_to_grid_1d """ """ -struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} +struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} # fext is an array for storing f(z) on the extended domain needed # to perform complex-to-complex FFT using the fact that f(theta) is even in theta fext::Array{Complex{mk_float},1} @@ -28,18 +30,29 @@ struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.Scal f::Array{mk_float,2} # Chebyshev spectral coefficients of derivative of f df::Array{mk_float,1} - # plan for the complex-to-complex, in-place, forward Fourier transform on Chebyshev-Gauss-Lobatto grid + # plan for the complex-to-complex, in-place, forward Fourier transform on Chebyshev-Gauss-Lobatto/Radau grid forward::TForward - # plan for the complex-to-complex, in-place, backward Fourier transform on Chebyshev-Gauss-Lobatto grid + # plan for the complex-to-complex, in-place, backward Fourier transform on Chebyshev-Gauss-Lobatto/Radau grid #backward_transform::FFTW.cFFTWPlan backward::TBackward end +struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} + lobotto::chebyshev_base_info{TForward, TBackward} + radau::chebyshev_base_info{TForward, TBackward} +end + """ create arrays needed for explicit Chebyshev pseudospectral treatment and create the plans for the forward and backward fast Fourier transforms """ function setup_chebyshev_pseudospectral(coord) + lobotto = setup_chebyshev_pseudospectral_lobotto(coord) + radau = setup_chebyshev_pseudospectral_radau(coord) + return chebyshev_info(lobotto,radau) +end + +function setup_chebyshev_pseudospectral_lobotto(coord) # ngrid_fft is the number of grid points in the extended domain # in z = cos(theta). this is necessary to turn a cosine transform on [0,π] # into a complex transform on [0,2π], which is more efficient in FFTW @@ -54,7 +67,25 @@ function setup_chebyshev_pseudospectral(coord) backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) # return a structure containing the information needed to carry out # a 1D Chebyshev transform - return chebyshev_info(fext, fcheby, dcheby, forward_transform, backward_transform) + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform) +end + +function setup_chebyshev_pseudospectral_radau(coord) + # ngrid_fft is the number of grid points in the extended domain + # in z = cos(theta). this is necessary to turn a cosine transform on [0,π] + # into a complex transform on [0,2π], which is more efficient in FFTW + ngrid_fft = 2*coord.ngrid - 1 + # create array for f on extended [0,2π] domain in theta = ArcCos[z] + fext = allocate_complex(ngrid_fft) + # create arrays for storing Chebyshev spectral coefficients of f and f' + fcheby = allocate_float(coord.ngrid, coord.nelement_local) + dcheby = allocate_float(coord.ngrid) + # setup the plans for the forward and backward Fourier transforms + forward_transform = plan_fft!(fext, flags=FFTW.MEASURE) + backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) + # return a structure containing the information needed to carry out + # a 1D Chebyshev transform + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform) end """ @@ -94,13 +125,68 @@ function scaled_chebyshev_grid(ngrid, nelement_global, nelement_local, n, return grid, wgts end +function scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n, + irank, box_length, imin, imax) + # initialize chebyshev grid defined on [1,-1] + # with n grid points chosen to facilitate + # the fast Chebyshev transform (aka the discrete cosine transform) + # needed to obtain Chebyshev spectral coefficients + # this grid goes from +1 to -1 + chebyshev_grid = chebyshevpoints(ngrid) + chebyshev_radau_grid = chebyshev_radau_points(ngrid) + # create array for the full grid + grid = allocate_float(n) + # setup the scale factor by which the Chebyshev grid on [-1,1] + # is to be multiplied to account for the full domain [-L/2,L/2] + # and the splitting into nelement elements with ngrid grid points + scale_factor = 0.5*box_length/float(nelement_global) + if irank == 0 # use a Chebyshev-Gauss-Radau element for the lowest element on rank 0 + shift = box_length*(0.5/float(nelement_global) - 0.5) + grid[imin[1]:imax[1]] .= (chebyshev_radau_grid[1:ngrid] * scale_factor) .+ shift + # account for the fact that the minimum index needed for the chebyshev_grid + # within each element changes from 1 to 2 in going from the first element + # to the remaining elements + k = 2 + @inbounds for j ∈ 2:nelement_local + #wgts[imin[j]:imax[j]] .= sqrt.(1.0 .- reverse(chebyshev_grid)[k:ngrid].^2) * scale_factor + # amount by which to shift the centre of this element from zero + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + # reverse the order of the original chebyshev_grid (ran from [1,-1]) + # and apply the scale factor and shift + grid[imin[j]:imax[j]] .= (reverse(chebyshev_grid)[k:ngrid] * scale_factor) .+ shift + end + wgts = clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, scale_factor) + else + # account for the fact that the minimum index needed for the chebyshev_grid + # within each element changes from 1 to 2 in going from the first element + # to the remaining elements + k = 1 + @inbounds for j ∈ 1:nelement_local + #wgts[imin[j]:imax[j]] .= sqrt.(1.0 .- reverse(chebyshev_grid)[k:ngrid].^2) * scale_factor + # amount by which to shift the centre of this element from zero + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + # reverse the order of the original chebyshev_grid (ran from [1,-1]) + # and apply the scale factor and shift + grid[imin[j]:imax[j]] .= (reverse(chebyshev_grid)[k:ngrid] * scale_factor) .+ shift + # after first element, increase minimum index for chebyshev_grid to 2 + # to avoid double-counting boundary element + k = 2 + end + wgts = clenshaw_curtis_weights(ngrid, nelement_local, n, imin, imax, scale_factor) + end + return grid, wgts +end + """ """ function chebyshev_derivative!(df, ff, chebyshev, coord) # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds - @boundscheck nelement == size(chebyshev.f,2) || throw(BoundsError(chebyshev.f)) + @boundscheck nelement == size(chebyshev.lobotto.f,2) || throw(BoundsError(chebyshev.lobotto.f)) + @boundscheck nelement == size(chebyshev.radau.f,2) || throw(BoundsError(chebyshev.radau.f)) @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) # note that one must multiply by 2*nelement/L to get derivative # in scaled coordinate @@ -110,25 +196,49 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) # variable k will be used to avoid double counting of overlapping point # at element boundaries (see below for further explanation) k = 0 + j = 1 # the first element + if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views chebyshev_radau_derivative_single_element!(df[:,j], ff[imin:imax], + chebyshev.radau.f[:,j], chebyshev.radau.df, chebyshev.radau.fext, chebyshev.radau.forward, coord) + # and multiply by scaling factor needed to go + # from Chebyshev z coordinate to actual z + for i ∈ 1:coord.ngrid + df[i,j] *= scale_factor + end + else #differentiate using the Lobotto scheme + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], + chebyshev.lobotto.f[:,j], chebyshev.lobotto.df, chebyshev.lobotto.fext, chebyshev.lobotto.forward, coord) + # and multiply by scaling factor needed to go + # from Chebyshev z coordinate to actual z + for i ∈ 1:coord.ngrid + df[i,j] *= scale_factor + end + end # calculate the Chebyshev derivative on each element - @inbounds for j ∈ 1:nelement + @inbounds for j ∈ 2:nelement # imin is the minimum index on the full grid for this (jth) element # the 'k' below accounts for the fact that the first element includes # both boundary points, while each additional element shares a boundary # point with neighboring elements. the choice was made when defining # coord.imin to exclude the lower boundary point in each element other # than the first so that no point is double-counted + k = 1 imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.f[:,j], chebyshev.df, chebyshev.fext, chebyshev.forward, coord) + chebyshev.lobotto.f[:,j], chebyshev.lobotto.df, chebyshev.lobotto.fext, chebyshev.lobotto.forward, coord) # and multiply by scaling factor needed to go # from Chebyshev z coordinate to actual z for i ∈ 1:coord.ngrid df[i,j] *= scale_factor - end - k = 1 + end end return nothing end @@ -145,6 +255,7 @@ function chebyshev_derivative_single_element!(df, ff, cheby_f, cheby_df, cheby_f chebyshev_backward_transform!(df, cheby_fext, cheby_df, forward, coord.ngrid) end + """ Chebyshev transform f to get Chebyshev spectral coefficients """ @@ -235,9 +346,7 @@ result : Array function interpolate_to_grid_1d(newgrid, f, coord, chebyshev::chebyshev_info) # define local variable nelement for convenience nelement = coord.nelement_local - # check array bounds - @boundscheck nelement == size(chebyshev.f,2) || throw(BoundsError(chebyshev.f)) - + # Array for output result = similar(newgrid) @@ -303,7 +412,7 @@ function chebyshev_interpolate_single_element(newgrid, f, j, coord, chebyshev) scale = 2.0 / (coord.grid[imax] - coord.grid[imin]) # Get Chebyshev coefficients - chebyshev_forward_transform!(cheby_f, chebyshev.fext, f, chebyshev.forward, coord.ngrid) + chebyshev_forward_transform!(cheby_f, chebyshev.lobotto.fext, f, chebyshev.lobotto.forward, coord.ngrid) for (i, x) ∈ enumerate(newgrid) z = scale * (x - shift) @@ -346,6 +455,30 @@ function clenshaw_curtis_weights(ngrid, nelement_local, n, imin, imax, scale_fac return wgts end +function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, scale_factor) + # create array containing the integration weights + wgts = zeros(mk_float, n) + # calculate the modified Chebshev moments of the first kind + μ = chebyshevmoments(ngrid) + wgts_lobotto = clenshawcurtisweights(μ)*scale_factor + wgts_radau = chebyshev_radau_weights(μ, ngrid)*scale_factor + @inbounds begin + # calculate the weights within a single element and + # scale to account for modified domain (not [-1,1]) + wgts[1:ngrid] .= wgts_radau[1:ngrid] + if nelement_local > 1 + # account for double-counting of points at inner element boundaries + wgts[ngrid] += wgts_lobotto[1] + for j ∈ 2:nelement_local + wgts[imin[j]:imax[j]] .= wgts_lobotto[2:ngrid] + end + # remove double-counting of outer element boundary for last element + wgts[n] *= 0.5 + end + end + return wgts +end + """ compute and return modified Chebyshev moments of the first kind: ∫dx Tᵢ(x) over range [-1,1] @@ -373,6 +506,50 @@ function chebyshevpoints(n) return grid end +function chebyshev_radau_points(n) + grid = allocate_float(n) + nfac = 1.0/(n-0.5) + @inbounds begin + # calculate z = cos(θ) ∈ (-1,1] + for j ∈ 1:n + grid[j] = cospi((n-j)*nfac) + end + end + return grid +end + +function chebyshev_radau_weights(moments::Array{mk_float,1}, n) + # input should have values moments[j] = (cos(pi j) + 1)/(1-j^2) for j >= 0 + nfft = 2*n - 1 + # create array for moments on extended [0,2π] domain in theta = ArcCos[z] + fext = allocate_complex(nfft) + # make fft plan + forward_transform = plan_fft!(fext, flags=FFTW.MEASURE) + # assign values of fext from moments + @inbounds begin + for j ∈ 1:n + fext[j] = complex(moments[j],0.0) + end + for j ∈ 1:n-1 + fext[n+j] = fext[n-j+1] + end + end + # perform the forward, complex-to-complex FFT in-place (fext is overwritten) + forward_transform*fext + # use reality + evenness of moments to eliminate unncessary information + # also sort out normalisation and order of array + # note that fft order output is reversed compared to the order of + # the grid chosen, which runs from (-1,1] + wgts = allocate_float(n) + @inbounds begin + for j ∈ 2:n + wgts[n-j+1] = 2.0*real(fext[j])/nfft + end + wgts[n] = real(fext[1])/nfft + end + return wgts +end + """ takes the real function ff on a Chebyshev grid in z (domain [-1, 1]), which corresponds to the domain [π, 2π] in variable theta = ArcCos(z). @@ -461,4 +638,75 @@ function chebyshev_backward_transform!(ff, fext, chebyf, transform, n) return nothing end +function chebyshev_radau_forward_transform!(chebyf, fext, ff, transform, n) + @inbounds begin + for j ∈ 1:n + fext[j] = complex(ff[n-j+1],0.0) + end + for j ∈ 1:n-1 + fext[n+j] = fext[n-j+1] + end + end + #println("ff",ff) + #println("fext",fext) + # perform the forward, complex-to-complex FFT in-place (cheby.fext is overwritten) + transform*fext + #println("fext",fext) + # use reality + evenness of f to eliminate unncessary information + # and obtain Chebyshev spectral coefficients for this element + # also sort out normalisation + @inbounds begin + nfft = 2*n - 1 + for j ∈ 2:n + chebyf[j] = 2.0*real(fext[j])/nfft + end + chebyf[1] = real(fext[1])/nfft + end + return nothing + end + + """ + """ + function chebyshev_radau_backward_transform!(ff, fext, chebyf, transform, n) + # chebyf as input contains Chebyshev spectral coefficients + # need to use reality condition to extend onto negative frequency domain + @inbounds begin + # first, fill in values for fext corresponding to positive frequencies + for j ∈ 2:n + fext[j] = chebyf[j]*0.5 + end + # next, fill in values for fext corresponding to negative frequencies + # using fext(-k) = conjg(fext(k)) = fext(k) + # usual FFT ordering with j=1 <-> k=0, followed by ascending k up to kmax + # and then descending from -kmax down to -dk + for j ∈ 1:n-1 + fext[n+j] = fext[n-j+1] + end + # fill in zero frequency mode, which is special in that it does not require + # the 1/2 scale factor + fext[1] = chebyf[1] + end + #println("chebyf",chebyf) + #println("fext",fext) + # perform the backward, complex-to-complex FFT in-place (fext is overwritten) + transform*fext + #println("fext",fext) + + @inbounds begin + for j ∈ 1:n + ff[j] = real(fext[n-j+1]) + end + end + return nothing + end + function chebyshev_radau_derivative_single_element!(df, ff, cheby_f, cheby_df, cheby_fext, forward, coord) + # calculate the Chebyshev coefficients of the real-space function ff and return + # as cheby_f + chebyshev_radau_forward_transform!(cheby_f, cheby_fext, ff, forward, coord.ngrid) + # calculate the Chebyshev coefficients of the derivative of ff with respect to coord.grid + chebyshev_spectral_derivative!(cheby_df, cheby_f) + # inverse Chebyshev transform to get df/dcoord + chebyshev_radau_backward_transform!(df, cheby_fext, cheby_df, forward, coord.ngrid) + end + end diff --git a/src/coordinates.jl b/src/coordinates.jl index 6fea736b5..93ac7359e 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -7,7 +7,7 @@ export equally_spaced_grid using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_int -using ..chebyshev: scaled_chebyshev_grid +using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid using ..quadrature: composite_simpson_weights using ..input_structs: advection_input @@ -170,11 +170,11 @@ function init_grid(ngrid, nelement_global, nelement_local, n_global, n_local, ir end elseif discretization == "chebyshev_pseudospectral" if name == "vperp" - # initialize chebyshev grid defined on [-L/2,L/2] - grid, wgts = scaled_chebyshev_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) - grid .= grid .+ L/2.0 # shift to [0,L] appropriate to vperp variable + # initialize chebyshev-radau grid defined on [-L/2,L/2] + grid, wgts = scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) + grid .= grid .+ L/2.0 # shift to [0,L] appropriate to vperp variable wgts = 2.0 .* wgts .* grid # to include 2 vperp in jacobian of integral - # see note above on normalisation + # see note above on normalisation else # initialize chebyshev grid defined on [-L/2,L/2] # with n grid points chosen to facilitate From 70bc97dbee026f19a60d3f2a8e34b66d94c5fdfd Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Apr 2023 09:06:24 +0100 Subject: [PATCH 003/331] Added vperp zero boundary condition imposed at vperp -> infinity. A regularity condition may be required at vperp ~ 0. --- src/initial_conditions.jl | 16 ++++++++++++++++ src/time_advance.jl | 13 ++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 846eef0cf..060e3120d 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -6,6 +6,7 @@ export init_pdf_and_moments export enforce_r_boundary_condition! export enforce_z_boundary_condition! export enforce_vpa_boundary_condition! +export enforce_vperp_boundary_condition! export enforce_boundary_conditions! export enforce_neutral_boundary_conditions! export enforce_neutral_r_boundary_condition! @@ -581,6 +582,10 @@ function enforce_boundary_conditions!(f, f_r_bc, # use that adv.speed independent of vpa @views enforce_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_diffusion) end + if vperp.n > 1 + begin_s_r_z_vpa_region() + @views enforce_vperp_boundary_condition!(f,vperp) + end begin_s_r_vperp_vpa_region() @views enforce_z_boundary_condition!(f, z_bc, z_adv, vpa, vperp, z, r, composition, scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, @@ -748,7 +753,18 @@ function enforce_vpa_boundary_condition_local!(f::T, bc, adv_speed, vpa_diffusio f[nvpa] = f[1] end end +""" +enforce zero boundary condition at vperp -> infinity +""" +function enforce_vperp_boundary_condition!(f,vperp) + nvperp = vperp.n + @loop_s_r_z_vpa is ir iz ivpa begin + f[ivpa,nvperp,iz,ir,is] = 0.0 + end +end +""" +""" function enforce_neutral_boundary_conditions!(f_neutral, f_charged, boundary_distributions, r_adv_neutral::T1, z_adv_neutral::T2, z_adv_charged::T3, vz, vr, vzeta, vpa, vperp, z, r, composition, scratch_dummy::T4) where {T1, T2, T3, T4} #f_initial, # f_initial contains the initial condition for enforcing a fixed-boundary-value condition diff --git a/src/time_advance.jl b/src/time_advance.jl index 3d2bb961b..f1af4068f 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -24,7 +24,7 @@ using ..velocity_moments: update_neutral_pzeta!, update_neutral_pz!, update_neut using ..velocity_grid_transforms: vzvrvzeta_to_vpavperp!, vpavperp_to_vzvrvzeta! using ..initial_conditions: enforce_z_boundary_condition!, enforce_boundary_conditions! using ..initial_conditions: enforce_vpa_boundary_condition!, enforce_r_boundary_condition! -using ..initial_conditions: enforce_neutral_boundary_conditions! +using ..initial_conditions: enforce_neutral_boundary_conditions!, enforce_vperp_boundary_condition! using ..initial_conditions: enforce_neutral_z_boundary_condition!, enforce_neutral_r_boundary_condition! using ..input_structs: advance_info, time_input using ..advection: setup_advection #, update_boundary_indices! @@ -391,12 +391,15 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, @serial_region begin for is ∈ 1:n_ion_species @views update_speed_vperp!(vperp_advect[is], vpa, vperp, z, r) - # enforce prescribed boundary condition in vpa on the distribution function f - #PLACEHOLDER - #@views enforce_vperp_boundary_condition!(pdf.norm[:,:,:,:,is], vpa.bc, vpa_advect[is]) + end end - + # enforce prescribed boundary condition in vperp on the distribution function f + if vperp.n > 1 + begin_s_r_z_vpa_region() + @views enforce_vperp_boundary_condition!(f,vperp) + end + ## # Neutral particle advection ## From 5f6b040331213bbebf1a89e9f6bada75cde5d5ea Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Apr 2023 13:28:41 +0100 Subject: [PATCH 004/331] Initial steps towards testing the calculation of G[F_M], H[F_M], and C[F_M,F_M] via direct integration. Numerical difficulties are found in the calculation of H, due to locations in the integrand where vpap = vpa, vperpp = vperp, since here mm = 1 and K(mm) = Inf. --- fkpl_test.jl | 430 +++++++++++++++++++++++++++++++++++++++++++ src/fokker_planck.jl | 18 +- 2 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 fkpl_test.jl diff --git a/fkpl_test.jl b/fkpl_test.jl new file mode 100644 index 000000000..6dd58c5c1 --- /dev/null +++ b/fkpl_test.jl @@ -0,0 +1,430 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using SpecialFunctions: erf + +function eta_speed(vpa,vperp,ivpa,ivperp) + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + return eta +end + +function G_Maxwellian(vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G +end +function H_Maxwellian(vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 + H = 2.0/sqrt(pi) + else + # H_M = erf(eta)/eta + H = erf(eta)/eta + end + return H +end + + #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) + # #Gamma = 0.0 + # #return Gamma + #end + #function Gamma_vpa_GMaxwellian(Bmag,vpa,mu,ivpa,imu) + # ## speed variable + # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + # # + # #d2Gdeta2 = (erf(eta)/(eta^3)) - (2.0/sqrt(pi))*(exp(-eta^2)/(eta^2)) + # #zero = 1.0e-10 + # #if eta > zero + # # #Gamma = -2.0*vpa.grid[ivpa]*exp(-eta^2)*d2Gdeta2 + # #else + # # #Gamma = 0.0 + # #end + # #return Gamma + #end + #function Gamma_vpa_HMaxwellian(Bmag,vpa,mu,ivpa,imu) + # ## speed variable + # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + # # + # #dHdeta = (2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2)) + # #zero = 1.0e-10 + # #if eta > zero + # # #Gamma = -2.0*vpa.grid[ivpa]*exp(-eta^2)*(1.0/eta)*dHdeta + # #else + # # #Gamma = 0.0 + # #end + # #return Gamma + #end + + #function Gamma_mu_Maxwellian(Bmag,vpa,mu,ivpa,imu) + # #Gamma = 0.0 + # #return Gamma + #end + #function Gamma_mu_GMaxwellian(Bmag,vpa,mu,ivpa,imu) + # ## speed variable + # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + # # + # #d2Gdeta2 = (erf(eta)/(eta^3)) - (2.0/sqrt(pi))*(exp(-eta^2)/(eta^2)) + # #zero = 1.0e-10 + # #if eta > zero + # # #Gamma = -4.0*mu.grid[imu]*exp(-eta^2)*d2Gdeta2 + # #else + # # #Gamma = 0.0 + # #end + # #return Gamma + #end + #function Gamma_mu_HMaxwellian(Bmag,vpa,mu,ivpa,imu) + # ## speed variable + # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + # # + # #dHdeta = (2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2)) + # #zero = 1.0e-10 + # #if eta > zero + # # #Gamma = -4.0*mu.grid[imu]*exp(-eta^2)*(1.0/eta)*dHdeta + # #else + # # #Gamma = 0.0 + # #end + # #return Gamma + #end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + import moment_kinetics + using moment_kinetics.input_structs: grid_input, advection_input + using moment_kinetics.coordinates: define_coordinate + using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral + using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! + using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! + #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! + using moment_kinetics.fokker_planck: init_fokker_planck_collisions + using moment_kinetics.type_definitions: mk_float, mk_int + + discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = 3 #number of points per element + vpa_nelement_local = 3 # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 6.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = 3 #number of points per element + vperp_nelement_local = 3 # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, bc, adv_input,comm) + + # create the coordinate structs + println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + println(vperp.grid) + println(vperp.wgts) + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + cfreqssp = 1.0 + ms = 1.0 + msp = 1.0 + + fkarrays = init_fokker_planck_collisions(vperp, vpa) + + Cssp = Array{mk_float,2}(undef,nvpa,nvperp) + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #H_check = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = Array{mk_float,2}(undef,nvpa,nvperp) + #H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_Herr = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) + + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = exp( - vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2 ) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(vpa,vperp,ivpa,ivperp) + end + end + + fsp_in = fs_in + + # evaluate the Rosenbluth potentials + @views calculate_Rosenbluth_potentials!(fkarrays.Rosenbluth_G, fkarrays.Rosenbluth_H, + fsp_in, fkarrays.elliptic_integral_E_factor,fkarrays.elliptic_integral_K_factor, + fkarrays.buffer_vpavperp_1,vperp,vpa) + + #@views calculate_Rosenbluth_H_from_G!(H_check,G_Maxwell, + # vpa,vpa_spectral,vperp,vperp_spectral,Bmag, + # fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) + @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) + @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) + #@. H_check_err = abs(H_check - H_Maxwell) + println("max(G_err)",maximum(G_err)) + println("max(H_err)",maximum(H_err)) + #println("max(H_check_err)",maximum(H_check_err)) + zero = 1.0e-3 + #println(G_Maxwell[41,:]) + #println(G_Maxwell[:,1]) + for ivperp in 1:nvperp + for ivpa in 1:nvpa + if (maximum(G_err[ivpa,ivperp]) > zero) + #println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) + #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) + #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) + end + #H_err[ivpa,ivperp] + end + end + #println(H_Maxwell[:,1]) + #println(fkarrays.Rosenbluth_H[:,1]) + #zero = 0.1 + for ivperp in 1:nvperp + for ivpa in 1:nvpa + if (maximum(H_err[ivpa,ivperp]) > zero) + #println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) + println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) + end + #H_err[ivpa,ivperp] + end + end + + ## evaluate the collision operator with numerically computed G & H + #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") + #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, + # mu, vpa, mu_spectral, vpa_spectral, Bmag, fkarrays) + # + #zero = 1.0e1 + #Cssp_err = maximum(abs.(Cssp)) + #if Cssp_err > zero + # #println("ERROR: C_ss'[F_Ms,F_Ms] /= 0") + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if maximum(abs.(Cssp[ivpa,imu])) > zero + # # # # ###print("ivpa: ",ivpa," imu: ",imu," C: ") + # # # # ##println("ivpa: ",ivpa," imu: ",imu," C: ", Cssp[ivpa,imu]) + # # # # ##println(" imu: ",imu," C[:,imu]:") + # # # # ###@printf("%.1e", Cssp[ivpa,imu]) + # # # # ###println("") + # # # #end + # # #end + # #end + #end + #println("max(abs(C_ss'[F_Ms,F_Ms])): ", Cssp_err) + # + # + ## evaluate the collision operator with analytically computed G & H + #zero = 1.0e-6 + #println("TEST: Css'[F_M,F_M] with analytical G[F_M] & H[F_M]") + #@views @. fkarrays.Rosenbluth_G = G_Maxwell + #@views @. fkarrays.Rosenbluth_H = H_Maxwell + #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, + # mu, vpa, mu_spectral, vpa_spectral, Bmag, fkarrays) + # + #for imu in 1:mu.n + # #for ivpa in 1:vpa.n + # # #Gam_vpa_Maxwell[ivpa,imu] = Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) + # # #Gam_mu_Maxwell[ivpa,imu] = Gamma_mu_Maxwellian(Bmag,vpa,mu,ivpa,imu) + # # # + # # #Gam_vpa_err[ivpa,imu] = abs(fkarrays.Gamma_vpa[ivpa,imu] - Gam_vpa_Maxwell[ivpa,imu]) + # # #Gam_mu_err[ivpa,imu] = abs(fkarrays.Gamma_mu[ivpa,imu] - Gam_mu_Maxwell[ivpa,imu]) + # #end + #end + #max_Gam_vpa_err = maximum(Gam_vpa_err) + #println("max(abs(Gamma_vpa[F_Ms,F_Ms])): ", max_Gam_vpa_err) + #if max_Gam_vpa_err > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_vpa_err[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_err: ",Gam_vpa_err[ivpa,imu]," Gam_vpa_num: ",fkarrays.Gamma_vpa[ivpa,imu]," Gam_vpa_Maxwell: ",Gam_vpa_Maxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_Maxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_num.pdf") + # # # #savefig(outfile) + #end + #max_Gam_mu_err = maximum(Gam_mu_err) + #println("max(abs(Gamma_mu[F_Ms,F_Ms])): ", max_Gam_mu_err) + #if max_Gam_mu_err > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_mu_err[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_err: ",Gam_mu_err[ivpa,imu]," Gam_mu_num: ",fkarrays.Gamma_mu[ivpa,imu]," Gam_mu_Maxwell: ",Gam_mu_Maxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_mu_Maxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_Maxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_num.pdf") + # # # #savefig(outfile) + #end + # + #for imu in 1:mu.n + # #for ivpa in 1:vpa.n + # # #Gam_vpa_GMaxwell[ivpa,imu] = Gamma_vpa_GMaxwellian(Bmag,vpa,mu,ivpa,imu) + # # #Gam_mu_GMaxwell[ivpa,imu] = Gamma_mu_GMaxwellian(Bmag,vpa,mu,ivpa,imu) + # # # + # # #Gam_vpa_Gerr[ivpa,imu] = abs(fkarrays.Gamma_vpa_G[ivpa,imu] - Gam_vpa_GMaxwell[ivpa,imu]) + # # #Gam_mu_Gerr[ivpa,imu] = abs(fkarrays.Gamma_mu_G[ivpa,imu] - Gam_mu_GMaxwell[ivpa,imu]) + # #end + #end + #max_Gam_vpa_Gerr = maximum(Gam_vpa_Gerr) + #println("max(abs(Gamma_vpa_G[F_Ms,F_Ms])): ", max_Gam_vpa_Gerr) + #if max_Gam_vpa_Gerr > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_vpa_Gerr[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_Gerr: ",Gam_vpa_Gerr[ivpa,imu]," Gam_vpa_G_num: ",fkarrays.Gamma_vpa_G[ivpa,imu]," Gam_vpa_GMaxwell: ",Gam_vpa_GMaxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_GMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_GMaxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa_G[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_G_num.pdf") + # # # #savefig(outfile) + #end + #max_Gam_mu_Gerr = maximum(Gam_mu_Gerr) + #println("max(abs(Gamma_mu_G[F_Ms,F_Ms])): ", max_Gam_mu_Gerr) + #if max_Gam_mu_Gerr > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_mu_Gerr[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_Gerr: ",Gam_mu_Gerr[ivpa,imu]," Gam_mu_G_num: ",fkarrays.Gamma_mu_G[ivpa,imu]," Gam_mu_GMaxwell: ",Gam_mu_GMaxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_mu_GMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_GMaxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu_G[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_G_num.pdf") + # # # #savefig(outfile) + #end + # + #for imu in 1:mu.n + # #for ivpa in 1:vpa.n + # # #Gam_vpa_HMaxwell[ivpa,imu] = Gamma_vpa_HMaxwellian(Bmag,vpa,mu,ivpa,imu) + # # #Gam_mu_HMaxwell[ivpa,imu] = Gamma_mu_HMaxwellian(Bmag,vpa,mu,ivpa,imu) + # # # + # # #Gam_vpa_Herr[ivpa,imu] = abs(fkarrays.Gamma_vpa_H[ivpa,imu] - Gam_vpa_HMaxwell[ivpa,imu]) + # # #Gam_mu_Herr[ivpa,imu] = abs(fkarrays.Gamma_mu_H[ivpa,imu] - Gam_mu_HMaxwell[ivpa,imu]) + # #end + #end + #max_Gam_vpa_Herr = maximum(Gam_vpa_Herr) + #println("max(abs(Gamma_vpa_H[F_Ms,F_Ms])): ", max_Gam_vpa_Herr) + #if max_Gam_vpa_Herr > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_vpa_Herr[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_Herr: ",Gam_vpa_Herr[ivpa,imu]," Gam_vpa_H_num: ",fkarrays.Gamma_vpa_H[ivpa,imu]," Gam_vpa_HMaxwell: ",Gam_vpa_HMaxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_HMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_HMaxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa_H[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_vpa_H_num.pdf") + # # # #savefig(outfile) + #end + #max_Gam_mu_Herr = maximum(Gam_mu_Herr) + #println("max(abs(Gamma_mu_H[F_Ms,F_Ms])): ", max_Gam_mu_Herr) + #if max_Gam_mu_Herr > zero + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if Gam_mu_Herr[ivpa,imu] > zero + # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_Herr: ",Gam_mu_Herr[ivpa,imu]," Gam_mu_H_num: ",fkarrays.Gamma_mu_H[ivpa,imu]," Gam_mu_HMaxwell: ",Gam_mu_HMaxwell[ivpa,imu]) + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Gam_mu_HMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_HMaxwell.pdf") + # # # #savefig(outfile) + # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu_H[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Gam_mu_H_num.pdf") + # # # #savefig(outfile) + #end + # + #Cssp_err = maximum(abs.(Cssp)) + #if Cssp_err > zero + # #println("ERROR: C_ss'[F_Ms,F_Ms] /= 0") + # #for imu in 1:mu.n + # # #for ivpa in 1:vpa.n + # # # #if maximum(abs.(Cssp[ivpa,imu])) > zero + # # # # #print("ivpa: ",ivpa," imu: ",imu," C: ") + # # # # ##println("ivpa: ",ivpa," imu: ",imu," C: ", Cssp[ivpa,imu]) + # # # # ##println(" imu: ",imu," C[:,imu]:") + # # # # #@printf("%.1e", Cssp[ivpa,imu]) + # # # # #println("") + # # # #end + # # #end + # #end + # #@views heatmap(mu.grid, vpa.grid, Cssp[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # # # #windowsize = (360,240), margin = 15pt) + # # # #outfile = string("fkpl_Cssp_num.pdf") + # # # #savefig(outfile) + #end + #println("max(abs(C_ss'[F_Ms,F_Ms])): ", Cssp_err) + +end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 5bc9cb779..1c4bf86df 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -6,6 +6,7 @@ module fokker_planck export init_fokker_planck_collisions export explicit_fokker_planck_collisions! +export calculate_Rosenbluth_potentials! using SpecialFunctions: ellipk, ellipe using ..type_definitions: mk_float, mk_int @@ -75,15 +76,20 @@ function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_in # mm = 4 vperp vperp' / ( (vpa- vpa')^2 + (vperp + vperp')) denom = (vpa.grid[ivpa] - vpa.grid[ivpap])^2 + (vperp.grid[ivperp] + vperp.grid[ivperpp])^2 if denom < zero - #then vpa = vpa' = vperp' = vperp = 0 - mm = 0.0 - prefac = 0.0 # because vperp' wgt = 0 here - else + println("denom = zero ",ivperpp," ",ivpap," ",ivperp," ",ivpa) + end + # #then vpa = vpa' = vperp' = vperp = 0 + # mm = 0.0 + # prefac = 0.0 # because vperp' wgt = 0 here + #else mm = 4.0*vperp.grid[ivperp]*vperp.grid[ivperpp]/denom prefac = sqrt(denom) - end + #end + #println(mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) elliptic_integral_E_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipe(mm)*prefac/pi elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipk(mm)/(pi*prefac) + println(elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) + end end end @@ -150,7 +156,7 @@ function evaluate_RMJ_collision_operator!(Cssp_out,fs_in,fsp_in,ms,msp,cfreqssp, buffer_1 = fokkerplanck_arrays.buffer_vpavperp_1 buffer_2 = fokkerplanck_arrays.buffer_vpavperp_2 Rosenbluth_G = fokkerplanck_arrays.Rosenbluth_G - Rosenbluth_H = fokkerplanck_arrays.Rosenbluth_G + Rosenbluth_H = fokkerplanck_arrays.Rosenbluth_H nvperp = vperp.n nvpa = vpa.n # zero Cssp to prepare for addition of collision terms From df30f6eb025ad4f69628e14892f7d5754c7ee61e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Apr 2023 13:30:52 +0100 Subject: [PATCH 005/331] Further experiments with the direct elliptic solve to explore the possibility of avoiding the calculation of G, H, and their derivatives via a divergent integrand. The derivative matrices to be inverted have large condition numbers in the 1D cases. Poor behaviour is observed in the 1D case on a half-infinite domain. --- cheb_matrix_test.jl | 367 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 349 insertions(+), 18 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 67cac6d10..6b449053f 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -1,7 +1,7 @@ using Printf using Plots using LaTeXStrings - +using MPI if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") @@ -12,8 +12,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.calculus: derivative!, integral #import LinearAlgebra - using LinearAlgebra: mul!, lu + using LinearAlgebra: mul!, lu, cond + using SparseArrays: sparse + using SpecialFunctions: erf zero = 1.0e-10 + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.1f ", matrix[i,j]) + end + println("") + end + println("\n") + end + function Djj(x::Array{Float64,1},j::Int64) return -0.5*x[j]/( 1.0 - x[j]^2) end @@ -129,18 +143,24 @@ if abspath(PROGRAM_FILE) == @__FILE__ imin = x.imin imax = x.imax + zero_bc_upper_boundary = x.bc == "zero" || x.bc == "zero_upper" + zero_bc_lower_boundary = x.bc == "zero" || x.bc == "zero_lower" + # fill in first element j = 1 - if x.bc == "zero" - D[imin[j],imin[j]:imax[j]] .+= D_elementwise[1,:]./2.0 - D[imin[j],imin[j]] += D_elementwise[x.ngrid,x.ngrid]/2.0 + if zero_bc_lower_boundary #x.bc == "zero" + D[imin[j],imin[j]:imax[j]] .+= D_elementwise[1,:]./2.0 #contributions from this element/2 + D[imin[j],imin[j]] += D_elementwise[x.ngrid,x.ngrid]/2.0 #contribution from missing `zero' element/2 else D[imin[j],imin[j]:imax[j]] .+= D_elementwise[1,:] end for k in 2:imax[j]-imin[j] D[k,imin[j]:imax[j]] .+= D_elementwise[k,:] end - if x.nelement_local > 1 || x.bc == "zero" + if zero_bc_upper_boundary && x.nelement_local == 1 + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 + elseif x.nelement_local > 1 #x.bc == "zero" D[imax[j],imin[j]:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 else D[imax[j],imin[j]:imax[j]] .+= D_elementwise[x.ngrid,:] @@ -153,11 +173,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ D[k+imin[j]-2,imin[j]-1:imax[j]] .+= D_elementwise[k,:] end # upper boundary condition on element - if j == x.nelement_local && !(x.bc == "zero") + if j == x.nelement_local && !(zero_bc_upper_boundary) D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:] - elseif j == x.nelement_local && x.bc == "zero" - D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 - D[imax[j],imax[j]] += D_elementwise[1,1]/2.0 + elseif j == x.nelement_local && zero_bc_upper_boundary + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 else D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 end @@ -278,7 +298,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ################### # define inputs needed for the test - ngrid = 17 #number of points per element + ngrid = 2 #number of points per element nelement_local = 10 # number of elements per rank nelement_global = nelement_local # total number of elements L = 1.0 #physical box size in reference units @@ -288,7 +308,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ adv_input = advection_input("default", 1.0, 0.0, 0.0) nrank = 1 irank = 0 - comm = false + comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate input = grid_input("coord", ngrid, nelement_global, nelement_local, @@ -385,7 +405,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ df_exact[ix] = -2.0*alpha*x.grid[ix]*exp(-alpha*(x.grid[ix])^2) df2_exact[ix] = ((2.0*alpha*x.grid[ix])^2 - 2.0*alpha)*exp(-alpha*(x.grid[ix])^2) end - println("test f: \n",f) + #println("test f: \n",f) # calculate d f / d x from matrix mul!(df,Dxreverse,f) # calculate d^2 f / d x from second application of Dx matrix @@ -400,11 +420,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("Reversed - multiple elements") #println("df \n",df) #println("df_exact \n",df_exact) - println("df_err \n",df_err) + #println("df_err \n",df_err) #println("df2 \n",df2) #println("df2_exact \n",df2_exact) - println("df2_err \n",df2_err) - println("df2cheb_err \n",df2cheb_err) + #println("df2_err \n",df2_err) + #println("df2cheb_err \n",df2cheb_err) println("max(df_err) \n",maximum(abs.(df_err))) println("max(df2_err) \n",maximum(abs.(df2_err))) @@ -449,8 +469,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("result", yy) #println("check result", AA*yy, bb) MMS_test = false - evolution_test = true - + evolution_test = false#true + elliptic_solve_test = true + elliptic_solve_1D_infinite_domain_test = false#true + elliptic_2Dsolve_test = false#true if MMS_test ntest = 5 MMS_errors = Array{Float64,1}(undef,ntest) @@ -528,4 +550,313 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("ff_vs_x.gif") gif(anim, outfile, fps=5) end + + if elliptic_solve_test + println("elliptic solve test") + ngrid = 17 + nelement_local = 50 + L = 25 + nelement_global = nelement_local + input = grid_input("vperp", ngrid, nelement_global, nelement_local, + nrank, irank, L, discretization, fd_option, "zero_upper", adv_input,comm) + y = define_coordinate(input) + Dy = Array{Float64,2}(undef, y.n, y.n) + cheb_derivative_matrix_reversed!(Dy,y) + + + yDy = Array{Float64,2}(undef, y.n, y.n) + for iy in 1:y.n + @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] + end + + + Dy_yDy = Array{Float64,2}(undef, y.n, y.n) + mul!(Dy_yDy,Dy,yDy) + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] + #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] + + D2y = Array{Float64,2}(undef, y.n, y.n) + mul!(D2y,Dy,Dy) + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] + D2y[end,end] = 2.0*D2y[end,end] + yD2y = Array{Float64,2}(undef, y.n, y.n) + for iy in 1:y.n + @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] + end + + if y.n < 20 + print_matrix(Dy,"Dy",y.n,y.n) + print_matrix(yDy,"yDy",y.n,y.n) + print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) + print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) + end + Sy = Array{Float64,1}(undef, y.n) + Fy = Array{Float64,1}(undef, y.n) + Fy_exact = Array{Float64,1}(undef, y.n) + Fy_err = Array{Float64,1}(undef, y.n) + for iy in 1:y.n + Sy[iy] = (1.0 - y.grid[iy])*exp(-y.grid[iy]) + Fy_exact[iy] = -exp(-y.grid[iy]) + end + LL = Array{Float64,2}(undef, y.n, y.n) + #@. LL = yD2y + Dy + @. LL = Dy_yDy + Dirichlet = true + if Dirichlet + @. LL[1,:] = 0.0 + @. LL[end,:] = 0.0 + LL[end,end] = 1.0 + LL[1,1] = 1.0 + Sy[1] = Fy_exact[1]; Sy[end] = Fy_exact[end] + end + + println("condition number: ", cond(LL)) + LL_lu_obj = lu(sparse(LL)) + + # do elliptic solve + Fy = LL_lu_obj\Sy + @. Fy_err = abs(Fy - Fy_exact) + + println("maximum(Fy_err)",maximum(Fy_err)) + #println("Fy_err",Fy_err) + #println("Fy_exact",Fy_exact) + #println("Fy",Fy) + plot([y.grid,y.grid,y.grid], [Fy,Fy_exact,Fy_err], xlabel="y", ylabel="", label=["F" "F_exact" "F_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_elliptic_solve_test.pdf" + savefig(outfile) + plot([y.grid], [Fy_err], xlabel="x", ylabel="", label=["F_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_elliptic_solve_test_err.pdf" + savefig(outfile) + + end + + if elliptic_solve_1D_infinite_domain_test + println("elliptic solve 1D infinite domain test") + ngrid = 17 + nelement_local = 50 + L = 25 + nelement_global = nelement_local + input = grid_input("vpa", ngrid, nelement_global, nelement_local, + nrank, irank, L, discretization, fd_option, "zero", adv_input,comm) + x = define_coordinate(input) + Dx = Array{Float64,2}(undef, x.n, x.n) + cheb_derivative_matrix_reversed!(Dx,x) + + D2x = Array{Float64,2}(undef, x.n, x.n) + mul!(D2x,Dx,Dx) + Dirichlet= true + if Dirichlet + # Dirichlet BC? + @. D2x[1,:] = 0.0 + @. D2x[end,:] = 0.0 + D2x[1,1] = 1.0 + D2x[end,end] = 1.0 + else + # FD-like zero - BC + D2x[1,1] = 2.0*D2x[1,1] + D2x[end,end] = 2.0*D2x[end,end] + end + + if x.n < 20 + print_matrix(Dx,"Dx",x.n,x.n) + print_matrix(D2x,"D2x",x.n,x.n) + end + LLx = Array{Float64,2}(undef, x.n, x.n) + @. LLx = D2x + println("condition number: ", cond(LLx)) + LLx_lu_obj = lu(sparse(LLx)) + + Sx = Array{Float64,1}(undef, x.n) + Fx = Array{Float64,1}(undef, x.n) + Fx_exact = Array{Float64,1}(undef, x.n) + Fx_err = Array{Float64,1}(undef, x.n) + for ix in 1:x.n + Sx[ix] = (4.0*x.grid[ix]^2 - 2.0)*exp(-x.grid[ix]^2) + Fx_exact[ix] = exp(-x.grid[ix]^2) + end + # do elliptic solve + if Dirichlet + Sx[1] = 0.0; Sx[end] = 0.0 #Dirichlet BC values + end + Fx = LLx_lu_obj\Sx + @. Fx_err = abs(Fx - Fx_exact) + + println("test 1: maximum(Fx_err)",maximum(Fx_err)) + #println("Fx_err",Fx_err) + #println("Fx_exact",Fx_exact) + #println("Fx",Fx) + plot([x.grid,x.grid,x.grid], [Fx,Fx_exact,Fx_err], xlabel="x", ylabel="", label=["F" "F_exact" "F_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_infinite_domain_elliptic_solve_test.pdf" + savefig(outfile) + plot([x.grid], [Fx_err], xlabel="x", ylabel="", label=["F_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_infinite_domain_elliptic_solve_test_err.pdf" + savefig(outfile) + + for ix in 1:x.n + Sx[ix] = exp(-x.grid[ix]^2) + Fx_exact[ix] = (sqrt(pi)/2.0)*x.grid[ix]*erf(x.grid[ix]) + exp(-x.grid[ix]^2)/2.0 + end + if Dirichlet + Sx[1] = 0.0; Sx[end] = 0.0 #Dirichlet BC values + end + Fx = LLx_lu_obj\Sx + @. Fx += (sqrt(pi)/2.0)*x.grid[end] + @. Fx_err = abs(Fx - Fx_exact) + println("test 2: maximum(Fx_err)",maximum(Fx_err)) + plot([x.grid], [Fx], xlabel="x", ylabel="", label=["Fx"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_infinite_domain_elliptic_solve_gaussian_source.pdf" + savefig(outfile) + plot([x.grid], [Fx_err], xlabel="x", ylabel="", label=["F_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1D_infinite_domain_elliptic_solve_gaussian_source_err.pdf" + savefig(outfile) + + end + + if elliptic_2Dsolve_test + println("elliptic 2D solve test") + ngrid = 2 + nelement_local = 5 + x_L = 1 + y_L = 1 + nelement_global = nelement_local + + input = grid_input("vpa", ngrid, nelement_global, nelement_local, + nrank, irank, x_L, discretization, fd_option, "zero", adv_input, comm) + x = define_coordinate(input) + + Dx = Array{Float64,2}(undef, x.n, x.n) + cheb_derivative_matrix_reversed!(Dx,x) + D2x = Array{Float64,2}(undef, x.n, x.n) + mul!(D2x,Dx,Dx) + D2x[1,1] = 2.0*D2x[1,1] + D2x[end,end] = 2.0*D2x[end,end] + if x.n < 20 + print_matrix(Dx,"Dx",x.n,x.n) + print_matrix(D2x,"D2x",x.n,x.n) + end + + input = grid_input("vperp", ngrid, nelement_global, nelement_local, + nrank, irank, y_L, discretization, fd_option, "zero_upper", adv_input, comm) + y = define_coordinate(input) + + Dy = Array{Float64,2}(undef, y.n, y.n) + cheb_derivative_matrix_reversed!(Dy,y) + + + yDy = Array{Float64,2}(undef, y.n, y.n) + for iy in 1:y.n + @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] + end + + + Dy_yDy = Array{Float64,2}(undef, y.n, y.n) + mul!(Dy_yDy,Dy,yDy) + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] + #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] + + D2y = Array{Float64,2}(undef, y.n, y.n) + mul!(D2y,Dy,Dy) + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] + D2y[end,end] = 2.0*D2y[end,end] + yD2y = Array{Float64,2}(undef, y.n, y.n) + for iy in 1:y.n + @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] + end + + if y.n < 20 + print_matrix(Dy,"Dy",y.n,y.n) + print_matrix(yDy,"yDy",y.n,y.n) + print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) + print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) + end + + ### now form 2D matrix to invert and corresponding sources + function dH_Maxwellian_dvpa(Bmag,vpa,mu,ivpa,imu) + # speed variable + eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + zero = 1.0e-10 + if eta < zero + dHdvpa = -(4.0*vpa.grid[ivpa])/(3.0*sqrt(pi)) + else + dHdvpa = (2.0/sqrt(pi))*vpa.grid[ivpa]*((exp(-eta^2)/eta) - (erf(eta)/(eta^2))) + end + return dHdvpa + end + # Array in 2D form + nx = x.n + ny = y.n + Sxy = Array{Float64,2}(undef, nx, ny) + Fxy = Array{Float64,2}(undef, nx, ny) + Fxy_exact = Array{Float64,2}(undef, nx, ny) + Fxy_err = Array{Float64,2}(undef, nx, ny) + LLxy = Array{Float64,4}(undef, nx, ny, nx, ny) + # Array in compound 1D form + # ic = (ix-1) + nx*(iy-1) + 1 + # iy = mod(ic,nx) + 1 + # ix = rem(ic,nx) + function icfunc(ix,iy,nx) + return ix + nx*(iy-1) + end + function iyfunc(ic,nx) + #return mod(ic,nx) + 1 + return floor(Int64,(ic-1)/nx) + 1 + end + function ixfunc(ic,nx) + ix = ic - nx*(iyfunc(ic,nx) - 1) + #return rem(ic,nx) + return ix + end + nc = nx*ny + Fc = Array{Float64,1}(undef, nc) + Sc = Array{Float64,1}(undef, nc) + LLc = Array{Float64,2}(undef, nc, nc) + + for iy in 1:ny + for ix in 1:nx + Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-2.0*y.grid[iy]-x.grid[ix]^2)) + Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(1.0,x,y,ix,iy) + for iyp in 1:ny + for ixp in 1:nx + LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] + end + end + end + end + for ic in 1:nc + ix = ixfunc(ic,nx) + iy = iyfunc(ic,nx) + Sc[ic] = Sxy[ix,iy] + for icp in 1:nc + ixp = ixfunc(icp,nx) + iyp = iyfunc(icp,nx) + #println("ic: ",ic," ix: ", ix," iy: ",iy," icp: ",icp," ixp: ", ixp," iyp: ",iyp) + LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] + end + end + println("condition number(LLc): ", cond(LLc)) + LLc_lu_obj = lu(sparse(LLc)) + # do elliptic solve + Fc = LLc_lu_obj\Sc + #reshape to 2D vector + for ic in 1:nc + ix = ixfunc(ic,nx) + iy = iyfunc(ic,nx) + Fxy[ix,iy] = Fc[ic] + end + + @. Fxy_err = abs(Fxy - Fxy_exact) + + println("maximum(Fxy_err)",maximum(Fxy_err)) + println("Fxy_err",Fxy_err[1,:]) + println("Fxy_exact",Fxy_exact[1,:]) + println("Fxy",Fxy[1,:]) + + + end end From 92b69c023da506c8f2d5fb49532511ba35c650a4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 29 Apr 2023 11:30:22 +0100 Subject: [PATCH 006/331] Lots of experimental changes. Attempt to use iterative solvers. Attempt to impose a flux boundary condition at the origin in the 1D half infinite test by replacing a row of the matrix LL. Attempt to change the test to a more relevant test example where the solution has no gradient at the origin in y. These attempts failed to improve accuracy of the solution. The final and successful change is to make consistent the derivative matrix Dy with the radau points in the 1st element of the y grid. With these changes and a single zero bc at y -> inf, we can get machine precision accuracy for sufficiently large ngrid in the y elements. More work is required to understand how to carry these results to the 2D case. --- cheb_matrix_test.jl | 261 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 223 insertions(+), 38 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 6b449053f..96e6a8104 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -9,10 +9,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral + using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral, chebyshev_radau_derivative_single_element! using moment_kinetics.calculus: derivative!, integral #import LinearAlgebra - using LinearAlgebra: mul!, lu, cond + using IterativeSolvers: jacobi!, gauss_seidel!, idrs! + using LinearAlgebra: mul!, lu, cond, det using SparseArrays: sparse using SpecialFunctions: erf zero = 1.0e-10 @@ -26,6 +27,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("") end println("\n") + end + + function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") end function Djj(x::Array{Float64,1},j::Int64) @@ -261,6 +271,94 @@ if abspath(PROGRAM_FILE) == @__FILE__ D .= (2.0*float(nelement)/L).*D end + """ + derivative matrix for radau grid + """ + function calculate_chebyshev_radau_D_matrix_via_FFT!(D::Array{Float64,2}, coord, spectral) + ff_buffer = Array{Float64,1}(undef,coord.ngrid) + df_buffer = Array{Float64,1}(undef,coord.ngrid) + # use response matrix approach to calculate derivative matrix D + for j in 1:coord.ngrid + ff_buffer .= 0.0 + ff_buffer[j] = 1.0 + @views chebyshev_radau_derivative_single_element!(df_buffer[:], ff_buffer[:], + spectral.radau.f[:,1], spectral.radau.df, spectral.radau.fext, spectral.radau.forward, coord) + @. D[:,j] = df_buffer[:] # assign appropriate column of derivative matrix + end + # correct diagonal elements to gurantee numerical stability + # gives D*[1.0, 1.0, ... 1.0] = [0.0, 0.0, ... 0.0] + for j in 1:coord.ngrid + D[j,j] = 0.0 + D[j,j] = -sum(D[j,:]) + end + + #multiply by scale factor for element length + D .= (2.0*float(coord.nelement_global)/coord.L).*D + end + + function cheb_radau_derivative_matrix_reversed!(D::Array{Float64,2},x,x_spectral) + D_lobotto_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + cheb_derivative_matrix_elementwise_reversed!(D_lobotto_elementwise,x.ngrid,x.L,x.nelement_global) + + D_radau_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + calculate_chebyshev_radau_D_matrix_via_FFT!(D_radau_elementwise,x,x_spectral) + if x.ngrid < 8 + print_matrix(D_lobotto_elementwise,"D_lobotto_elementwise",x.ngrid,x.ngrid) + print_matrix(D_radau_elementwise,"D_radau_elementwise",x.ngrid,x.ngrid) + end + assign_cheb_derivative_matrix!(D,D_lobotto_elementwise,D_radau_elementwise,x) + end + + + function assign_cheb_derivative_matrix!(D::Array{Float64,2},D_lobotto_elementwise::Array{Float64,2},D_radau_elementwise::Array{Float64,2},x) + + # zero output matrix before assignment + D[:,:] .= 0.0 + imin = x.imin + imax = x.imax + + zero_bc_upper_boundary = x.bc == "zero" || x.bc == "zero_upper" + zero_bc_lower_boundary = x.bc == "zero" || x.bc == "zero_lower" + + # fill in first element + j = 1 + if zero_bc_lower_boundary #x.bc == "zero" + D[imin[j],imin[j]:imax[j]] .+= D_radau_elementwise[1,:]./2.0 #contributions from this element/2 + D[imin[j],imin[j]] += D_radau_elementwise[x.ngrid,x.ngrid]/2.0 #contribution from missing `zero' element/2 + else + D[imin[j],imin[j]:imax[j]] .+= D_radau_elementwise[1,:] + end + for k in 2:imax[j]-imin[j] + D[k,imin[j]:imax[j]] .+= D_radau_elementwise[k,:] + end + if zero_bc_upper_boundary && x.nelement_local == 1 + D[imax[j],imin[j]-1:imax[j]] .+= D_radau_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_lobotto_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 + elseif x.nelement_local > 1 #x.bc == "zero" + D[imax[j],imin[j]:imax[j]] .+= D_radau_elementwise[x.ngrid,:]./2.0 + else + D[imax[j],imin[j]:imax[j]] .+= D_radau_elementwise[x.ngrid,:] + end + # remaining elements recalling definitions of imax and imin + for j in 2:x.nelement_local + #lower boundary condition on element + D[imin[j]-1,imin[j]-1:imax[j]] .+= D_lobotto_elementwise[1,:]./2.0 + for k in 2:imax[j]-imin[j]+1 + D[k+imin[j]-2,imin[j]-1:imax[j]] .+= D_lobotto_elementwise[k,:] + end + # upper boundary condition on element + if j == x.nelement_local && !(zero_bc_upper_boundary) + D[imax[j],imin[j]-1:imax[j]] .+= D_lobotto_elementwise[x.ngrid,:] + elseif j == x.nelement_local && zero_bc_upper_boundary + D[imax[j],imin[j]-1:imax[j]] .+= D_lobotto_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_lobotto_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 + else + D[imax[j],imin[j]-1:imax[j]] .+= D_lobotto_elementwise[x.ngrid,:]./2.0 + end + end + + end + """ function integrating d y / d t = f(t) """ @@ -553,16 +651,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_solve_test println("elliptic solve test") - ngrid = 17 + ngrid = 25 nelement_local = 50 - L = 25 + L = 8 nelement_global = nelement_local - input = grid_input("vperp", ngrid, nelement_global, nelement_local, - nrank, irank, L, discretization, fd_option, "zero_upper", adv_input,comm) - y = define_coordinate(input) - Dy = Array{Float64,2}(undef, y.n, y.n) - cheb_derivative_matrix_reversed!(Dy,y) - + radau = true #false + if radau + input = grid_input("vperp", ngrid, nelement_global, nelement_local, + nrank, irank, L, discretization, fd_option, "zero_upper", adv_input,comm) + y = define_coordinate(input) + y_spectral = setup_chebyshev_pseudospectral(y) + Dy = Array{Float64,2}(undef, y.n, y.n) + cheb_radau_derivative_matrix_reversed!(Dy,y,y_spectral) + else #lobotto + input = grid_input("vpa", ngrid, nelement_global, nelement_local, + nrank, irank, L, discretization, fd_option, "zero_upper", adv_input,comm) + y = define_coordinate(input) + @. y.grid += y.L/2 + Dy = Array{Float64,2}(undef, y.n, y.n) + cheb_derivative_matrix_reversed!(Dy,y) + end yDy = Array{Float64,2}(undef, y.n, y.n) for iy in 1:y.n @@ -595,28 +703,67 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fy_exact = Array{Float64,1}(undef, y.n) Fy_err = Array{Float64,1}(undef, y.n) for iy in 1:y.n - Sy[iy] = (1.0 - y.grid[iy])*exp(-y.grid[iy]) - Fy_exact[iy] = -exp(-y.grid[iy]) + #Sy[iy] = (y.grid[iy] - 1.0)*exp(-y.grid[iy]) + #Fy_exact[iy] = exp(-y.grid[iy]) + Sy[iy] = 4.0*y.grid[iy]*(y.grid[iy]^2 - 1.0)*exp(-y.grid[iy]^2) + Fy_exact[iy] = exp(-y.grid[iy]^2) end LL = Array{Float64,2}(undef, y.n, y.n) #@. LL = yD2y + Dy - @. LL = Dy_yDy + for iy in 1:y.n + #@. LL[iy,:] = Dy_yDy[iy,:] #*(1.0/y.grid[iy]) + @. LL[iy,:] = yD2y[iy,:] + Dy[iy,:] #*(1.0/y.grid[iy]) + end Dirichlet = true if Dirichlet - @. LL[1,:] = 0.0 + # fixed value at orgin -- doesn't work well + #@. LL[1,:] = 0.0 + #Sy[1] = Fy_exact[1] + set_flux = false + if set_flux + # set flux at origin + @. LL[1,:] = 0.0 + ilim = y.imax[1] + @. LL[1,:] = yDy[ilim,:] + + print_vector(Sy,"Sy before",y.n) + integrand = Array{Float64,1}(undef,ilim) + @. integrand[1:ilim] = Sy[1:ilim]*y.wgts[1:ilim]/(2.0*y.grid[1:ilim]) + + print_vector(integrand,"integrand",ilim) + print_vector(y.wgts,"wgts",y.n) + #@. integrand[1:ilim] = y.grid[1:ilim]*Sy[1:ilim]*y.wgts[1:ilim] + flux = sum(integrand) + Sy[1] = flux + end + # zero at infinity @. LL[end,:] = 0.0 LL[end,end] = 1.0 - LL[1,1] = 1.0 - Sy[1] = Fy_exact[1]; Sy[end] = Fy_exact[end] + #LL[1,1] = 1.0 +# @. LL[1,:] = 2.0*D2y[1,:] + Sy[end] = Fy_exact[end] + + #print_matrix(LL,"LL",y.n,y.n) + #print_vector(Sy,"Sy",y.n) end - println("condition number: ", cond(LL)) - LL_lu_obj = lu(sparse(LL)) - - # do elliptic solve - Fy = LL_lu_obj\Sy - @. Fy_err = abs(Fy - Fy_exact) - + #lu_solver = false + #gauss_seidel_solver = true + #if lu_solver + println("det: ", det(LL)) + println("condition number: ", cond(LL)) + LL_lu_obj = lu(sparse(LL)) + + # do elliptic solve + Fy = LL_lu_obj\Sy + #elseif gauss_seidel_solver + # niter=100 + # @. Fy[:] = Fy_exact[:] # initial guess + # gauss_seidel!(Fy,sparse(LL),Sy,maxiter=niter) + #else + # println("no solution method prescribed") + #end + @. Fy_err = abs(Fy - Fy_exact) println("maximum(Fy_err)",maximum(Fy_err)) #println("Fy_err",Fy_err) #println("Fy_exact",Fy_exact) @@ -665,8 +812,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ end LLx = Array{Float64,2}(undef, x.n, x.n) @. LLx = D2x - println("condition number: ", cond(LLx)) - LLx_lu_obj = lu(sparse(LLx)) Sx = Array{Float64,1}(undef, x.n) Fx = Array{Float64,1}(undef, x.n) @@ -680,7 +825,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ if Dirichlet Sx[1] = 0.0; Sx[end] = 0.0 #Dirichlet BC values end - Fx = LLx_lu_obj\Sx + + println("condition number: ", cond(LLx)) + LLx_lu_obj = lu(sparse(LLx)) + lu_solver = true#false + iterative_solver= false#true + if lu_solver + Fx = LLx_lu_obj\Sx + elseif iterative_solver + niter=1000 + @. Fx[:] = 1.0/(x.grid[:]^8 + 1.0) # initial guess Fx_exact[:] + Fx[1] =0.0; Fx[end] =0.0 + #gauss_seidel!(Fx,sparse(LLx),Sx,maxiter=niter) + #jacobi!(Fx,sparse(LLx),Sx,maxiter=niter) + idrs!(Fx,sparse(LLx),Sx;abstol=10^(-10)) + else + println("no solution method prescribed") + end @. Fx_err = abs(Fx - Fx_exact) println("test 1: maximum(Fx_err)",maximum(Fx_err)) @@ -703,7 +864,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ if Dirichlet Sx[1] = 0.0; Sx[end] = 0.0 #Dirichlet BC values end - Fx = LLx_lu_obj\Sx + + if lu_solver + Fx = LLx_lu_obj\Sx + elseif iterative_solver + niter=1000 + @. Fx[:] = 1.0/(x.grid[:]^8 + 1.0) # initial guess Fx_exact[:] + Fx[1] =0.0; Fx[end] =0.0 + #gauss_seidel!(Fx,sparse(LLx),Sx,maxiter=niter) + #jacobi!(Fx,sparse(LLx),Sx,maxiter=niter) + idrs!(Fx,sparse(LLx),Sx) + else + println("no solution method prescribed") + end + @. Fx += (sqrt(pi)/2.0)*x.grid[end] @. Fx_err = abs(Fx - Fx_exact) println("test 2: maximum(Fx_err)",maximum(Fx_err)) @@ -721,7 +895,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") ngrid = 2 - nelement_local = 5 + nelement_local = 3 x_L = 1 y_L = 1 nelement_global = nelement_local @@ -734,8 +908,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ cheb_derivative_matrix_reversed!(Dx,x) D2x = Array{Float64,2}(undef, x.n, x.n) mul!(D2x,Dx,Dx) - D2x[1,1] = 2.0*D2x[1,1] - D2x[end,end] = 2.0*D2x[end,end] + #D2x[1,1] = 2.0*D2x[1,1] + #D2x[end,end] = 2.0*D2x[end,end] + @. D2x[1,:] = 0.0 + @. D2x[end,:] = 0.0 + D2x[1,1] = 1.0 + D2x[end,end] = 1.0 + if x.n < 20 print_matrix(Dx,"Dx",x.n,x.n) print_matrix(D2x,"D2x",x.n,x.n) @@ -754,12 +933,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] end - - Dy_yDy = Array{Float64,2}(undef, y.n, y.n) - mul!(Dy_yDy,Dy,yDy) - #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] - #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] - D2y = Array{Float64,2}(undef, y.n, y.n) mul!(D2y,Dy,Dy) #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] @@ -769,6 +942,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] end + Dy_yDy = Array{Float64,2}(undef, y.n, y.n) + mul!(Dy_yDy,Dy,yDy) + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] + #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] + @. Dy_yDy[1,:] = 2.0*yD2y[1,:] + @. Dy_yDy[end,:] = 0.0 + Dy_yDy[end,end] = 1.0 + if y.n < 20 print_matrix(Dy,"Dy",y.n,y.n) print_matrix(yDy,"yDy",y.n,y.n) @@ -817,13 +998,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ Sc = Array{Float64,1}(undef, nc) LLc = Array{Float64,2}(undef, nc, nc) + for iy in 1:ny for ix in 1:nx Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-2.0*y.grid[iy]-x.grid[ix]^2)) Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(1.0,x,y,ix,iy) for iyp in 1:ny for ixp in 1:nx - LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] + #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] + LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + Dy_yDy[iyp,iy] end end end @@ -839,8 +1022,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] end end + print_matrix(LLc,"LLc",nc,nc) println("condition number(LLc): ", cond(LLc)) - LLc_lu_obj = lu(sparse(LLc)) + println("determinant(LLc): ", det(LLc)) + LLc_lu_obj = lu(LLc) # do elliptic solve Fc = LLc_lu_obj\Sc #reshape to 2D vector From cfa6edb7a98ba332997f185657b76503c1f37f2f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 30 Apr 2023 09:51:07 +0100 Subject: [PATCH 007/331] Changes which fix the 2D elliptic solver for the case where the solution is a Maxwellian in vpa (x) and vperp (y). The initialisation time appears to be the dominant cost for high resolutions. Further optimisations are required to speed up the solver, and a method to treat functions that do not decay rapidly to zero is required (to permit reasonable values of L). --- cheb_matrix_test.jl | 73 ++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 96e6a8104..bafd9ee33 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -2,6 +2,8 @@ using Printf using Plots using LaTeXStrings using MPI +using Measures + if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") @@ -568,9 +570,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("check result", AA*yy, bb) MMS_test = false evolution_test = false#true - elliptic_solve_test = true + elliptic_solve_test = false#true elliptic_solve_1D_infinite_domain_test = false#true - elliptic_2Dsolve_test = false#true + elliptic_2Dsolve_test = true if MMS_test ntest = 5 MMS_errors = Array{Float64,1}(undef,ntest) @@ -894,13 +896,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") - ngrid = 2 - nelement_local = 3 - x_L = 1 - y_L = 1 - nelement_global = nelement_local + x_ngrid = 5 + x_nelement_local = 32 + x_L = 12 + y_L = 6 + y_ngrid = 5 + y_nelement_local = 16 - input = grid_input("vpa", ngrid, nelement_global, nelement_local, + x_nelement_global = x_nelement_local + y_nelement_global = y_nelement_local + + input = grid_input("vpa", x_ngrid, x_nelement_global, x_nelement_local, nrank, irank, x_L, discretization, fd_option, "zero", adv_input, comm) x = define_coordinate(input) @@ -920,12 +926,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ print_matrix(D2x,"D2x",x.n,x.n) end - input = grid_input("vperp", ngrid, nelement_global, nelement_local, + input = grid_input("vperp", y_ngrid, y_nelement_global, y_nelement_local, nrank, irank, y_L, discretization, fd_option, "zero_upper", adv_input, comm) y = define_coordinate(input) - + y_spectral = setup_chebyshev_pseudospectral(y) Dy = Array{Float64,2}(undef, y.n, y.n) - cheb_derivative_matrix_reversed!(Dy,y) + cheb_radau_derivative_matrix_reversed!(Dy,y,y_spectral) yDy = Array{Float64,2}(undef, y.n, y.n) @@ -956,11 +962,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) end - + println("Initialised 1D arrays") ### now form 2D matrix to invert and corresponding sources - function dH_Maxwellian_dvpa(Bmag,vpa,mu,ivpa,imu) + function dH_Maxwellian_dvpa(vpa,vperp,ivpa,ivperp) # speed variable - eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) zero = 1.0e-10 if eta < zero dHdvpa = -(4.0*vpa.grid[ivpa])/(3.0*sqrt(pi)) @@ -998,15 +1004,24 @@ if abspath(PROGRAM_FILE) == @__FILE__ Sc = Array{Float64,1}(undef, nc) LLc = Array{Float64,2}(undef, nc, nc) - + @. LLxy = 0.0 for iy in 1:ny for ix in 1:nx - Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-2.0*y.grid[iy]-x.grid[ix]^2)) - Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(1.0,x,y,ix,iy) + #Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-y.grid[iy]^2-x.grid[ix]^2)) + #Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(x,y,ix,iy) + + Sxy[ix,iy] = ((4.0*x.grid[ix]^2 - 2.0) + (4.0*y.grid[iy]^2 - 4.0))*exp(-y.grid[iy]^2-x.grid[ix]^2) + Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) for iyp in 1:ny for ixp in 1:nx #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] - LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + Dy_yDy[iyp,iy] + if iy == iyp + LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] + end + if ix == ixp + #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] + LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] + end end end end @@ -1022,10 +1037,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] end end - print_matrix(LLc,"LLc",nc,nc) + + println("Initialised 2D arrays") + if nc < 30 + print_matrix(LLc,"LLc",nc,nc) + end println("condition number(LLc): ", cond(LLc)) println("determinant(LLc): ", det(LLc)) LLc_lu_obj = lu(LLc) + println("Initialised 2D solve") # do elliptic solve Fc = LLc_lu_obj\Sc #reshape to 2D vector @@ -1035,13 +1055,24 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fxy[ix,iy] = Fc[ic] end + println("Finished 2D solve") @. Fxy_err = abs(Fxy - Fxy_exact) println("maximum(Fxy_err)",maximum(Fxy_err)) println("Fxy_err",Fxy_err[1,:]) println("Fxy_exact",Fxy_exact[1,:]) println("Fxy",Fxy[1,:]) - - + @views heatmap(y.grid, x.grid, Fxy_exact[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("Fxy_exact_2D_solve.pdf") + savefig(outfile) + @views heatmap(y.grid, x.grid, Fxy[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("Fxy_num_2D_solve.pdf") + savefig(outfile) + @views heatmap(y.grid, x.grid, Fxy_err[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("Fxy_err_2D_solve.pdf") + savefig(outfile) end end From a08f43c1c3cd1bec6d2886625bd6deefdad6a235 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 30 Apr 2023 10:24:17 +0100 Subject: [PATCH 008/331] Attempt to speed up initialisation by taking if ixp==ix & if iyp==iy statements outside of loop, and reducing the number of loops over all ix, ixp, iy, iyp. --- cheb_matrix_test.jl | 61 +++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index bafd9ee33..07571ac5d 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -897,7 +897,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") x_ngrid = 5 - x_nelement_local = 32 + x_nelement_local = 32 x_L = 12 y_L = 6 y_ngrid = 5 @@ -921,9 +921,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ D2x[1,1] = 1.0 D2x[end,end] = 1.0 + IIx = Array{Float64,2}(undef,x.n,x.n) + @. IIx = 0.0 + for ix in 1:x.n + IIx[ix,ix] = 1.0 + end + if x.n < 20 print_matrix(Dx,"Dx",x.n,x.n) print_matrix(D2x,"D2x",x.n,x.n) + print_matrix(IIx,"IIx",x.n,x.n) end input = grid_input("vperp", y_ngrid, y_nelement_global, y_nelement_local, @@ -956,11 +963,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Dy_yDy[end,:] = 0.0 Dy_yDy[end,end] = 1.0 + IIy = Array{Float64,2}(undef,y.n,y.n) + @. IIy = 0.0 + for iy in 1:y.n + IIy[iy,iy] = 1.0 + end if y.n < 20 print_matrix(Dy,"Dy",y.n,y.n) print_matrix(yDy,"yDy",y.n,y.n) print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) + print_matrix(IIy,"IIy",y.n,y.n) end println("Initialised 1D arrays") ### now form 2D matrix to invert and corresponding sources @@ -982,7 +995,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fxy = Array{Float64,2}(undef, nx, ny) Fxy_exact = Array{Float64,2}(undef, nx, ny) Fxy_err = Array{Float64,2}(undef, nx, ny) - LLxy = Array{Float64,4}(undef, nx, ny, nx, ny) + #LLxy = Array{Float64,4}(undef, nx, ny, nx, ny) # Array in compound 1D form # ic = (ix-1) + nx*(iy-1) + 1 # iy = mod(ic,nx) + 1 @@ -999,12 +1012,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ #return rem(ic,nx) return ix end + function kronecker_delta(i,j) + delta = 0.0 + if i == j + delta = 1.0 + end + return delta + end nc = nx*ny Fc = Array{Float64,1}(undef, nc) Sc = Array{Float64,1}(undef, nc) LLc = Array{Float64,2}(undef, nc, nc) - @. LLxy = 0.0 + #@. LLxy = 0.0 + @. LLc = 0.0 for iy in 1:ny for ix in 1:nx #Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-y.grid[iy]^2-x.grid[ix]^2)) @@ -1012,18 +1033,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ Sxy[ix,iy] = ((4.0*x.grid[ix]^2 - 2.0) + (4.0*y.grid[iy]^2 - 4.0))*exp(-y.grid[iy]^2-x.grid[ix]^2) Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) - for iyp in 1:ny - for ixp in 1:nx - #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] - if iy == iyp - LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] - end - if ix == ixp - #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] - LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] - end - end - end + #for iyp in 1:ny + # for ixp in 1:nx + # #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] + # if iy == iyp + # LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] + # end + # if ix == ixp + # #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] + # LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] + # end + # end + #end end end for ic in 1:nc @@ -1034,7 +1055,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ ixp = ixfunc(icp,nx) iyp = iyfunc(icp,nx) #println("ic: ",ic," ix: ", ix," iy: ",iy," icp: ",icp," ixp: ", ixp," iyp: ",iyp) - LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] + #LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] + #LLc[icp,ic] = D2x[ixp,ix]*kronecker_delta(iyp,iy) + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*kronecker_delta(ixp,ix) + LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*IIx[ixp,ix] end end @@ -1059,9 +1082,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Fxy_err = abs(Fxy - Fxy_exact) println("maximum(Fxy_err)",maximum(Fxy_err)) - println("Fxy_err",Fxy_err[1,:]) - println("Fxy_exact",Fxy_exact[1,:]) - println("Fxy",Fxy[1,:]) + #println("Fxy_err",Fxy_err[1,:]) + #println("Fxy_exact",Fxy_exact[1,:]) + #println("Fxy",Fxy[1,:]) @views heatmap(y.grid, x.grid, Fxy_exact[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("Fxy_exact_2D_solve.pdf") From 1b2f9da61a19fb4a9637b6f518387e34d32140ba Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 2 May 2023 08:29:03 +0100 Subject: [PATCH 009/331] Functioning calculation of dHdvpa for F_M input using elliptic solve. Both zero and fixed value boundary conditions are supported. In the zero boundary condition case, the dominant errors are at x (vpa) -> +-inf, since dHdvpa ~ vpa/eta^3 at infinity. In the fixed value case, we use the known asymptotic behaviour of dHdvpa to specify the appropriate value of the function at infinity. This appears to cause ringing to propagate in the solution causing similar levels of error to the zero boundary condition option, everywhere in the domain. The exponential decay test remains supported. --- cheb_matrix_test.jl | 184 ++++++++++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 51 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 07571ac5d..6ba946525 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -897,14 +897,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") x_ngrid = 5 - x_nelement_local = 32 - x_L = 12 - y_L = 6 + x_nelement_local = 16 + x_L = 24 + y_L = 12 y_ngrid = 5 - y_nelement_local = 16 + y_nelement_local = 8 x_nelement_global = x_nelement_local y_nelement_global = y_nelement_local + # bc option + dirichlet_zero = false#true + dirichlet_fixed_value = true#false + # test option + exponential_decay_test = false input = grid_input("vpa", x_ngrid, x_nelement_global, x_nelement_local, nrank, irank, x_L, discretization, fd_option, "zero", adv_input, comm) @@ -914,12 +919,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ cheb_derivative_matrix_reversed!(Dx,x) D2x = Array{Float64,2}(undef, x.n, x.n) mul!(D2x,Dx,Dx) - #D2x[1,1] = 2.0*D2x[1,1] - #D2x[end,end] = 2.0*D2x[end,end] - @. D2x[1,:] = 0.0 - @. D2x[end,:] = 0.0 - D2x[1,1] = 1.0 - D2x[end,end] = 1.0 + + # set x bc on D2x + if dirichlet_zero + D2x[1,1] = 2.0*D2x[1,1] + D2x[end,end] = 2.0*D2x[end,end] + elseif dirichlet_fixed_value + @. D2x[1,:] = 0.0 + @. D2x[end,:] = 0.0 + D2x[1,1] = 1.0 + D2x[end,end] = 1.0 + end IIx = Array{Float64,2}(undef,x.n,x.n) @. IIx = 0.0 @@ -940,29 +950,39 @@ if abspath(PROGRAM_FILE) == @__FILE__ Dy = Array{Float64,2}(undef, y.n, y.n) cheb_radau_derivative_matrix_reversed!(Dy,y,y_spectral) - - yDy = Array{Float64,2}(undef, y.n, y.n) - for iy in 1:y.n - @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] - end - D2y = Array{Float64,2}(undef, y.n, y.n) mul!(D2y,Dy,Dy) + if dirichlet_zero + D2y[end,end] = 2.0*D2y[end,end] + end + #yDy = Array{Float64,2}(undef, y.n, y.n) + #for iy in 1:y.n + # @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] + #end + #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] - D2y[end,end] = 2.0*D2y[end,end] - yD2y = Array{Float64,2}(undef, y.n, y.n) - for iy in 1:y.n - @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] - end + #yD2y = Array{Float64,2}(undef, y.n, y.n) + #for iy in 1:y.n + # @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] + #end - Dy_yDy = Array{Float64,2}(undef, y.n, y.n) - mul!(Dy_yDy,Dy,yDy) + #Dy_yDy = Array{Float64,2}(undef, y.n, y.n) + #mul!(Dy_yDy,Dy,yDy) #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] - @. Dy_yDy[1,:] = 2.0*yD2y[1,:] - @. Dy_yDy[end,:] = 0.0 - Dy_yDy[end,end] = 1.0 + #@. Dy_yDy[1,:] = 2.0*yD2y[1,:] + #@. Dy_yDy[end,:] = 0.0 + #Dy_yDy[end,end] = 1.0 + # y derivative operator + LLy = Array{Float64,2}(undef,y.n,y.n) + for iy in 1:y.n + @. LLy[iy,:] = D2y[iy,:] + (1.0/y.grid[iy])*Dy[iy,:] + end + if dirichlet_fixed_value + @. LLy[end,:] = 0.0 + LLy[end,end] = 1.0 + end IIy = Array{Float64,2}(undef,y.n,y.n) @. IIy = 0.0 for iy in 1:y.n @@ -970,9 +990,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if y.n < 20 print_matrix(Dy,"Dy",y.n,y.n) - print_matrix(yDy,"yDy",y.n,y.n) - print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) - print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) + print_matrix(D2y,"D2y",y.n,y.n) + print_matrix(LLy,"LLy",y.n,y.n) + #print_matrix(yDy,"yDy",y.n,y.n) + #print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) + #print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) print_matrix(IIy,"IIy",y.n,y.n) end println("Initialised 1D arrays") @@ -984,7 +1006,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ if eta < zero dHdvpa = -(4.0*vpa.grid[ivpa])/(3.0*sqrt(pi)) else - dHdvpa = (2.0/sqrt(pi))*vpa.grid[ivpa]*((exp(-eta^2)/eta) - (erf(eta)/(eta^2))) + dHdvpa = (vpa.grid[ivpa]/eta)*((2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2))) end return dHdvpa end @@ -1024,29 +1046,62 @@ if abspath(PROGRAM_FILE) == @__FILE__ Sc = Array{Float64,1}(undef, nc) LLc = Array{Float64,2}(undef, nc, nc) + if exponential_decay_test + for iy in 1:ny + for ix in 1:nx + # Exponential test inputs below + Sxy[ix,iy] = ((4.0*x.grid[ix]^2 - 2.0) + (4.0*y.grid[iy]^2 - 4.0))*exp(-y.grid[iy]^2-x.grid[ix]^2) + Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) + end + end + else #@. LLxy = 0.0 - @. LLc = 0.0 - for iy in 1:ny + for iy in 1:ny + for ix in 1:nx + # Rosenbluth dHdvpa test + Sxy[ix,iy] = -(4.0/sqrt(pi))*(-2.0*x.grid[ix]*exp(-y.grid[iy]^2-x.grid[ix]^2)) + Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(x,y,ix,iy) + + #for iyp in 1:ny + # for ixp in 1:nx + # #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] + # if iy == iyp + # LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] + # end + # if ix == ixp + # #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] + # LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] + # end + # end + #end + end + end + end + function dHdvpa_inf(vpa,vperp,ivpa,ivperp) + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + dHdvpa_inf = -vpa.grid[ivpa]/eta^3 + return dHdvpa_inf + end + if dirichlet_fixed_value + # set boundary values for ix in 1:nx - #Sxy[ix,iy] = -4.0*pi*(-2.0*x.grid[ix]*exp(-y.grid[iy]^2-x.grid[ix]^2)) - #Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(x,y,ix,iy) - - Sxy[ix,iy] = ((4.0*x.grid[ix]^2 - 2.0) + (4.0*y.grid[iy]^2 - 4.0))*exp(-y.grid[iy]^2-x.grid[ix]^2) - Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) - #for iyp in 1:ny - # for ixp in 1:nx - # #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] - # if iy == iyp - # LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] - # end - # if ix == ixp - # #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] - # LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] - # end - # end - #end + Sxy[ix,ny] = dHdvpa_inf(x,y,ix,ny) end + for iy in 1:ny + Sxy[1,iy] = dHdvpa_inf(x,y,1,iy) + Sxy[nx,iy] = dHdvpa_inf(x,y,nx,iy) + end + #println(Sxy[:,ny]) + println(abs.(Sxy[:,ny] .- Fxy_exact[:,ny])) + println(abs.(Sxy[1,:] .- Fxy_exact[1,:])) + println(abs.(Sxy[nx,:] .- Fxy_exact[nx,:])) + #println(Sxy[1,:]) + #println(Fxy_exact[1,:]) + #println(Sxy[nx,:]) + #println(Fxy_exact[nx,:]) end + # assign values to arrays in compound coordinates + @. LLc = 0.0 for ic in 1:nc ix = ixfunc(ic,nx) iy = iyfunc(ic,nx) @@ -1057,10 +1112,37 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("ic: ",ic," ix: ", ix," iy: ",iy," icp: ",icp," ixp: ", ixp," iyp: ",iyp) #LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] #LLc[icp,ic] = D2x[ixp,ix]*kronecker_delta(iyp,iy) + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*kronecker_delta(ixp,ix) - LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*IIx[ixp,ix] + #LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*IIx[ixp,ix] + LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + LLy[iyp,iy]*IIx[ixp,ix] + end + end + #set fixed bc in LLc directly + if dirichlet_fixed_value + ix = 1; ixp = 1 + for iyp in 1:ny + for iy in 1:ny + ic = icfunc(ix,iy,nx) + icp = icfunc(ixp,iyp,nx) + LLc[icp,ic] = IIy[iyp,iy] + end + end + ix = nx; ixp = nx + for iyp in 1:ny + for iy in 1:ny + ic = icfunc(ix,iy,nx) + icp = icfunc(ixp,iyp,nx) + LLc[icp,ic] = IIy[iyp,iy] + end + end + iy = ny; iyp = ny + for ixp in 1:nx + for ix in 1:nx + ic = icfunc(ix,iy,nx) + icp = icfunc(ixp,iyp,nx) + LLc[icp,ic] = IIx[ixp,ix] + end end end - println("Initialised 2D arrays") if nc < 30 print_matrix(LLc,"LLc",nc,nc) From 71ad0f003f2553a512713d97a39793bc2addc69e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 2 May 2023 08:33:42 +0100 Subject: [PATCH 010/331] Remove commented out code from elliptic 2D solve test. --- cheb_matrix_test.jl | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 6ba946525..8d7f2dcaa 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -954,26 +954,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ mul!(D2y,Dy,Dy) if dirichlet_zero D2y[end,end] = 2.0*D2y[end,end] - end - #yDy = Array{Float64,2}(undef, y.n, y.n) - #for iy in 1:y.n - # @. yDy[iy,:] = y.grid[iy]*Dy[iy,:] - #end - - #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] - #yD2y = Array{Float64,2}(undef, y.n, y.n) - #for iy in 1:y.n - # @. yD2y[iy,:] = y.grid[iy]*D2y[iy,:] - #end - - #Dy_yDy = Array{Float64,2}(undef, y.n, y.n) - #mul!(Dy_yDy,Dy,yDy) - #Dy_yDy[1,1] = 2.0*Dy_yDy[1,1] - #Dy_yDy[end,end] = 2.0*Dy_yDy[end,end] - #@. Dy_yDy[1,:] = 2.0*yD2y[1,:] - #@. Dy_yDy[end,:] = 0.0 - #Dy_yDy[end,end] = 1.0 - + end # y derivative operator LLy = Array{Float64,2}(undef,y.n,y.n) for iy in 1:y.n @@ -992,9 +973,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ print_matrix(Dy,"Dy",y.n,y.n) print_matrix(D2y,"D2y",y.n,y.n) print_matrix(LLy,"LLy",y.n,y.n) - #print_matrix(yDy,"yDy",y.n,y.n) - #print_matrix(Dy_yDy,"Dy_yDy",y.n,y.n) - #print_matrix(yD2y+Dy,"yD2y+Dy",y.n,y.n) print_matrix(IIy,"IIy",y.n,y.n) end println("Initialised 1D arrays") @@ -1061,19 +1039,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ # Rosenbluth dHdvpa test Sxy[ix,iy] = -(4.0/sqrt(pi))*(-2.0*x.grid[ix]*exp(-y.grid[iy]^2-x.grid[ix]^2)) Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(x,y,ix,iy) - - #for iyp in 1:ny - # for ixp in 1:nx - # #LLxy[ixp,iyp,ix,iy] = D2x[ixp,ix] + yD2y[iyp,iy] + Dy[iyp,iy] - # if iy == iyp - # LLxy[ixp,iyp,ix,iy] += D2x[ixp,ix] #+ Dy_yDy[iyp,iy] - # end - # if ix == ixp - # #LLxy[ixp,iyp,ix,iy] += yD2y[iyp,iy] + Dy[iyp,iy] - # LLxy[ixp,iyp,ix,iy] += D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy] - # end - # end - #end end end end @@ -1092,6 +1057,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Sxy[nx,iy] = dHdvpa_inf(x,y,nx,iy) end #println(Sxy[:,ny]) + println("Check boundary specification") println(abs.(Sxy[:,ny] .- Fxy_exact[:,ny])) println(abs.(Sxy[1,:] .- Fxy_exact[1,:])) println(abs.(Sxy[nx,:] .- Fxy_exact[nx,:])) @@ -1110,9 +1076,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ ixp = ixfunc(icp,nx) iyp = iyfunc(icp,nx) #println("ic: ",ic," ix: ", ix," iy: ",iy," icp: ",icp," ixp: ", ixp," iyp: ",iyp) - #LLc[icp,ic] = LLxy[ixp,iyp,ix,iy] - #LLc[icp,ic] = D2x[ixp,ix]*kronecker_delta(iyp,iy) + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*kronecker_delta(ixp,ix) - #LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + (D2y[iyp,iy] + (1.0/y.grid[iyp])*Dy[iyp,iy])*IIx[ixp,ix] LLc[icp,ic] = D2x[ixp,ix]*IIy[iyp,iy] + LLy[iyp,iy]*IIx[ixp,ix] end end From 22b9d138a3bb3d0f6700656fc8dcdf3e30de1252 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 3 May 2023 09:23:32 +0000 Subject: [PATCH 011/331] Fixes to prevent collision operator routines from being called when vperp.n = 1. --- src/fokker_planck.jl | 8 +++++--- src/time_advance.jl | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 1c4bf86df..3c52d43d1 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -103,9 +103,11 @@ function that initialises the arrays needed for Fokker Planck collisions function init_fokker_planck_collisions(vperp,vpa) fokkerplanck_arrays = allocate_fokkerplanck_arrays(vperp,vpa) - @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, - fokkerplanck_arrays.elliptic_integral_K_factor, - vperp,vpa) + if vperp.n > 1 + @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, + fokkerplanck_arrays.elliptic_integral_K_factor, + vperp,vpa) + end return fokkerplanck_arrays end diff --git a/src/time_advance.jl b/src/time_advance.jl index f1af4068f..4a7cfa901 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -204,7 +204,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance_continuity = false advance_force_balance = false advance_energy = false - if collisions.nuii > 0.0 + if collisions.nuii > 0.0 && vperp.n > 1 explicit_fp_collisions = true else explicit_fp_collisions = false From fc007186b18bae6ca28d466d45a5ecaa02a4720c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 3 May 2023 14:16:03 +0000 Subject: [PATCH 012/331] Addition of a test calculation of d2Gdvpa2 from d2Hdvpa2. Problematic oscillations arise in the solution, possibly due to the interaction of the Dirichlet boundary condition and the slow decay of d2Gdvpa2 with large speeds. Errors of ~ 10% are observed for reasonable values of the resolution parameters. --- cheb_matrix_test.jl | 155 +++++++++++++++++++++++++++++++++----------- 1 file changed, 117 insertions(+), 38 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index 8d7f2dcaa..d71295ca3 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -14,7 +14,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral, chebyshev_radau_derivative_single_element! using moment_kinetics.calculus: derivative!, integral #import LinearAlgebra - using IterativeSolvers: jacobi!, gauss_seidel!, idrs! + #using IterativeSolvers: jacobi!, gauss_seidel!, idrs! using LinearAlgebra: mul!, lu, cond, det using SparseArrays: sparse using SpecialFunctions: erf @@ -388,6 +388,61 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end + """ + functions for Rosenbluth potential tests + """ + function dH_Maxwellian_dvpa(vpa,vperp,ivpa,ivperp) + # speed variable + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + zero = 1.0e-10 + if eta < zero + dHdvpa = -(4.0*vpa.grid[ivpa])/(3.0*sqrt(pi)) + else + dHdvpa = (vpa.grid[ivpa]/eta)*((2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2))) + end + return dHdvpa + end + function d2H_Maxwellian_dvpa2(vpa,vperp,ivpa,ivperp) + # speed variable + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + zero = 1.0e-10 + if eta < zero + dHdeta_over_eta = -4.0/(3.0*sqrt(pi)) + d2Hdeta2 = -4.0/(3.0*sqrt(pi)) + else + dHdeta_over_eta = (2.0/sqrt(pi))*(exp(-eta^2)/eta^2) - (erf(eta)/(eta^3)) + d2Hdeta2 = 2.0*(erf(eta)/(eta^3)) - (4.0/sqrt(pi))*(1.0 + (1.0/eta^2))*exp(-eta^2) + end + d2Hdvpa2 = ((vperp.grid[ivperp]^2)/(eta^2))*dHdeta_over_eta + ((vpa.grid[ivpa]^2)/(eta^2))*d2Hdeta2 + return d2Hdvpa2 + end + function d2G_Maxwellian_dvpa2(vpa,vperp,ivpa,ivperp) + # speed variable + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + zero = 1.0e-10 + if eta < zero + dGdeta_over_eta = 4.0/(3.0*sqrt(pi)) + d2Gdeta2 = 4.0/(3.0*sqrt(pi)) + else + dGdeta_over_eta = (1.0/sqrt(pi))*(exp(-eta^2)/(eta^2)) + (1.0 - (0.5/eta^2))*erf(eta)/eta + d2Gdeta2 = (erf(eta)/(eta^3)) - (2.0/sqrt(pi))*exp(-eta^2)/eta^2 + end + d2Gdvpa2 = ((vperp.grid[ivperp]^2)/(eta^2))*dGdeta_over_eta + ((vpa.grid[ivpa]^2)/(eta^2))*d2Gdeta2 + return d2Gdvpa2 + end + + function dHdvpa_inf(vpa,vperp,ivpa,ivperp) + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + dHdvpa_inf = -vpa.grid[ivpa]/eta^3 + return dHdvpa_inf + end + function d2Gdvpa2_inf(vpa,vperp,ivpa,ivperp) + eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) + d2Gdvpa2_inf = ((vpa.grid[ivpa]^2)/eta^5) + ((vperp.grid[ivperp]^2)/eta^3)*( 1.0 - (0.5/eta^2)) + return d2Gdvpa2_inf + end + + #using LinearAlgebra.mul discretization = "chebyshev_pseudospectral" #discretization = "finite_difference" @@ -831,16 +886,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("condition number: ", cond(LLx)) LLx_lu_obj = lu(sparse(LLx)) lu_solver = true#false - iterative_solver= false#true + #iterative_solver= false#true if lu_solver Fx = LLx_lu_obj\Sx - elseif iterative_solver - niter=1000 - @. Fx[:] = 1.0/(x.grid[:]^8 + 1.0) # initial guess Fx_exact[:] - Fx[1] =0.0; Fx[end] =0.0 - #gauss_seidel!(Fx,sparse(LLx),Sx,maxiter=niter) - #jacobi!(Fx,sparse(LLx),Sx,maxiter=niter) - idrs!(Fx,sparse(LLx),Sx;abstol=10^(-10)) + #elseif iterative_solver + # niter=1000 + # @. Fx[:] = 1.0/(x.grid[:]^8 + 1.0) # initial guess Fx_exact[:] + # Fx[1] =0.0; Fx[end] =0.0 + # #gauss_seidel!(Fx,sparse(LLx),Sx,maxiter=niter) + # #jacobi!(Fx,sparse(LLx),Sx,maxiter=niter) + # #idrs!(Fx,sparse(LLx),Sx;abstol=10^(-10)) else println("no solution method prescribed") end @@ -897,11 +952,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") x_ngrid = 5 - x_nelement_local = 16 - x_L = 24 - y_L = 12 + x_nelement_local = 8 + x_L = 12 + y_L = 6 y_ngrid = 5 - y_nelement_local = 8 + y_nelement_local = 4 x_nelement_global = x_nelement_local y_nelement_global = y_nelement_local @@ -910,10 +965,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ dirichlet_fixed_value = true#false # test option exponential_decay_test = false + dHdvpa_test = false + d2Gdvpa2_test = true input = grid_input("vpa", x_ngrid, x_nelement_global, x_nelement_local, nrank, irank, x_L, discretization, fd_option, "zero", adv_input, comm) x = define_coordinate(input) + x_spectral = setup_chebyshev_pseudospectral(x) Dx = Array{Float64,2}(undef, x.n, x.n) cheb_derivative_matrix_reversed!(Dx,x) @@ -977,21 +1035,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ end println("Initialised 1D arrays") ### now form 2D matrix to invert and corresponding sources - function dH_Maxwellian_dvpa(vpa,vperp,ivpa,ivperp) - # speed variable - eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) - zero = 1.0e-10 - if eta < zero - dHdvpa = -(4.0*vpa.grid[ivpa])/(3.0*sqrt(pi)) - else - dHdvpa = (vpa.grid[ivpa]/eta)*((2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2))) - end - return dHdvpa - end + # Array in 2D form nx = x.n ny = y.n Sxy = Array{Float64,2}(undef, nx, ny) + Sxy_check = Array{Float64,2}(undef, nx, ny) + Sxy_check_err = Array{Float64,2}(undef, nx, ny) + Txy = Array{Float64,2}(undef, nx, ny) Fxy = Array{Float64,2}(undef, nx, ny) Fxy_exact = Array{Float64,2}(undef, nx, ny) Fxy_err = Array{Float64,2}(undef, nx, ny) @@ -1032,8 +1083,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) end end - else - #@. LLxy = 0.0 + elseif dHdvpa_test for iy in 1:ny for ix in 1:nx # Rosenbluth dHdvpa test @@ -1041,23 +1091,52 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fxy_exact[ix,iy] = dH_Maxwellian_dvpa(x,y,ix,iy) end end + elseif d2Gdvpa2_test + for iy in 1:ny + for ix in 1:nx + # Rosenbluth d2Gdvpa2 test + Sxy[ix,iy] = 2.0*d2H_Maxwellian_dvpa2(x,y,ix,iy) + Fxy_exact[ix,iy] = d2G_Maxwellian_dvpa2(x,y,ix,iy) + Txy[ix,iy] = 2.0*dH_Maxwellian_dvpa(x,y,ix,iy) + end + @views derivative!(Sxy_check[:,iy],Txy[:,iy],x,x_spectral) + end + @. Sxy_check_err = abs(Sxy - Sxy_check) + println("maximum(Sxy_check_err)",maximum(Sxy_check_err)) + #@views heatmap(y.grid, x.grid, Sxy[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("Sxy_exact_2D_solve.pdf") + # savefig(outfile) + #@views heatmap(y.grid, x.grid, Sxy_check[:,:], xlabel=L"y", ylabel=L"x", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("Sxy_num_2D_solve.pdf") + # savefig(outfile) + else + println("No Sxy or Fxy_exact specified") end - function dHdvpa_inf(vpa,vperp,ivpa,ivperp) - eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) - dHdvpa_inf = -vpa.grid[ivpa]/eta^3 - return dHdvpa_inf - end + if dirichlet_fixed_value # set boundary values - for ix in 1:nx - Sxy[ix,ny] = dHdvpa_inf(x,y,ix,ny) - end - for iy in 1:ny - Sxy[1,iy] = dHdvpa_inf(x,y,1,iy) - Sxy[nx,iy] = dHdvpa_inf(x,y,nx,iy) + if dHdvpa_test + for ix in 1:nx + Sxy[ix,ny] = dHdvpa_inf(x,y,ix,ny) + end + for iy in 1:ny + Sxy[1,iy] = dHdvpa_inf(x,y,1,iy) + Sxy[nx,iy] = dHdvpa_inf(x,y,nx,iy) + end + elseif d2Gdvpa2_test + for ix in 1:nx + Sxy[ix,ny] = d2Gdvpa2_inf(x,y,ix,ny) + end + for iy in 1:ny + Sxy[1,iy] = d2Gdvpa2_inf(x,y,1,iy) + Sxy[nx,iy] = d2Gdvpa2_inf(x,y,nx,iy) + end end - #println(Sxy[:,ny]) println("Check boundary specification") + #println(Sxy[:,ny]) + #println(Fxy_exact[:,ny]) println(abs.(Sxy[:,ny] .- Fxy_exact[:,ny])) println(abs.(Sxy[1,:] .- Fxy_exact[1,:])) println(abs.(Sxy[nx,:] .- Fxy_exact[nx,:])) From 3896934729e8e8ae595063e31d41187beda9a79f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 5 May 2023 07:08:37 +0000 Subject: [PATCH 013/331] Addition of a test where we solve Poisson`s equation for a source with no 0th moment . Failed attempt to get working the elementwise second derivative matrices in the 2D Poisson`s solver. Minor cleaning of code. --- cheb_matrix_test.jl | 113 +++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/cheb_matrix_test.jl b/cheb_matrix_test.jl index d71295ca3..9c6324ecd 100644 --- a/cheb_matrix_test.jl +++ b/cheb_matrix_test.jl @@ -137,13 +137,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ D2_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) mul!(D2_elementwise,D_elementwise,D_elementwise) if x.ngrid < 8 - println("\n D2_elementwise \n") - for i in 1:x.ngrid - for j in 1:x.ngrid - @printf("%.1f ", D2_elementwise[i,j]) - end - println("") - end + print_matrix(D2_elementwise,"D2_elementwise",x.ngrid,x.ngrid) end assign_cheb_derivative_matrix!(D,D2_elementwise,x) end @@ -310,6 +304,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ end assign_cheb_derivative_matrix!(D,D_lobotto_elementwise,D_radau_elementwise,x) end + + function cheb_radau_second_derivative_matrix_reversed!(D::Array{Float64,2},x,x_spectral) + D_lobotto_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + cheb_derivative_matrix_elementwise_reversed!(D_lobotto_elementwise,x.ngrid,x.L,x.nelement_global) + D2_lobotto_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + mul!(D2_lobotto_elementwise,D_lobotto_elementwise,D_lobotto_elementwise) + + D_radau_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + calculate_chebyshev_radau_D_matrix_via_FFT!(D_radau_elementwise,x,x_spectral) + D2_radau_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + mul!(D2_radau_elementwise,D_radau_elementwise,D_radau_elementwise) + + if x.ngrid < 8 + #print_matrix(D_lobotto_elementwise,"D_lobotto_elementwise",x.ngrid,x.ngrid) + print_matrix(D2_lobotto_elementwise,"D2_lobotto_elementwise",x.ngrid,x.ngrid) + #print_matrix(D_radau_elementwise,"D_radau_elementwise",x.ngrid,x.ngrid) + print_matrix(D2_radau_elementwise,"D2_radau_elementwise",x.ngrid,x.ngrid) + end + assign_cheb_derivative_matrix!(D,D2_lobotto_elementwise,D2_radau_elementwise,x) + end function assign_cheb_derivative_matrix!(D::Array{Float64,2},D_lobotto_elementwise::Array{Float64,2},D_radau_elementwise::Array{Float64,2},x) @@ -453,8 +467,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ################### # define inputs needed for the test - ngrid = 2 #number of points per element - nelement_local = 10 # number of elements per rank + ngrid = 17 #number of points per element + nelement_local = 20 # number of elements per rank nelement_global = nelement_local # total number of elements L = 1.0 #physical box size in reference units bc = "" #not required to take a particular value, not used @@ -525,28 +539,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ Dxreverse2[end,end] = 2.0*Dxreverse2[end,end] #println("x.grid \n",x.grid) if x.n < 20 - println("\n Dxreverse \n") - for i in 1:x.n - for j in 1:x.n - @printf("%.1f ", Dxreverse[i,j]) - end - println("") - end - println("\n Dxreverse*Dxreverse \n") - for i in 1:x.n - for j in 1:x.n - @printf("%.1f ", Dxreverse2[i,j]) - end - println("") - end - - println("\n D2xreverse \n") - for i in 1:x.n - for j in 1:x.n - @printf("%.1f ", D2xreverse[i,j]) - end - println("") - end + print_matrix(Dxreverse,"\n Dxreverse \n",x.n,x.n) + print_matrix(Dxreverse2,"\n Dxreverse*Dxreverse \n",x.n,x.n) + print_matrix(D2xreverse,"\n D2xreverse \n",x.n,x.n) println("\n") end @@ -951,22 +946,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ if elliptic_2Dsolve_test println("elliptic 2D solve test") - x_ngrid = 5 - x_nelement_local = 8 + x_ngrid = 17 + x_nelement_local = 4 x_L = 12 y_L = 6 - y_ngrid = 5 - y_nelement_local = 4 + y_ngrid = 17 + y_nelement_local = 2 x_nelement_global = x_nelement_local y_nelement_global = y_nelement_local # bc option - dirichlet_zero = false#true - dirichlet_fixed_value = true#false + dirichlet_zero = true#false# + dirichlet_fixed_value = false#true# # test option - exponential_decay_test = false + secular_decay_test = false#true + exponential_decay_test = true#false dHdvpa_test = false - d2Gdvpa2_test = true + d2Gdvpa2_test = false#true + # second derivative option + # default = false -> if true then use D2coord matrices based on D_elementwise^2 + second_derivative_elementwise = false#true input = grid_input("vpa", x_ngrid, x_nelement_global, x_nelement_local, nrank, irank, x_L, discretization, fd_option, "zero", adv_input, comm) @@ -976,8 +975,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ Dx = Array{Float64,2}(undef, x.n, x.n) cheb_derivative_matrix_reversed!(Dx,x) D2x = Array{Float64,2}(undef, x.n, x.n) - mul!(D2x,Dx,Dx) - + if second_derivative_elementwise + cheb_second_derivative_matrix_reversed!(D2x,x) + else + mul!(D2x,Dx,Dx) + end # set x bc on D2x if dirichlet_zero D2x[1,1] = 2.0*D2x[1,1] @@ -1009,7 +1011,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ cheb_radau_derivative_matrix_reversed!(Dy,y,y_spectral) D2y = Array{Float64,2}(undef, y.n, y.n) - mul!(D2y,Dy,Dy) + if second_derivative_elementwise + cheb_radau_second_derivative_matrix_reversed!(D2y,y,y_spectral) + else + mul!(D2y,Dy,Dy) + end if dirichlet_zero D2y[end,end] = 2.0*D2y[end,end] end @@ -1083,6 +1089,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ Fxy_exact[ix,iy] = exp(-x.grid[ix]^2 - y.grid[iy]^2) end end + elseif secular_decay_test + for iy in 1:ny + for ix in 1:nx + # secular test inputs below + eta2 = x.grid[ix]^2 + y.grid[iy]^2 + zero = 1.0e-10 + if eta2 < zero + Sxy[ix,iy] = 0.0 + Fxy_exact[ix,iy] = 0.5 + else + Sxy[ix,iy] = exp(-1.0/eta2)*(1.0/eta2^2 - 2.0/eta2^3) + Fxy_exact[ix,iy] = 0.5 - 0.5*exp(-1.0/eta2) + end + end + end elseif dHdvpa_test for iy in 1:ny for ix in 1:nx @@ -1201,7 +1222,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ iy = iyfunc(ic,nx) Fxy[ix,iy] = Fc[ic] end - + #if dHdvpa_test && dirichlet_zero + # for iy in 1:ny + # for ix in 1:nx + # Fxy[ix,iy] += dHdvpa_inf(x,y,ix,iy) + # end + # end + #end println("Finished 2D solve") @. Fxy_err = abs(Fxy - Fxy_exact) From cd07a268009762f7b2d8bbb656fe8db7b8bba91e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 12 May 2023 11:24:07 +0100 Subject: [PATCH 014/331] Attempt to refactor velocity integrals so that these routines can be tested. Two factors of 2 errors are discovered in the definition of vth and ppar, which are now rectified. The moments upar and ppar are now computed correctly within the update_*! functions. Previously, upar and ppar were actually the 1st and 2nd vpa moments. The automatic tests fail on the nonlinear sound wave tests -- this may be because the results there were incorrect. Further work is required to benchmark the code to make sure the moments are calculated correctly. --- src/initial_conditions.jl | 12 ++-- src/time_advance.jl | 16 +++--- src/velocity_moments.jl | 58 ++++++++++++++----- test_velocity_integrals.jl | 112 +++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 30 deletions(-) create mode 100644 test_velocity_integrals.jl diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 060e3120d..731d0b32d 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -206,16 +206,14 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, end # update upar, ppar, qpar, vth consistent with manufactured solns update_density!(moments.charged.dens, pdf.charged.unnorm, vpa, vperp, z, r, composition) - update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) - update_ppar!(moments.charged.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition) - # get particle flux - update_upar!(moments.charged.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition) - # convert from particle particle flux to parallel flow + update_upar!(moments.charged.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.dens) + update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) + update_ppar!(moments.charged.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar) begin_s_r_z_region() @loop_s_r_z is ir iz begin - moments.charged.upar[iz,ir,is] /= moments.charged.dens[iz,ir,is] + #moments.charged.upar[iz,ir,is] /= moments.charged.dens[iz,ir,is] # update the thermal speed - moments.charged.vth[iz,ir,is] = sqrt(2.0*moments.charged.ppar[iz,ir,is]/moments.charged.dens[iz,ir,is]) + moments.charged.vth[iz,ir,is] = sqrt(moments.charged.ppar[iz,ir,is]/moments.charged.dens[iz,ir,is]) end if n_neutral_species > 0 diff --git a/src/time_advance.jl b/src/time_advance.jl index 4a7cfa901..3f93a370f 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -765,19 +765,19 @@ function rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, end update_density!(new_scratch.density, pdf.charged.unnorm, vpa, vperp, z, r, composition) - update_upar!(new_scratch.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition) - # convert from particle particle flux to parallel flow - begin_s_r_z_region() - @loop_s_r_z is ir iz begin - new_scratch.upar[iz,ir,is] /= new_scratch.density[iz,ir,is] - end + update_upar!(new_scratch.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition, new_scratch.density) + # convert from particle particle flux to parallel flow -> MRH now done inside upar function + #begin_s_r_z_region() + #@loop_s_r_z is ir iz begin + # new_scratch.upar[iz,ir,is] /= new_scratch.density[iz,ir,is] + #end - update_ppar!(new_scratch.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition) + update_ppar!(new_scratch.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, new_scratch.upar) # update the thermal speed begin_s_r_z_region() try #below block causes DomainError if ppar < 0 or density, so exit cleanly if possible @loop_s_r_z is ir iz begin - moments.charged.vth[iz,ir,is] = sqrt(2.0*new_scratch.ppar[iz,ir,is]/new_scratch.density[iz,ir,is]) + moments.charged.vth[iz,ir,is] = sqrt(new_scratch.ppar[iz,ir,is]/new_scratch.density[iz,ir,is]) end catch e if global_size[] > 1 diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 71110c015..7ced2622c 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -23,6 +23,12 @@ export update_neutral_pr! export update_neutral_pzeta! export update_neutral_qz! +# for testing +export get_density +export get_upar +export get_ppar +export get_pperp + using ..type_definitions: mk_float using ..array_allocation: allocate_shared_float, allocate_bool using ..calculus: integral @@ -127,14 +133,14 @@ function update_moments!(moments, ff, vpa, nz, nr, composition) moments.dens_updated[is] = true end if moments.upar_updated[is] == false - @views update_upar_species!(moments.upar[:,:,is], ff[:,:,:,is], vpa, z, r) + @views update_upar_species!(moments.upar[:,:,is], ff[:,:,:,is], vpa, z, r, moments.dens[:,:,is]) moments.upar_updated[is] = true end if moments.ppar_updated[is] == false - @views update_ppar_species!(moments.ppar[:,:,is], ff[:,:,:,is], vpa, z, r) + @views update_ppar_species!(moments.ppar[:,:,is], ff[:,:,:,is], vpa, z, r, moments.upar[:,:,is]) moments.ppar_updated[is] = true end - @. moments.vth = sqrt(2*moments.ppar/moments.dens) + @. moments.vth = sqrt(moments.ppar/moments.dens) if moments.qpar_updated[is] == false @views update_qpar_species!(moments.qpar[:,is], ff[:,:,is], vpa, z, r, moments.vpa_norm_fac[:,:,is]) moments.qpar_updated[is] = true @@ -169,31 +175,36 @@ function update_density_species!(dens, ff, vpa, vperp, z, r) @boundscheck z.n == size(dens, 1) || throw(BoundsError(dens)) @boundscheck r.n == size(dens, 2) || throw(BoundsError(dens)) @loop_r_z ir iz begin - dens[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), - vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + #dens[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), + # vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + dens[iz,ir] = get_density(@view(ff[:,:,iz,ir]), vpa, vperp) end return nothing end +function get_density(ff, vpa, vperp) + return integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) +end + """ NB: if this function is called and if upar_updated is false, then the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density """ -function update_upar!(upar, pdf, vpa, vperp, z, r, composition) +function update_upar!(upar, pdf, vpa, vperp, z, r, composition, density) begin_s_r_z_region() n_species = size(pdf,5) @boundscheck n_species == size(upar,3) || throw(BoundsError(upar)) @loop_s is begin - @views update_upar_species!(upar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r) + @views update_upar_species!(upar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r, density[:,:,is]) end end """ calculate the updated parallel flow (upar) for a given species """ -function update_upar_species!(upar, ff, vpa, vperp, z, r) +function update_upar_species!(upar, ff, vpa, vperp, z, r, density) @boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff)) @boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff)) @boundscheck z.n == size(ff, 3) || throw(BoundsError(ff)) @@ -201,15 +212,22 @@ function update_upar_species!(upar, ff, vpa, vperp, z, r) @boundscheck z.n == size(upar, 1) || throw(BoundsError(upar)) @boundscheck r.n == size(upar, 2) || throw(BoundsError(upar)) @loop_r_z ir iz begin - upar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), - vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts) + #upar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), + # vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts) + upar[iz,ir] = get_upar(@view(ff[:,:,iz,ir]), vpa, vperp, density[iz,ir]) end return nothing end +function get_upar(ff, vpa, vperp, density) + upar = integrate_over_vspace(@view(ff[:,:]), vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts) + upar /= density + return upar +end + """ """ -function update_ppar!(ppar, pdf, vpa, vperp, z, r, composition) +function update_ppar!(ppar, pdf, vpa, vperp, z, r, composition, upar) @boundscheck composition.n_ion_species == size(ppar,3) || throw(BoundsError(ppar)) @boundscheck r.n == size(ppar,2) || throw(BoundsError(ppar)) @boundscheck z.n == size(ppar,1) || throw(BoundsError(ppar)) @@ -217,14 +235,14 @@ function update_ppar!(ppar, pdf, vpa, vperp, z, r, composition) begin_s_r_z_region() @loop_s is begin - @views update_ppar_species!(ppar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r) + @views update_ppar_species!(ppar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r, upar[:,:,is]) end end """ calculate the updated parallel pressure (ppar) for a given species """ -function update_ppar_species!(ppar, ff, vpa, vperp, z, r) +function update_ppar_species!(ppar, ff, vpa, vperp, z, r, upar) @boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff)) @boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff)) @boundscheck z.n == size(ff, 3) || throw(BoundsError(ff)) @@ -232,12 +250,22 @@ function update_ppar_species!(ppar, ff, vpa, vperp, z, r) @boundscheck z.n == size(ppar, 1) || throw(BoundsError(ppar)) @boundscheck r.n == size(ppar, 2) || throw(BoundsError(ppar)) @loop_r_z ir iz begin - ppar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), - vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) + #ppar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), + # vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) + ppar[iz,ir] = get_ppar(@view(ff[:,:,iz,ir]), vpa, vperp, upar[iz,ir]) end return nothing end +function get_ppar(ff, vpa, vperp, upar) + @. vpa.scratch = vpa.grid - upar + return 2.0*integrate_over_vspace(@view(ff[:,:]), vpa.scratch, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) +end + +function get_pperp(ff, vpa, vperp) + return integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 2, vperp.wgts) +end + """ NB: if this function is called and if ppar_updated is false, then the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density diff --git a/test_velocity_integrals.jl b/test_velocity_integrals.jl new file mode 100644 index 000000000..08b282662 --- /dev/null +++ b/test_velocity_integrals.jl @@ -0,0 +1,112 @@ +using Printf +using Plots +using LaTeXStrings +using MPI +using Measures + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + import moment_kinetics + using moment_kinetics.input_structs: grid_input, advection_input + using moment_kinetics.coordinates: define_coordinate + using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp + using moment_kinetics.array_allocation: allocate_float + + # define inputs needed for the test + ngrid = 17 #number of points per element + nelement_local = 20 # number of elements per rank + nelement_global = nelement_local # total number of elements + Lvpa = 12.0 #physical box size in reference units + Lvperp = 6.0 #physical box size in reference units + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + discretization = "chebyshev_pseudospectral" + fd_option = "fourth_order_centered" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", ngrid, nelement_global, nelement_local, + nrank, irank, Lvpa, discretization, fd_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", ngrid, nelement_global, nelement_local, + nrank, irank, Lvperp, discretization, fd_option, bc, adv_input,comm) + # create the coordinate struct 'x' + println("made inputs") + println("vpa: ngrid: ",ngrid," nelement: ",nelement_local) + println("vperp: ngrid: ",ngrid," nelement: ",nelement_local) + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + + dfn = allocate_float(vpa.n,vperp.n) + + function pressure(ppar,pperp) + pres = (1.0/3.0)*(ppar + 2.0*pperp) + return pres + end + # assign a known isotropic Maxwellian distribution in normalised units + dens = 3.0/4.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + pperp = 2.0/3.0 + pres = pressure(ppar,pperp) + mass = 1.0 + vth = sqrt(pres/(dens*mass)) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + vpa_val = vpa.grid[ivpa] + vperp_val = vperp.grid[ivperp] + dfn[ivpa,ivperp] = (dens/vth^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vth^2 ) + end + end + + # now check that we can extract the correct moments from the distribution + + dens_test = get_density(dfn,vpa,vperp) + upar_test = get_upar(dfn,vpa,vperp,dens_test) + ppar_test = get_ppar(dfn,vpa,vperp,upar_test) + pperp_test = get_pperp(dfn,vpa,vperp) + pres_test = pressure(ppar_test,pperp_test) + # output test results + println("") + println("Isotropic Maxwellian") + println("dens_test: ", dens_test, " dens: ", dens, " error: ", abs(dens_test-dens)) + println("upar_test: ", upar_test, " upar: ", upar, " error: ", abs(upar_test-upar)) + println("pres_test: ", pres_test, " pres: ", pres, " error: ", abs(pres_test-pres)) + println("") + + # assign a known biMaxwellian distribution in normalised units + dens = 3.0/4.0 + upar = 2.0/3.0 + ppar = 4.0/5.0 + pperp = 1.0/4.0 + mass = 1.0 + vthpar = sqrt(ppar/(dens*mass)) + vthperp = sqrt(pperp/(dens*mass)) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + vpa_val = vpa.grid[ivpa] + vperp_val = vperp.grid[ivperp] + dfn[ivpa,ivperp] = (dens/(vthpar*vthperp^2))*exp( - ((vpa_val-upar)^2)/vthpar^2 - (vperp_val^2)/vthperp^2 ) + end + end + + # now check that we can extract the correct moments from the distribution + + dens_test = get_density(dfn,vpa,vperp) + upar_test = get_upar(dfn,vpa,vperp,dens_test) + ppar_test = get_ppar(dfn,vpa,vperp,upar_test) + pperp_test = get_pperp(dfn,vpa,vperp) + # output test results + + println("") + println("biMaxwellian") + println("dens_test: ", dens_test, " dens: ", dens, " error: ", abs(dens_test-dens)) + println("upar_test: ", upar_test, " upar: ", upar, " error: ", abs(upar_test-upar)) + println("ppar_test: ", ppar_test, " ppar: ", ppar, " error: ", abs(ppar_test-ppar)) + println("pperp_test: ", pperp_test, " pperp: ", pperp, " error: ", abs(pperp_test-pperp)) + println("") +end \ No newline at end of file From 4dd189e7cd28a2a41c398fd2cba3da5545df1939 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 16 May 2023 11:02:25 +0100 Subject: [PATCH 015/331] nth derivative test script including matrix differentiation for comparison to base code FFT differentiation. Only testing the Lobotto grid (as Radau differentiation matrix is constructed by FFT using a response matrix). --- nth_derivatives_test.jl | 364 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 nth_derivatives_test.jl diff --git a/nth_derivatives_test.jl b/nth_derivatives_test.jl new file mode 100644 index 000000000..2cfaec441 --- /dev/null +++ b/nth_derivatives_test.jl @@ -0,0 +1,364 @@ +using MPI +using Printf +using Plots +using LinearAlgebra: mul! + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + import moment_kinetics + using moment_kinetics.input_structs: grid_input, advection_input + using moment_kinetics.coordinates: define_coordinate + using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral + using moment_kinetics.calculus: derivative!, integral + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.1f ", matrix[i,j]) + end + println("") + end + println("\n") + end + + function Djk(x::Array{Float64,1},j::Int64,k::Int64,c_j::Float64,c_k::Float64) + return (c_j/c_k)*((-1)^(k+j))/(x[j] - x[k]) + end + + function cheb_derivative_matrix_reversed!(D::Array{Float64,2},x) + D_elementwise = Array{Float64,2}(undef,x.ngrid,x.ngrid) + cheb_derivative_matrix_elementwise_reversed!(D_elementwise,x.ngrid,x.L,x.nelement_global) + if x.ngrid < 8 + print_matrix(D_elementwise,"D_elementwise",x.ngrid,x.ngrid) + end + assign_cheb_derivative_matrix!(D,D_elementwise,x) + end + + function assign_cheb_derivative_matrix!(D::Array{Float64,2},D_elementwise::Array{Float64,2},x) + + # zero output matrix before assignment + D[:,:] .= 0.0 + imin = x.imin + imax = x.imax + + zero_bc_upper_boundary = x.bc == "zero" || x.bc == "zero_upper" + zero_bc_lower_boundary = x.bc == "zero" || x.bc == "zero_lower" + + # fill in first element + j = 1 + if zero_bc_lower_boundary #x.bc == "zero" + D[imin[j],imin[j]:imax[j]] .+= D_elementwise[1,:]./2.0 #contributions from this element/2 + D[imin[j],imin[j]] += D_elementwise[x.ngrid,x.ngrid]/2.0 #contribution from missing `zero' element/2 + else + D[imin[j],imin[j]:imax[j]] .+= D_elementwise[1,:] + end + for k in 2:imax[j]-imin[j] + D[k,imin[j]:imax[j]] .+= D_elementwise[k,:] + end + if zero_bc_upper_boundary && x.nelement_local == 1 + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 + elseif x.nelement_local > 1 #x.bc == "zero" + D[imax[j],imin[j]:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 + else + D[imax[j],imin[j]:imax[j]] .+= D_elementwise[x.ngrid,:] + end + # remaining elements recalling definitions of imax and imin + for j in 2:x.nelement_local + #lower boundary condition on element + D[imin[j]-1,imin[j]-1:imax[j]] .+= D_elementwise[1,:]./2.0 + for k in 2:imax[j]-imin[j]+1 + D[k+imin[j]-2,imin[j]-1:imax[j]] .+= D_elementwise[k,:] + end + # upper boundary condition on element + if j == x.nelement_local && !(zero_bc_upper_boundary) + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:] + elseif j == x.nelement_local && zero_bc_upper_boundary + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 #contributions from this element/2 + D[imax[j],imax[j]] += D_elementwise[1,1]/2.0 #contribution from missing `zero' element/2 + else + D[imax[j],imin[j]-1:imax[j]] .+= D_elementwise[x.ngrid,:]./2.0 + end + end + + end + + function cheb_derivative_matrix_elementwise_reversed!(D::Array{Float64,2},n::Int64,L::Float64,nelement::Int64) + + #define Chebyshev points in reversed order x_j = { -1, ... , 1} + x = Array{Float64,1}(undef,n) + for j in 1:n + x[j] = cospi((n-j)/(n-1)) + end + + # zero matrix before allocating values + D[:,:] .= 0.0 + + # top row + j = 1 + c_j = 2.0 + c_k = 1.0 + for k in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + k = n + c_k = 2.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + + # bottom row + j = n + c_j = 2.0 + c_k = 1.0 + for k in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + k = 1 + c_k = 2.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + + #left column + k = 1 + c_j = 1.0 + c_k = 2.0 + for j in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + + #right column + k = n + c_j = 1.0 + c_k = 2.0 + for j in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + + + # top left, bottom right + #D[n,n] = (2.0*(n - 1.0)^2 + 1.0)/6.0 + #D[1,1] = -(2.0*(n - 1.0)^2 + 1.0)/6.0 + # interior rows and columns + for j in 2:n-1 + #D[j,j] = Djj(x,j) + for k in 2:n-1 + if j == k + continue + end + c_k = 1.0 + c_j = 1.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + end + + # calculate diagonal entries to guarantee that + # D * (1, 1, ..., 1, 1) = (0, 0, ..., 0, 0) + for j in 1:n + D[j,j] = -sum(D[j,:]) + end + + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + end + testopt_sine = "sinewavesegment" + testopt_poly = "xpower5" + testopt = testopt_poly + #testopt = testopt_sine + if testopt == testopt_poly + L = 1.0 + elseif testopt == testopt_sine + L = 2.0*pi/100.0 #physical box size in reference units + end + + discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + etol = 1.0e-15 + outprefix = "derivative_test" + ################### + ## df/dx Nonperiodic (No) BC test + ################### + + # define inputs needed for the test + ngrid = 33 #number of points per element + nelement_local = 1 # number of elements per rank + nelement_global = nelement_local # total number of elements + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + input = grid_input("coord", ngrid, nelement_global, nelement_local, + nrank, irank, L, discretization, fd_option, bc, adv_input,comm) + # create the coordinate struct 'x' + println("testopt: ", testopt) + println("made inputs: ngrid: ",ngrid, " nelement: ", nelement_local, " L: ",L) + x = define_coordinate(input) + println("made x") + # create arrays needed for Chebyshev pseudospectral treatment in x + # and create the plans for the forward and backward fast Chebyshev + # transforms + if discretization == "chebyshev_pseudospectral" + spectral = setup_chebyshev_pseudospectral(x) + else + spectral = false + end + println("made spectral") + # create array for the function f(x) to be differentiated/integrated + f = Array{Float64,1}(undef, x.n) + # create array for the derivative df/dx + df = Array{Float64,1}(undef, x.n) + df_exact = Array{Float64,1}(undef, x.n) + df_err = Array{Float64,1}(undef, x.n) + d2f = Array{Float64,1}(undef, x.n) + d2f_exact = Array{Float64,1}(undef, x.n) + d2f_err = Array{Float64,1}(undef, x.n) + d3f = Array{Float64,1}(undef, x.n) + d3f_exact = Array{Float64,1}(undef, x.n) + d3f_err = Array{Float64,1}(undef, x.n) + d4f = Array{Float64,1}(undef, x.n) + d4f_exact = Array{Float64,1}(undef, x.n) + d4f_err = Array{Float64,1}(undef, x.n) + + if testopt == testopt_poly + p = 5.0 + for ix in 1:x.n + f[ix] = x.grid[ix]^p + df_exact[ix] = p*x.grid[ix]^(p-1.0) + d2f_exact[ix] = p*(p-1.0)*x.grid[ix]^(p-2.0) + d3f_exact[ix] = p*(p-1.0)*(p-2.0)*x.grid[ix]^(p-3.0) + d4f_exact[ix] = p*(p-1.0)*(p-2.0)*(p-3.0)*x.grid[ix]^(p-4.0) + end + + elseif testopt == testopt_sine + ## sin(2pix/L) test + for ix in 1:x.n + scale = 1.0 + arg = x.grid[ix]*scale + f[ix] = sin(arg) + df_exact[ix] = scale*cos(arg) + d2f_exact[ix] = -scale*scale*sin(arg) + d3f_exact[ix] = -scale*scale*scale*cos(arg) + d4f_exact[ix] = scale*scale*scale*scale*sin(arg) + end + end + # differentiate f + derivative!(df, f, x, spectral) + derivative!(d2f, df, x, spectral) + derivative!(d3f, d2f, x, spectral) + derivative!(d4f, d3f, x, spectral) + + @. df_err = abs(df - df_exact) + @. d2f_err = abs(d2f - d2f_exact) + @. d3f_err = abs(d3f - d3f_exact) + @. d4f_err = abs(d4f - d4f_exact) + println("FFT differentiation") + println("max(df_err)",maximum(df_err)) + println("max(d2f_err)",maximum(d2f_err)) + println("max(d3f_err)",maximum(d3f_err)) + println("max(d4f_err)",maximum(d4f_err)) + + plot_output = true + if plot_output + # plot df and f + plot([x.grid,x.grid,x.grid], [df,df_exact,df_err], xlabel="x", ylabel="", label=["df_num" "df_exact" "df_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1st_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [df_err], xlabel="x", ylabel="", label=["df_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1st_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d2f,d2f_exact,d2f_err], xlabel="x", ylabel="", label=["d2f_num" "d2f_exact" "d2f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "2nd_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d2f_err], xlabel="x", ylabel="", label=["d2f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "2nd_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d3f,d3f_exact,d3f_err], xlabel="x", ylabel="", label=["d3f_num" "d3f_exact" "d3f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "3rd_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d3f_err], xlabel="x", ylabel="", label=["d3f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "3rd_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d4f,d4f_exact,d4f_err], xlabel="x", ylabel="", label=["d4f_num" "d4f_exact" "d4f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "4th_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d4f_err], xlabel="x", ylabel="", label=["d4f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "4th_derivative_err.pdf" + savefig(outfile) + end + + # derivative matrix approach + Dx = Array{Float64,2}(undef, x.n, x.n) + cheb_derivative_matrix_reversed!(Dx,x) + # differentiate f + mul!(df,Dx,f) + mul!(d2f,Dx,df) + mul!(d3f,Dx,d2f) + mul!(d4f,Dx,d3f) + @. df_err = abs(df - df_exact) + @. d2f_err = abs(d2f - d2f_exact) + @. d3f_err = abs(d3f - d3f_exact) + @. d4f_err = abs(d4f - d4f_exact) + println("matrix differentiation") + println("max(df_err)",maximum(df_err)) + println("max(d2f_err)",maximum(d2f_err)) + println("max(d3f_err)",maximum(d3f_err)) + println("max(d4f_err)",maximum(d4f_err)) + + plot_output = true + if plot_output + # plot df and f + plot([x.grid,x.grid,x.grid], [df,df_exact,df_err], xlabel="x", ylabel="", label=["df_num" "df_exact" "df_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1st_matrix_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [df_err], xlabel="x", ylabel="", label=["df_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "1st_matrix_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d2f,d2f_exact,d2f_err], xlabel="x", ylabel="", label=["d2f_num" "d2f_exact" "d2f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "2nd_matrix_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d2f_err], xlabel="x", ylabel="", label=["d2f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "2nd_matrix_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d3f,d3f_exact,d3f_err], xlabel="x", ylabel="", label=["d3f_num" "d3f_exact" "d3f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "3rd_matrix_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d3f_err], xlabel="x", ylabel="", label=["d3f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "3rd_matrix_derivative_err.pdf" + savefig(outfile) + + plot([x.grid,x.grid,x.grid], [d4f,d4f_exact,d4f_err], xlabel="x", ylabel="", label=["d4f_num" "d4f_exact" "d4f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "4th_matrix_derivative_test.pdf" + savefig(outfile) + plot([x.grid], [d4f_err], xlabel="x", ylabel="", label=["d4f_err"], + shape =:circle, markersize = 5, linewidth=2) + outfile = "4th_matrix_derivative_err.pdf" + savefig(outfile) + end + +end + From 3b2630d0deba7fc22a2eadb48bd67357776ccbec Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 17 May 2023 11:35:11 +0100 Subject: [PATCH 016/331] fix imposition of vperp BC in initialisation --- src/time_advance.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time_advance.jl b/src/time_advance.jl index 3f93a370f..35314097d 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -397,7 +397,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # enforce prescribed boundary condition in vperp on the distribution function f if vperp.n > 1 begin_s_r_z_vpa_region() - @views enforce_vperp_boundary_condition!(f,vperp) + @views enforce_vperp_boundary_condition!(pdf.charged.norm[:,:,:,:,:],vperp) end ## From b0b3f56e12b27874e4581fda45d59302527640c3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 17 May 2023 11:42:45 +0100 Subject: [PATCH 017/331] Added MMS tests for ppar and upar for the z_bc = wall case. These tests pass for nvperp = 1. Further work is required to test qpar, vth, and pperp, as these variables may have differing values between nvperp = 1 and nvperp > 1 cases. --- src/manufactured_solns.jl | 61 ++++++++++++++++++++++++++++++++++++--- src/post_processing.jl | 13 +++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index 68f05308a..b613a50fe 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -24,10 +24,12 @@ using IfElse if dfni_vpa_power_opt == "2" pvpa = 2 nconst = 0.25 + pconst = 3.0/4.0 fluxconst = 0.5 elseif dfni_vpa_power_opt == "4" pvpa = 4 - nconst = (3.0/8.0) + nconst = 3.0/8.0 + pconst = 15.0/8.0 fluxconst = 1.0 end @@ -146,11 +148,53 @@ using IfElse elseif z_bc == "wall" epsilon = composition.epsilon_offset alpha = composition.alpha_switch - densi = nconst*(0.5 - z/Lz)*nminus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + nconst*(z/Lz + 0.5)*nplus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + (z/Lz + 0.5)*(0.5 - z/Lz)*nzero_sym(Lr,Lz,r_bc,z_bc,alpha) #+ 0.5*(r/Lr + 0.5) + 0.5*(z/Lz + 0.5) + densi = nconst*(0.5 - z/Lz)*nminus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + nconst*(z/Lz + 0.5)*nplus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + (z/Lz + 0.5)*(0.5 - z/Lz)*nzero_sym(Lr,Lz,r_bc,z_bc,alpha) #+ 0.5*(r/Lr + 0.5) + 0.5*(z/Lz + 0.5) end return densi end - + + # ion mean parallel flow symbolic function + function upari_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr) + if z_bc == "periodic" + upari = 0.0 #not supported + elseif z_bc == "wall" + densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) + Er, Ez, phi = electric_fields(Lr,Lz,r_bc,z_bc,composition,nr) + rhostar = geometry.rhostar + bzed = geometry.bzed + epsilon = composition.epsilon_offset + alpha = composition.alpha_switch + upari = ( (fluxconst/(sqrt(pi)*densi))*((z/Lz + 0.5)*nplus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + - (0.5 - z/Lz)*nminus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha)) + + alpha*(rhostar/(2.0*bzed))*Er ) + end + return upari + end + + # ion parallel pressure symbolic function + function ppari_sym(Lr,Lz,r_bc,z_bc,composition) + if z_bc == "periodic" + ppari = 0.0 # not supported + elseif z_bc == "wall" + densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) + epsilon = composition.epsilon_offset + alpha = composition.alpha_switch + ppari = ( pconst*((0.5 - z/Lz)*nminus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + + (z/Lz + 0.5)*nplus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha)) + + (z/Lz + 0.5)*(0.5 - z/Lz)*nzero_sym(Lr,Lz,r_bc,z_bc,alpha) + - (2.0/(pi*densi))*((z/Lz + 0.5)*nplus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha) + - (0.5 - z/Lz)*nminus_sym(Lr,Lz,r_bc,z_bc,epsilon,alpha))^2 ) + end + return ppari + end + + # ion perpendicular pressure symbolic function + function pperpi_sym(Lr,Lz,r_bc,z_bc,composition) + densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) + pperpi = densi # simple vperp^2 dependence of dfni + return pperpi + end + function jpari_into_LHS_wall_sym(Lr,Lz,r_bc,z_bc,composition) if z_bc == "periodic" jpari_into_LHS_wall_sym = 0.0 @@ -238,6 +282,9 @@ using IfElse function manufactured_solutions(Lr,Lz,r_bc,z_bc,geometry,composition,nr) densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) + upari = upari_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr) + ppari = ppari_sym(Lr,Lz,r_bc,z_bc,composition) + pperpi = pperpi_sym(Lr,Lz,r_bc,z_bc,composition) dfni = dfni_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr) densn = densn_sym(Lr,Lz,r_bc,z_bc,geometry,composition) @@ -246,6 +293,9 @@ using IfElse #build julia functions from these symbolic expressions # cf. https://docs.juliahub.com/Symbolics/eABRO/3.4.0/tutorials/symbolic_functions/ densi_func = build_function(densi, z, r, t, expression=Val{false}) + upari_func = build_function(upari, z, r, t, expression=Val{false}) + ppari_func = build_function(ppari, z, r, t, expression=Val{false}) + pperpi_func = build_function(pperpi, z, r, t, expression=Val{false}) densn_func = build_function(densn, z, r, t, expression=Val{false}) dfni_func = build_function(dfni, vpa, vperp, z, r, t, expression=Val{false}) dfnn_func = build_function(dfnn, vz, vr, vzeta, z, r, t, expression=Val{false}) @@ -256,7 +306,10 @@ using IfElse # densn_func(zval, rval, tval) # dfnn_func(vzval, vrval, vzetapval, zval, rval, tval) - manufactured_solns_list = (densi_func = densi_func, densn_func = densn_func, dfni_func = dfni_func, dfnn_func = dfnn_func) + manufactured_solns_list = (densi_func = densi_func, densn_func = densn_func, + dfni_func = dfni_func, dfnn_func = dfnn_func, + upari_func = upari_func, ppari_func = ppari_func, + pperpi_func = pperpi_func) return manufactured_solns_list end diff --git a/src/post_processing.jl b/src/post_processing.jl index d9a0d52bc..c42fba813 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -476,6 +476,9 @@ function analyze_and_plot_data(path) manufactured_solns_list = manufactured_solutions(Lr_in,Lz,r_bc,z_bc,geometry,composition,nr_local) dfni_func = manufactured_solns_list.dfni_func densi_func = manufactured_solns_list.densi_func + upari_func = manufactured_solns_list.upari_func + ppari_func = manufactured_solns_list.ppari_func + pperpi_func = manufactured_solns_list.pperpi_func dfnn_func = manufactured_solns_list.dfnn_func densn_func = manufactured_solns_list.densn_func manufactured_E_fields = manufactured_electric_fields(Lr_in,Lz,r_bc,z_bc,composition,nr_local) @@ -509,16 +512,26 @@ function analyze_and_plot_data(path) # ion test density_sym = copy(density[:,:,:,:]) + upar_sym = copy(density[:,:,:,:]) + ppar_sym = copy(density[:,:,:,:]) + pperp_sym = copy(density[:,:,:,:]) is = 1 for it in 1:ntime for ir in 1:nr_global for iz in 1:nz_global density_sym[iz,ir,is,it] = densi_func(z[iz],r[ir],time[it]) + upar_sym[iz,ir,is,it] = upari_func(z[iz],r[ir],time[it]) + ppar_sym[iz,ir,is,it] = ppari_func(z[iz],r[ir],time[it]) + pperp_sym[iz,ir,is,it] = pperpi_func(z[iz],r[ir],time[it]) end end end compare_moments_symbolic_test(run_name,density,density_sym,"ion",z,r,time,nz_global,nr_global,ntime, L"\widetilde{n}_i",L"\widetilde{n}_i^{sym}",L"\varepsilon(\widetilde{n}_i)","dens") + compare_moments_symbolic_test(run_name,parallel_flow,upar_sym,"ion",z,r,time,nz_global,nr_global,ntime, + L"\widetilde{u}_{\|\|i}",L"\widetilde{u}_{\|\|i}^{sym}",L"\varepsilon(\widetilde{u}_{\|\|i})","upar") + compare_moments_symbolic_test(run_name,parallel_pressure,ppar_sym,"ion",z,r,time,nz_global,nr_global,ntime, + L"\widetilde{p}_{\|\|i}",L"\widetilde{p}_{\|\|i}^{sym}",L"\varepsilon(\widetilde{p}_{\|\|i})","ppar") compare_charged_pdf_symbolic_test(run_name,manufactured_solns_list,"ion", L"\widetilde{f}_i",L"\widetilde{f}^{sym}_i",L"\varepsilon(\widetilde{f}_i)","pdf") From 5ec7f4ce7edc563e0b2dd708ff72cf2d1b156a39 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 17 May 2023 13:27:04 +0100 Subject: [PATCH 018/331] Corrected bug where weights of the chebyshev radau grid were assembled incorrectly, missing the double counting of the weight at element boundaries. --- src/chebyshev.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index 8284ae939..b3c67ee7f 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -467,13 +467,12 @@ function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, sca # scale to account for modified domain (not [-1,1]) wgts[1:ngrid] .= wgts_radau[1:ngrid] if nelement_local > 1 - # account for double-counting of points at inner element boundaries - wgts[ngrid] += wgts_lobotto[1] for j ∈ 2:nelement_local + # account for double-counting of points at inner element boundaries + wgts[imin[j]-1] += wgts_lobotto[1] + # assign weights for interior of elements and one boundary point wgts[imin[j]:imax[j]] .= wgts_lobotto[2:ngrid] end - # remove double-counting of outer element boundary for last element - wgts[n] *= 0.5 end end return wgts From 8632cf761b778a28c0c80749ba12536a4939308e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 17 May 2023 14:27:26 +0100 Subject: [PATCH 019/331] Added the perpendicular pressure variable to diagnostic output. Tests pass when nvperp > 1. Special exceptions required for when nvperp = 1 and marginalised (over vperp) model is evolved. --- src/file_io.jl | 26 ++++++++++++++++++++++--- src/initial_conditions.jl | 7 +++++-- src/moment_kinetics_structs.jl | 1 + src/post_processing.jl | 8 ++++++-- src/time_advance.jl | 15 ++++++--------- src/velocity_moments.jl | 35 +++++++++++++++++++++++++++++++++- 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/file_io.jl b/src/file_io.jl index 85c6de038..398d06eb7 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -54,6 +54,8 @@ struct io_moments_info{Tfile, Ttime, Tphi, Tmomi, Tmomn} parallel_flow::Tmomi # handle for the charged species parallel pressure parallel_pressure::Tmomi + # handle for the charged species perpendicular pressure + perpendicular_pressure::Tmomi # handle for the charged species parallel heat flux parallel_heat_flux::Tmomi # handle for the charged species thermal speed @@ -357,6 +359,13 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, parallel_io=parallel_io, description="charged species parallel pressure", units="n_ref*T_ref") + + # io_pperp is the handle for the ion parallel pressure + io_pperp = create_dynamic_variable!(dynamic, "perpendicular_pressure", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="charged species perpendicular pressure", + units="n_ref*T_ref") # io_qpar is the handle for the ion parallel heat flux io_qpar = create_dynamic_variable!(dynamic, "parallel_heat_flux", mk_float, z, r; @@ -408,7 +417,7 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, units="c_ref") return io_moments_info(fid, io_time, io_phi, io_Er, io_Ez, io_density, io_upar, - io_ppar, io_qpar, io_vth, io_density_neutral, io_uz_neutral, + io_ppar, io_pperp, io_qpar, io_vth, io_density_neutral, io_uz_neutral, io_pz_neutral, io_qz_neutral, io_thermal_speed_neutral, parallel_io) end @@ -572,6 +581,8 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, n_ion_species) append_to_dynamic_var(io_moments.parallel_pressure, moments.charged.ppar, t_idx, z, r, n_ion_species) + append_to_dynamic_var(io_moments.perpendicular_pressure, moments.charged.pperp, t_idx, + z, r, n_ion_species) append_to_dynamic_var(io_moments.parallel_heat_flux, moments.charged.qpar, t_idx, z, r, n_ion_species) append_to_dynamic_var(io_moments.thermal_speed, moments.charged.vth, t_idx, z, r, @@ -636,6 +647,8 @@ end t_idx, z, r, n_ion_species) append_to_dynamic_var(io_moments.parallel_pressure.data, moments.charged.ppar, t_idx, z, r, n_ion_species) + append_to_dynamic_var(io_moments.perpendicular_pressure.data, moments.charged.pperp, + t_idx, z, r, n_ion_species) append_to_dynamic_var(io_moments.parallel_heat_flux.data, moments.charged.qpar, t_idx, z, r, n_ion_species) append_to_dynamic_var(io_moments.thermal_speed.data, moments.charged.vth, @@ -884,7 +897,7 @@ all the arrays have the same length, with an entry for each call to `debug_dump( function debug_dump end function debug_dump(vz::coordinate, vr::coordinate, vzeta::coordinate, vpa::coordinate, vperp::coordinate, z::coordinate, r::coordinate, t::mk_float; - ff=nothing, dens=nothing, upar=nothing, ppar=nothing, qpar=nothing, + ff=nothing, dens=nothing, upar=nothing, ppar=nothing, pperp=nothing, qpar=nothing, vth=nothing, ff_neutral=nothing, dens_neutral=nothing, uz_neutral=nothing, #ur_neutral=nothing, uzeta_neutral=nothing, @@ -983,6 +996,11 @@ function debug_dump(vz::coordinate, vr::coordinate, vzeta::coordinate, vpa::coor else debug_output_file.moments.parallel_pressure[:,:,:,debug_output_counter[]] = ppar end + if pperp === nothing + debug_output_file.moments.perpendicular_pressure[:,:,:,debug_output_counter[]] = 0.0 + else + debug_output_file.moments.perpendicular_pressure[:,:,:,debug_output_counter[]] = pperp + end if qpar === nothing debug_output_file.moments.parallel_heat_flux[:,:,:,debug_output_counter[]] = 0.0 else @@ -1059,6 +1077,7 @@ function debug_dump(fvec::Union{scratch_pdf,Nothing}, density = nothing upar = nothing ppar = nothing + pperp = nothing pdf_neutral = nothing density_neutral = nothing else @@ -1066,6 +1085,7 @@ function debug_dump(fvec::Union{scratch_pdf,Nothing}, density = fvec.density upar = fvec.upar ppar = fvec.ppar + pperp = fvec.pperp pdf_neutral = fvec.pdf_neutral density_neutral = fvec.density_neutral end @@ -1079,7 +1099,7 @@ function debug_dump(fvec::Union{scratch_pdf,Nothing}, Ez = fields.Ez end return debug_dump(vz, vr, vzeta, vpa, vperp, z, r, t; ff=pdf, dens=density, upar=upar, - ppar=ppar, ff_neutral=pdf_neutral, dens_neutral=density_neutral, + ppar=ppar, pperp=pperp, ff_neutral=pdf_neutral, dens_neutral=density_neutral, phi=phi, Er=Er, Ez=Ez, t, istage=istage, label=label) end diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 731d0b32d..d08a458bc 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -28,7 +28,7 @@ using ..velocity_moments: create_moments_charged, create_moments_neutral, update using ..velocity_moments: moments_charged_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! +using ..velocity_moments: update_ppar!, update_upar!, update_density!, update_pperp! using ..manufactured_solns: manufactured_solutions @@ -100,7 +100,9 @@ function init_pdf_and_moments(vz, vr, vzeta, vpa, vperp, z, r, composition, geom 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) - @. moments.charged.ppar = 0.5 * moments.charged.dens * moments.charged.vth^2 + # initialise pressures assuming isotropic distribution + @. moments.charged.ppar = moments.charged.dens * moments.charged.vth^2 + @. moments.charged.pperp = moments.charged.ppar if(n_neutral_species > 0) #neutral particles init_density!(moments.neutral.dens, z, r, species.neutral, n_neutral_species) @@ -209,6 +211,7 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, update_upar!(moments.charged.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.dens) update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) update_ppar!(moments.charged.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar) + update_pperp!(moments.charged.pperp, pdf.charged.unnorm, vpa, vperp, z, r, composition) begin_s_r_z_region() @loop_s_r_z is ir iz begin #moments.charged.upar[iz,ir,is] /= moments.charged.dens[iz,ir,is] diff --git a/src/moment_kinetics_structs.jl b/src/moment_kinetics_structs.jl index 04cb11f79..528c27a17 100644 --- a/src/moment_kinetics_structs.jl +++ b/src/moment_kinetics_structs.jl @@ -15,6 +15,7 @@ struct scratch_pdf{n_distribution_charged, n_moment, n_distribution_neutral,n_mo density::MPISharedArray{mk_float, n_moment} upar::MPISharedArray{mk_float, n_moment} ppar::MPISharedArray{mk_float, n_moment} + pperp::MPISharedArray{mk_float, n_moment} temp_z_s::MPISharedArray{mk_float, n_moment} # neutral particles pdf_neutral::MPISharedArray{mk_float, n_distribution_neutral} diff --git a/src/post_processing.jl b/src/post_processing.jl index c42fba813..58847d50d 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -191,9 +191,10 @@ function allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,nt 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) + perpendicular_pressure = allocate_float(nz_global,nr_global,n_ion_species,ntime) parallel_heat_flux = allocate_float(nz_global,nr_global,n_ion_species,ntime) thermal_speed = allocate_float(nz_global,nr_global,n_ion_species,ntime) - return density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed + return density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed end function allocate_global_zr_neutral_moments(nz_global,nr_global,n_neutral_species,ntime) @@ -310,7 +311,7 @@ function analyze_and_plot_data(path) # allocate arrays to contain the global fields as a function of (z,r,t) phi, Ez, Er = allocate_global_zr_fields(nz_global,nr_global,ntime) - density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed = + density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed = allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,ntime) if n_neutral_species > 0 neutral_density, neutral_uz, neutral_pz, neutral_qz, neutral_thermal_speed = @@ -331,6 +332,7 @@ function analyze_and_plot_data(path) read_distributed_zr_data!(density,"density",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(parallel_flow,"parallel_flow",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(parallel_pressure,"parallel_pressure",run_name,"moments",nblocks,nz_local,nr_local) + read_distributed_zr_data!(perpendicular_pressure,"perpendicular_pressure",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(parallel_heat_flux,"parallel_heat_flux",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(thermal_speed,"thermal_speed",run_name,"moments",nblocks,nz_local,nr_local) # neutral particle moments @@ -532,6 +534,8 @@ function analyze_and_plot_data(path) L"\widetilde{u}_{\|\|i}",L"\widetilde{u}_{\|\|i}^{sym}",L"\varepsilon(\widetilde{u}_{\|\|i})","upar") compare_moments_symbolic_test(run_name,parallel_pressure,ppar_sym,"ion",z,r,time,nz_global,nr_global,ntime, L"\widetilde{p}_{\|\|i}",L"\widetilde{p}_{\|\|i}^{sym}",L"\varepsilon(\widetilde{p}_{\|\|i})","ppar") + compare_moments_symbolic_test(run_name,perpendicular_pressure,pperp_sym,"ion",z,r,time,nz_global,nr_global,ntime, + L"\widetilde{p}_{\perp i}",L"\widetilde{p}_{\perp i}^{sym}",L"\varepsilon(\widetilde{p}_{\perp i})","pperp") compare_charged_pdf_symbolic_test(run_name,manufactured_solns_list,"ion", L"\widetilde{f}_i",L"\widetilde{f}^{sym}_i",L"\varepsilon(\widetilde{f}_i)","pdf") diff --git a/src/time_advance.jl b/src/time_advance.jl index 35314097d..19791b749 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -17,7 +17,7 @@ using ..chebyshev: setup_chebyshev_pseudospectral using ..chebyshev: chebyshev_derivative! using ..velocity_moments: update_moments!, reset_moments_status! using ..velocity_moments: enforce_moment_constraints! -using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_qpar! +using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_qpar!, update_pperp! 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! @@ -567,6 +567,7 @@ function setup_scratch_arrays(moments, pdf_charged_in, pdf_neutral_in, n_rk_stag density_array = allocate_shared_float(moment_dims...) upar_array = allocate_shared_float(moment_dims...) ppar_array = allocate_shared_float(moment_dims...) + pperp_array = allocate_shared_float(moment_dims...) temp_z_s_array = allocate_shared_float(moment_dims...) pdf_neutral_array = allocate_shared_float(pdf_neutral_dims...) @@ -574,13 +575,14 @@ function setup_scratch_arrays(moments, pdf_charged_in, pdf_neutral_in, n_rk_stag scratch[istage] = scratch_pdf(pdf_array, density_array, upar_array, - ppar_array, temp_z_s_array, + ppar_array, pperp_array, temp_z_s_array, pdf_neutral_array, density_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_neutral .= pdf_neutral_in scratch[istage].density_neutral .= moments.neutral.dens @@ -764,15 +766,9 @@ function rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, pdf.charged.unnorm[ivpa,ivperp,iz,ir,is] = new_scratch.pdf[ivpa,ivperp,iz,ir,is] end update_density!(new_scratch.density, pdf.charged.unnorm, vpa, vperp, z, r, composition) - update_upar!(new_scratch.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition, new_scratch.density) - # convert from particle particle flux to parallel flow -> MRH now done inside upar function - #begin_s_r_z_region() - #@loop_s_r_z is ir iz begin - # new_scratch.upar[iz,ir,is] /= new_scratch.density[iz,ir,is] - #end - update_ppar!(new_scratch.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, new_scratch.upar) + update_pperp!(new_scratch.pperp, pdf.charged.unnorm, vpa, vperp, z, r, composition) # update the thermal speed begin_s_r_z_region() try #below block causes DomainError if ppar < 0 or density, so exit cleanly if possible @@ -878,6 +874,7 @@ function ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, 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] end if composition.n_neutral_species > 0 # No need to synchronize here as we only change neutral quantities and previous diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 7ced2622c..93d027193 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -10,6 +10,7 @@ export update_moments! export update_density! export update_upar! export update_ppar! +export update_pperp! export update_qpar! export reset_moments_status! export enforce_moment_constraints! @@ -50,6 +51,8 @@ mutable struct moments_charged_substruct upar::MPISharedArray{mk_float,3} # this is the parallel pressure ppar::MPISharedArray{mk_float,3} + # this is the perpendicular pressure + pperp::MPISharedArray{mk_float,3} # this is the parallel heat flux qpar::MPISharedArray{mk_float,3} # this is the thermal speed based on the parallel temperature Tpar = ppar/dens: vth = sqrt(2*Tpar/m) @@ -90,6 +93,8 @@ function create_moments_charged(nz, nr, n_species) parallel_flow = allocate_shared_float(nz, nr, n_species) # allocate array used for the parallel pressure parallel_pressure = allocate_shared_float(nz, nr, n_species) + # allocate array used for the perpendicular pressure + perpendicular_pressure = allocate_shared_float(nz, nr, n_species) # allocate array used for the parallel flow parallel_heat_flux = allocate_shared_float(nz, nr, n_species) # allocate array of Bools that indicate if the parallel flow is updated for each species @@ -97,7 +102,7 @@ function create_moments_charged(nz, nr, n_species) thermal_speed = allocate_shared_float(nz, nr, n_species) # return struct containing arrays needed to update moments - return moments_charged_substruct(density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed) + return moments_charged_substruct(density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed) end # neutral particles have natural mean velocities @@ -262,6 +267,34 @@ function get_ppar(ff, vpa, vperp, upar) return 2.0*integrate_over_vspace(@view(ff[:,:]), vpa.scratch, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) end +function update_pperp!(pperp, pdf, vpa, vperp, z, r, composition) + @boundscheck composition.n_ion_species == size(pperp,3) || throw(BoundsError(pperp)) + @boundscheck r.n == size(pperp,2) || throw(BoundsError(pperp)) + @boundscheck z.n == size(pperp,1) || throw(BoundsError(pperp)) + + begin_s_r_z_region() + + @loop_s is begin + @views update_pperp_species!(pperp[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r) + end +end + +""" +calculate the updated perpendicular pressure (pperp) for a given species +""" +function update_pperp_species!(pperp, ff, vpa, vperp, z, r) + @boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff)) + @boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff)) + @boundscheck z.n == size(ff, 3) || throw(BoundsError(ff)) + @boundscheck r.n == size(ff, 4) || throw(BoundsError(ff)) + @boundscheck z.n == size(pperp, 1) || throw(BoundsError(pperp)) + @boundscheck r.n == size(pperp, 2) || throw(BoundsError(pperp)) + @loop_r_z ir iz begin + pperp[iz,ir] = get_pperp(@view(ff[:,:,iz,ir]), vpa, vperp) + end + return nothing +end + function get_pperp(ff, vpa, vperp) return integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 2, vperp.wgts) end From b1012545760dfdfb3c97ab6dce981c9923a208b4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 17 May 2023 15:20:26 +0100 Subject: [PATCH 020/331] Include extra information in print messages. --- test_velocity_integrals.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_velocity_integrals.jl b/test_velocity_integrals.jl index 08b282662..add57c0d7 100644 --- a/test_velocity_integrals.jl +++ b/test_velocity_integrals.jl @@ -36,8 +36,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ nrank, irank, Lvperp, discretization, fd_option, bc, adv_input,comm) # create the coordinate struct 'x' println("made inputs") - println("vpa: ngrid: ",ngrid," nelement: ",nelement_local) - println("vperp: ngrid: ",ngrid," nelement: ",nelement_local) + println("vpa: ngrid: ",ngrid," nelement: ",nelement_local, " Lvpa: ",Lvpa) + println("vperp: ngrid: ",ngrid," nelement: ",nelement_local, " Lvperp: ",Lvperp) vpa = define_coordinate(vpa_input) vperp = define_coordinate(vperp_input) From 6bd406efd07d677fe2ef175447ccbef9cd05b189 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 18 May 2023 10:27:57 +0100 Subject: [PATCH 021/331] Addition of the thermal speed calculation appropriate for a 2V model (using the isotropic pressure), keeping the parallel pressure thermal speed for the 1V case. MMS tests are provided. Upgrade of the test_velocity_integrals script to include a 1D Maxwellian example. Neutral particles are not touched by these changes (further work is required to fix factors of 2 for neutral particles). --- src/initial_conditions.jl | 13 ++++++----- src/manufactured_solns.jl | 31 ++++++++++++++++++++----- src/post_processing.jl | 7 +++++- src/time_advance.jl | 14 +++++++----- src/velocity_moments.jl | 29 ++++++++++++++++++++++++ test_velocity_integrals.jl | 46 +++++++++++++++++++++++++++++++++----- 6 files changed, 118 insertions(+), 22 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index d08a458bc..46fcfa3a5 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -28,7 +28,7 @@ using ..velocity_moments: create_moments_charged, create_moments_neutral, update using ..velocity_moments: moments_charged_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! +using ..velocity_moments: update_ppar!, update_upar!, update_density!, update_pperp!, update_vth! using ..manufactured_solns: manufactured_solutions @@ -192,7 +192,7 @@ function init_pdf!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, end function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, geometry,composition) - manufactured_solns_list = manufactured_solutions(r.L,z.L,r.bc,z.bc,geometry,composition,r.n) + manufactured_solns_list = manufactured_solutions(r.L,z.L,r.bc,z.bc,geometry,composition,r.n,vperp.n) dfni_func = manufactured_solns_list.dfni_func densi_func = manufactured_solns_list.densi_func dfnn_func = manufactured_solns_list.dfnn_func @@ -212,12 +212,13 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) update_ppar!(moments.charged.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar) update_pperp!(moments.charged.pperp, pdf.charged.unnorm, vpa, vperp, z, r, composition) - begin_s_r_z_region() - @loop_s_r_z is ir iz begin + update_vth!(moments.charged.vth, moments.charged.ppar, moments.charged.pperp, moments.charged.dens, vperp, z, r, composition) + #begin_s_r_z_region() + #@loop_s_r_z is ir iz begin #moments.charged.upar[iz,ir,is] /= moments.charged.dens[iz,ir,is] # update the thermal speed - moments.charged.vth[iz,ir,is] = sqrt(moments.charged.ppar[iz,ir,is]/moments.charged.dens[iz,ir,is]) - end + # moments.charged.vth[iz,ir,is] = sqrt(moments.charged.ppar[iz,ir,is]/moments.charged.dens[iz,ir,is]) + #end if n_neutral_species > 0 begin_sn_r_z_region() diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index b613a50fe..968c704fa 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -189,12 +189,31 @@ using IfElse end # ion perpendicular pressure symbolic function - function pperpi_sym(Lr,Lz,r_bc,z_bc,composition) + function pperpi_sym(Lr,Lz,r_bc,z_bc,composition,nvperp) densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) - pperpi = densi # simple vperp^2 dependence of dfni + if nvperp > 1 + pperpi = densi # simple vperp^2 dependence of dfni + else + pperpi = 0.0 # marginalised model has nvperp = 1, vperp[1] = 0 + end return pperpi end + # ion thermal speed symbolic function + function vthi_sym(Lr,Lz,r_bc,z_bc,composition,nvperp) + densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) + ppari = ppari_sym(Lr,Lz,r_bc,z_bc,composition) + pperpi = pperpi_sym(Lr,Lz,r_bc,z_bc,composition,nvperp) + isotropic_pressure = (1.0/3.0)*(ppari + 2.0*pperpi) + + if nvperp > 1 + vthi = sqrt(isotropic_pressure/densi) # thermal speed definition of 2V model + else + vthi = sqrt(ppari/densi) # thermal speed definition of 1V model + end + return vthi + end + function jpari_into_LHS_wall_sym(Lr,Lz,r_bc,z_bc,composition) if z_bc == "periodic" jpari_into_LHS_wall_sym = 0.0 @@ -280,11 +299,12 @@ using IfElse return Er_expanded, Ez_expanded, phi end - function manufactured_solutions(Lr,Lz,r_bc,z_bc,geometry,composition,nr) + function manufactured_solutions(Lr,Lz,r_bc,z_bc,geometry,composition,nr,nvperp) densi = densi_sym(Lr,Lz,r_bc,z_bc,composition) upari = upari_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr) ppari = ppari_sym(Lr,Lz,r_bc,z_bc,composition) - pperpi = pperpi_sym(Lr,Lz,r_bc,z_bc,composition) + pperpi = pperpi_sym(Lr,Lz,r_bc,z_bc,composition,nvperp) + vthi = vthi_sym(Lr,Lz,r_bc,z_bc,composition,nvperp) dfni = dfni_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr) densn = densn_sym(Lr,Lz,r_bc,z_bc,geometry,composition) @@ -296,6 +316,7 @@ using IfElse upari_func = build_function(upari, z, r, t, expression=Val{false}) ppari_func = build_function(ppari, z, r, t, expression=Val{false}) pperpi_func = build_function(pperpi, z, r, t, expression=Val{false}) + vthi_func = build_function(vthi, z, r, t, expression=Val{false}) densn_func = build_function(densn, z, r, t, expression=Val{false}) dfni_func = build_function(dfni, vpa, vperp, z, r, t, expression=Val{false}) dfnn_func = build_function(dfnn, vz, vr, vzeta, z, r, t, expression=Val{false}) @@ -309,7 +330,7 @@ using IfElse manufactured_solns_list = (densi_func = densi_func, densn_func = densn_func, dfni_func = dfni_func, dfnn_func = dfnn_func, upari_func = upari_func, ppari_func = ppari_func, - pperpi_func = pperpi_func) + pperpi_func = pperpi_func, vthi_func = vthi_func) return manufactured_solns_list end diff --git a/src/post_processing.jl b/src/post_processing.jl index 58847d50d..d021954b9 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -475,12 +475,13 @@ function analyze_and_plot_data(path) #geometry, composition = get_geometry_and_composition(scan_input,n_ion_species,n_neutral_species) - manufactured_solns_list = manufactured_solutions(Lr_in,Lz,r_bc,z_bc,geometry,composition,nr_local) + manufactured_solns_list = manufactured_solutions(Lr_in,Lz,r_bc,z_bc,geometry,composition,nr_local,nvperp) dfni_func = manufactured_solns_list.dfni_func densi_func = manufactured_solns_list.densi_func upari_func = manufactured_solns_list.upari_func ppari_func = manufactured_solns_list.ppari_func pperpi_func = manufactured_solns_list.pperpi_func + vthi_func = manufactured_solns_list.vthi_func dfnn_func = manufactured_solns_list.dfnn_func densn_func = manufactured_solns_list.densn_func manufactured_E_fields = manufactured_electric_fields(Lr_in,Lz,r_bc,z_bc,composition,nr_local) @@ -517,6 +518,7 @@ function analyze_and_plot_data(path) upar_sym = copy(density[:,:,:,:]) ppar_sym = copy(density[:,:,:,:]) pperp_sym = copy(density[:,:,:,:]) + vthi_sym = copy(density[:,:,:,:]) is = 1 for it in 1:ntime for ir in 1:nr_global @@ -525,6 +527,7 @@ function analyze_and_plot_data(path) upar_sym[iz,ir,is,it] = upari_func(z[iz],r[ir],time[it]) ppar_sym[iz,ir,is,it] = ppari_func(z[iz],r[ir],time[it]) pperp_sym[iz,ir,is,it] = pperpi_func(z[iz],r[ir],time[it]) + vthi_sym[iz,ir,is,it] = vthi_func(z[iz],r[ir],time[it]) end end end @@ -536,6 +539,8 @@ function analyze_and_plot_data(path) L"\widetilde{p}_{\|\|i}",L"\widetilde{p}_{\|\|i}^{sym}",L"\varepsilon(\widetilde{p}_{\|\|i})","ppar") compare_moments_symbolic_test(run_name,perpendicular_pressure,pperp_sym,"ion",z,r,time,nz_global,nr_global,ntime, L"\widetilde{p}_{\perp i}",L"\widetilde{p}_{\perp i}^{sym}",L"\varepsilon(\widetilde{p}_{\perp i})","pperp") + compare_moments_symbolic_test(run_name,thermal_speed,vthi_sym,"ion",z,r,time,nz_global,nr_global,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,manufactured_solns_list,"ion", L"\widetilde{f}_i",L"\widetilde{f}^{sym}_i",L"\varepsilon(\widetilde{f}_i)","pdf") diff --git a/src/time_advance.jl b/src/time_advance.jl index 19791b749..2f67fb89a 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -17,7 +17,7 @@ using ..chebyshev: setup_chebyshev_pseudospectral using ..chebyshev: chebyshev_derivative! using ..velocity_moments: update_moments!, reset_moments_status! using ..velocity_moments: enforce_moment_constraints! -using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_qpar!, update_pperp! +using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_qpar!, update_pperp!, update_vth! 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! @@ -757,7 +757,9 @@ function rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, ## # update the charged particle distribution and moments ## - + # MRH here we seem to have duplicate arrays for storing n, u||, p||, etc, but not for vth + # MRH 'scratch' is for the multiple stages of time advanced quantities, but 'moments' can be updated directly at each stage + # MRH in the standard drift-kinetic model. Consider taking moment quantities out of scratch for clarity. @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin new_scratch.pdf[ivpa,ivperp,iz,ir,is] = rk_coefs[1]*pdf.charged.norm[ivpa,ivperp,iz,ir,is] + rk_coefs[2]*old_scratch.pdf[ivpa,ivperp,iz,ir,is] + rk_coefs[3]*new_scratch.pdf[ivpa,ivperp,iz,ir,is] end @@ -772,9 +774,10 @@ function rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, # update the thermal speed begin_s_r_z_region() try #below block causes DomainError if ppar < 0 or density, so exit cleanly if possible - @loop_s_r_z is ir iz begin - moments.charged.vth[iz,ir,is] = sqrt(new_scratch.ppar[iz,ir,is]/new_scratch.density[iz,ir,is]) - end + update_vth!(moments.charged.vth, new_scratch.ppar, new_scratch.pperp, new_scratch.density, vperp, z, r, composition) + #@loop_s_r_z is ir iz begin + # moments.charged.vth[iz,ir,is] = sqrt(new_scratch.ppar[iz,ir,is]/new_scratch.density[iz,ir,is]) + #end catch e if global_size[] > 1 println("ERROR: error at line 598 of time_advance.jl") @@ -831,6 +834,7 @@ function ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, 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] end if composition.n_neutral_species > 0 diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 93d027193..65919452b 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -12,6 +12,7 @@ export update_upar! export update_ppar! export update_pperp! export update_qpar! +export update_vth! export reset_moments_status! export enforce_moment_constraints! export moments_chrg_substruct, moments_ntrl_substruct @@ -29,6 +30,7 @@ export get_density export get_upar export get_ppar export get_pperp +export get_pressure using ..type_definitions: mk_float using ..array_allocation: allocate_shared_float, allocate_bool @@ -299,6 +301,33 @@ function get_pperp(ff, vpa, vperp) return integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 2, vperp.wgts) end +function update_vth!(vth, ppar, pperp, dens, vperp, z, r, composition) + @boundscheck composition.n_ion_species == size(vth,3) || throw(BoundsError(vth)) + @boundscheck r.n == size(vth,2) || throw(BoundsError(vth)) + @boundscheck z.n == size(vth,1) || throw(BoundsError(vth)) + + begin_s_r_z_region() + + if vperp.n > 1 #2V definition + @loop_s_r_z is ir iz begin + piso = get_pressure(ppar[iz,ir,is],pperp[iz,ir,is]) + vth[iz,ir,is] = sqrt(piso/dens[iz,ir,is]) + end + else #1V definition + @loop_s_r_z is ir iz begin + vth[iz,ir,is] = sqrt(ppar[iz,ir,is]/dens[iz,ir,is]) + end + end +end + +""" +compute the isotropic pressure from the already computed ppar and pperp +""" +function get_pressure(ppar::mk_float,pperp::mk_float) + pres = (1.0/3.0)*(ppar + 2.0*pperp) + return pres +end + """ NB: if this function is called and if ppar_updated is false, then the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density diff --git a/test_velocity_integrals.jl b/test_velocity_integrals.jl index add57c0d7..108a665f7 100644 --- a/test_velocity_integrals.jl +++ b/test_velocity_integrals.jl @@ -11,7 +11,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp + using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.array_allocation: allocate_float # define inputs needed for the test @@ -30,7 +30,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate - vpa_input = grid_input("vpa", ngrid, nelement_global, nelement_local, + vr_input = grid_input("vperp", 1, 1, 1, + nrank, irank, 1.0, discretization, fd_option, bc, adv_input,comm) + vz_input = grid_input("vpa", ngrid, nelement_global, nelement_local, + nrank, irank, Lvpa, discretization, fd_option, bc, adv_input,comm) + vpa_input = grid_input("vpa", ngrid, nelement_global, nelement_local, nrank, irank, Lvpa, discretization, fd_option, bc, adv_input,comm) vperp_input = grid_input("vperp", ngrid, nelement_global, nelement_local, nrank, irank, Lvperp, discretization, fd_option, bc, adv_input,comm) @@ -38,21 +42,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("made inputs") println("vpa: ngrid: ",ngrid," nelement: ",nelement_local, " Lvpa: ",Lvpa) println("vperp: ngrid: ",ngrid," nelement: ",nelement_local, " Lvperp: ",Lvperp) - vpa = define_coordinate(vpa_input) + vz = define_coordinate(vz_input) + vr = define_coordinate(vr_input) + vpa = define_coordinate(vpa_input) vperp = define_coordinate(vperp_input) dfn = allocate_float(vpa.n,vperp.n) + dfn1D = allocate_float(vz.n, vr.n) function pressure(ppar,pperp) pres = (1.0/3.0)*(ppar + 2.0*pperp) return pres end + # 2D isotropic Maxwellian test # assign a known isotropic Maxwellian distribution in normalised units dens = 3.0/4.0 upar = 2.0/3.0 ppar = 2.0/3.0 pperp = 2.0/3.0 - pres = pressure(ppar,pperp) + pres = get_pressure(ppar,pperp) mass = 1.0 vth = sqrt(pres/(dens*mass)) for ivperp in 1:vperp.n @@ -72,12 +80,40 @@ if abspath(PROGRAM_FILE) == @__FILE__ pres_test = pressure(ppar_test,pperp_test) # output test results println("") - println("Isotropic Maxwellian") + println("Isotropic 2D Maxwellian") println("dens_test: ", dens_test, " dens: ", dens, " error: ", abs(dens_test-dens)) println("upar_test: ", upar_test, " upar: ", upar, " error: ", abs(upar_test-upar)) println("pres_test: ", pres_test, " pres: ", pres, " error: ", abs(pres_test-pres)) println("") + ################### + # 1D Maxwellian test + + dens = 3.0/4.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + mass = 1.0 + vth = sqrt(ppar/(dens*mass)) + for ivz in 1:vz.n + for ivr in 1:vr.n + vz_val = vz.grid[ivz] + dfn1D[ivz,ivr] = (dens/vth)*exp( - ((vz_val-upar)^2)/vth^2 ) + end + end + dens_test = get_density(dfn1D,vz,vr) + upar_test = get_upar(dfn1D,vz,vr,dens_test) + ppar_test = get_ppar(dfn1D,vz,vr,upar_test) + # output test results + println("") + println("1D Maxwellian") + println("dens_test: ", dens_test, " dens: ", dens, " error: ", abs(dens_test-dens)) + println("upar_test: ", upar_test, " upar: ", upar, " error: ", abs(upar_test-upar)) + println("ppar_test: ", ppar_test, " ppar: ", ppar, " error: ", abs(ppar_test-ppar)) + println("") + + ################### + # biMaxwellian test + # assign a known biMaxwellian distribution in normalised units dens = 3.0/4.0 upar = 2.0/3.0 From d5c366fafec352f64f4e84e328808c77dc0246a5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 18 May 2023 14:32:19 +0100 Subject: [PATCH 022/331] Addition of a Krook collision operator and corresponding manufactured solutions test terms in the manufactured source. --- src/collision_models.jl | 52 ++++++++++++++++++++++++++++++++++++ src/input_structs.jl | 3 +++ src/manufactured_solns.jl | 13 ++++++++- src/moment_kinetics.jl | 1 + src/moment_kinetics_input.jl | 4 ++- src/time_advance.jl | 15 +++++++++-- 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/collision_models.jl diff --git a/src/collision_models.jl b/src/collision_models.jl new file mode 100644 index 000000000..01d93120f --- /dev/null +++ b/src/collision_models.jl @@ -0,0 +1,52 @@ +""" +module containing Krook collision operators and other model operators +for charged particle collisions +""" +module collision_models + +using ..looping + +""" +krook self-collision operator +Ckrook[F] = nu * ( FMaxwellian - F) +""" +function explicit_krook_collisions!(pdf_out, pdf_in, dens_in, upar_in, vth_in, + composition, collisions, dt, + r, z, vperp, vpa) + @boundscheck vpa.n == size(pdf_in, 1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in, 2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in, 3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in, 4) || throw(BoundsError(pdf_in)) + @boundscheck composition.n_ion_species == size(pdf_in, 5) || throw(BoundsError(pdf_in)) + @boundscheck vpa.n == size(pdf_out, 1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out, 2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out, 3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out, 4) || throw(BoundsError(pdf_out)) + @boundscheck composition.n_ion_species == size(pdf_out, 5) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(dens_in, 1) || throw(BoundsError(dens_in)) + @boundscheck r.n == size(dens_in, 2) || throw(BoundsError(dens_in)) + @boundscheck composition.n_ion_species == size(dens_in, 3) || throw(BoundsError(dens_in)) + @boundscheck z.n == size(upar_in, 1) || throw(BoundsError(upar_in)) + @boundscheck r.n == size(upar_in, 2) || throw(BoundsError(upar_in)) + @boundscheck composition.n_ion_species == size(upar_in, 3) || throw(BoundsError(upar_in)) + @boundscheck z.n == size(vth_in, 1) || throw(BoundsError(vth_in)) + @boundscheck r.n == size(vth_in, 2) || throw(BoundsError(vth_in)) + @boundscheck composition.n_ion_species == size(vth_in, 3) || throw(BoundsError(vth_in)) + nu_krook = collisions.nuii_krook + if vperp.n > 1 + pvth = 3 + else + pvth = 1 + end + begin_s_r_z_vperp_region() + @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin + # form the Maxwellian from the moments of the current distribution + F_Maxwellian = ( (dens_in[iz,ir,is]/vth_in[iz,ir,is]^pvth)* + exp( - (((vpa.grid[ivpa] - upar_in[iz,ir,is])^2) + + (vperp.grid[ivperp]^2) )/(vth_in[iz,ir,is]^2) ) ) + Ckrook = nu_krook*(F_Maxwellian - pdf_in[ivpa,ivperp,iz,ir,is]) + pdf_out[ivpa,ivperp,iz,ir,is] += dt*Ckrook + end +end + +end \ No newline at end of file diff --git a/src/input_structs.jl b/src/input_structs.jl index 47287a8ce..ace98aa53 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -66,6 +66,7 @@ mutable struct advance_info r_diffusion::Bool #flag to control how r bc is imposed when r diffusion terms are present vpa_diffusion::Bool #flag to control how vpa bc is imposed when vpa diffusion terms are present explicit_fp_collisions::Bool + explicit_krook_collisions::Bool end """ @@ -298,6 +299,8 @@ mutable struct collisions_input constant_ionization_rate::Bool # ion-ion self collision frequency nuii::mk_float + # ion-ion Krook operator collision frequency + nuii_krook::mk_float end """ diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index 968c704fa..1ca658b75 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -353,6 +353,8 @@ using IfElse # ion manufactured solutions densi = densi_sym(r_coord.L,z_coord.L,r_coord.bc,z_coord.bc,composition) + upari = upari_sym(r_coord.L,z_coord.L,r_coord.bc,z_coord.bc,composition,geometry,r_coord.n) + vthi = vthi_sym(r_coord.L,z_coord.L,r_coord.bc,z_coord.bc,composition,vperp_coord.n) dfni = dfni_sym(r_coord.L,z_coord.L,r_coord.bc,z_coord.bc,composition,geometry,r_coord.n) vrvzvzeta_dfni = cartesian_dfni_sym(r_coord.L,z_coord.L,r_coord.bc,z_coord.bc,composition) #dfni in vr vz vzeta coordinates @@ -374,6 +376,7 @@ using IfElse Bzed = geometry.Bzed Bmag = geometry.Bmag rhostar = geometry.rhostar + nu_krook = collisions.nuii_krook #exceptions for cases with missing terms if composition.n_neutral_species > 0 cx_frequency = collisions.charge_exchange @@ -394,7 +397,15 @@ using IfElse # the ion source to maintain the manufactured solution Si = ( Dt(dfni) + ( vpa * (Bzed/Bmag) - 0.5*rhostar*Er ) * Dz(dfni) + ( 0.5*rhostar*Ez*rfac ) * Dr(dfni) + ( 0.5*Ez*Bzed/Bmag ) * Dvpa(dfni) + cx_frequency*( densn*dfni - densi*gav_dfnn ) - ionization_frequency*dense*gav_dfnn) - + if nu_krook > 0.0 + if vperp_coord.n > 1 + pvth = 3 + else + pvth = 1 + end + FMaxwellian = (densi/vthi^pvth)*exp( -( ( vpa-upari)^2 + vperp^2 )/vthi^2) + Si += - nu_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)) diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index 910520f56..43c89328d 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -34,6 +34,7 @@ include("manufactured_solns.jl") # MRH Here? include("initial_conditions.jl") #include("semi_lagrange.jl") include("fokker_planck.jl") +include("collision_models.jl") include("advection.jl") include("vpa_advection.jl") include("z_advection.jl") diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 282133c13..9a002b5c7 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -183,6 +183,7 @@ function mk_input(scan_input=Dict()) collisions.ionization = get(scan_input, "ionization_frequency", collisions.charge_exchange) collisions.constant_ionization_rate = get(scan_input, "constant_ionization_rate", false) collisions.nuii = get(scan_input, "nuii", 0.0) + collisions.nuii_krook = get(scan_input, "nuii_krook", 0.0) # parameters related to the time stepping nstep = get(scan_input, "nstep", 5) @@ -861,7 +862,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) ionization = 0.0 constant_ionization_rate = false nuii = 0.0 - collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii) + nuii_krook = 0.0 + collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_krook) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength diff --git a/src/time_advance.jl b/src/time_advance.jl index 2f67fb89a..4515fa1c2 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -42,6 +42,7 @@ using ..force_balance: force_balance! using ..energy_equation: energy_equation! using ..em_fields: setup_em_fields, update_phi! using ..fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! +using ..collision_models: explicit_krook_collisions! #using ..semi_lagrange: setup_semi_lagrange using Dates @@ -209,6 +210,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, else explicit_fp_collisions = false end + if collisions.nuii_krook > 0.0 + explicit_krook_collisions = true + else + explicit_krook_collisions = false + 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) # flag to determine if a d^2/dvpa^2 operator is present @@ -216,7 +222,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance = advance_info(advance_vpa_advection, advance_z_advection, advance_r_advection, advance_neutral_z_advection, advance_neutral_r_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, advance_ionization_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, advance_energy, rk_coefs, - manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions) + manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_krook_collisions) if z.discretization == "chebyshev_pseudospectral" @@ -1035,7 +1041,12 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, if advance.explicit_fp_collisions explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf,composition,collisions,dt,fp_arrays, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) - end + end + if advance.explicit_krook_collisions + explicit_krook_collisions!(fvec_out.pdf, fvec_in.pdf, fvec_in.density, fvec_in.upar, moments.charged.vth, + composition, collisions, dt, r, z, vperp, vpa) + end + # enforce boundary conditions in r, z and vpa on the charged particle distribution function enforce_boundary_conditions!(fvec_out.pdf, boundary_distributions.pdf_rboundary_charged, From de13686c30b873de77c4f76cf3eee8c02d788a3f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 25 May 2023 15:42:52 +0100 Subject: [PATCH 023/331] Attempt to implement the operator C[F,F_Maxwellian] where the second argument feeds in to the Rosenbluth potentials. At low resolution poor behaviour is observed with Gibbs phenomena -- debugging/exploration required. --- src/fokker_planck.jl | 216 ++++++++++++++++++++++++++++++++++- src/input_structs.jl | 3 + src/moment_kinetics_input.jl | 4 +- src/time_advance.jl | 15 ++- 4 files changed, 231 insertions(+), 7 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 3c52d43d1..6b099ae86 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -8,10 +8,10 @@ export init_fokker_planck_collisions export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! -using SpecialFunctions: ellipk, ellipe +using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float -using ..communication: Array +using ..communication: MPISharedArray using ..velocity_moments: integrate_over_vspace using ..calculus: derivative!, second_derivative! using ..looping @@ -24,7 +24,14 @@ struct fokkerplanck_arrays_struct elliptic_integral_E_factor::Array{mk_float,4} elliptic_integral_K_factor::Array{mk_float,4} Rosenbluth_G::Array{mk_float,2} + Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} + Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} + Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} Rosenbluth_H::Array{mk_float,2} + Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} + Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} + Cflux_vpa::MPISharedArray{mk_float,2} + Cflux_vperp::MPISharedArray{mk_float,2} buffer_vpavperp_1::Array{mk_float,2} buffer_vpavperp_2::Array{mk_float,2} #Cssp_result_vpavperp::Array{mk_float,2} @@ -41,13 +48,22 @@ function allocate_fokkerplanck_arrays(vperp,vpa) elliptic_integral_E_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) elliptic_integral_K_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) Rosenbluth_G = allocate_float(nvpa,nvperp) + Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) Rosenbluth_H = allocate_float(nvpa,nvperp) + Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) + Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) + Cflux_vpa = allocate_shared_float(nvpa,nvperp) + Cflux_vperp = allocate_shared_float(nvpa,nvperp) buffer_vpavperp_1 = allocate_float(nvpa,nvperp) buffer_vpavperp_2 = allocate_float(nvpa,nvperp) #Cssp_result_vpavperp = allocate_float(nvpa,nvperp) return fokkerplanck_arrays_struct(elliptic_integral_E_factor,elliptic_integral_K_factor, - Rosenbluth_G,Rosenbluth_H, + Rosenbluth_G,Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, + Rosenbluth_H,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, + Cflux_vpa,Cflux_vperp, buffer_vpavperp_1,buffer_vpavperp_2) #Cssp_result_vpavperp) end @@ -88,7 +104,7 @@ function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_in #println(mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) elliptic_integral_E_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipe(mm)*prefac/pi elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipk(mm)/(pi*prefac) - println(elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) + #println(elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) end end @@ -135,6 +151,40 @@ function calculate_Rosenbluth_potentials!(Rosenbluth_G,Rosenbluth_H,fsp_in, end +""" +calculates the (normalised) Rosenbluth potential coefficients d2Gdvpa2, d2Gdvperpdvpa, ..., dHdvperp for a Maxwellian inputs. +""" +function calculate_Maxwellian_Rosenbluth_coefficients(dens,upar,vth,vpa,vperp,ivpa,ivperp,n_ion_species) # Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, + # zero coefficients prior to looping over s' + Rosenbluth_d2Gdvpa2 = 0.0 + Rosenbluth_d2Gdvperpdvpa = 0.0 + Rosenbluth_d2Gdvperp2 = 0.0 + Rosenbluth_dHdvpa = 0.0 + Rosenbluth_dHdvperp = 0.0 + + # fill in value at (ivpa,ivperp) + for isp in 1:n_ion_species + Rosenbluth_d2Gdvpa2 += d2Gdvpa2(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) + Rosenbluth_d2Gdvperpdvpa += d2Gdvperpdvpa(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) + Rosenbluth_d2Gdvperp2 += d2Gdvperp2(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) + Rosenbluth_dHdvpa += dHdvpa(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) + Rosenbluth_dHdvperp += dHdvperp(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) + end + return Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp +end + +""" +calculates the collisional fluxes given input F_s and G_sp, H_sp +""" +function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dHdvpa,dHdvperp, + ms,msp,vperp_val) + # fill in value at (ivpa,ivperp) + Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa - 2.0*(ms/msp)*F*dHdvpa + Cflux_vperp = vperp_val* ( dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp ) + return Cflux_vpa, Cflux_vperp +end + """ returns (normalised) C[Fs,Fs'] @@ -264,4 +314,162 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collision end end +function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_in,dens_in,upar_in,vth_in, + composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + n_ion_species = composition.n_ion_species + @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) + @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) + @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) + @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + + mi = 1.0 # generalise this to an Array with size n_ion_species + mip = 1.0 # generalise this to an Array with size n_ion_species + cfreqii = collisions.nuii # generalise this to an Array with size (n_ion_species,n_ion_species) + fk = fokkerplanck_arrays + Cssp_result_vpavperp = scratch_dummy.dummy_vpavperp + pdf_buffer_1 = scratch_dummy.buffer_vpavperpzrs_1 + pdf_buffer_2 = scratch_dummy.buffer_vpavperpzrs_2 + + # precompute derivatives of the pdfs to benefit from parallelisation + # d F / d vpa + begin_s_r_z_vperp_region() + @loop_s_r_z_vperp is ir iz ivperp begin + @views derivative!(vpa.scratch, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp,iz,ir,is] = vpa.scratch + end + # d F / d vperp + begin_s_r_z_vpa_region() + @loop_s_r_z_vpa is ir iz ivpa begin + @views derivative!(vperp.scratch, pdf_in[ivpa,:,iz,ir,is], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:,iz,ir,is] = vperp.scratch + end + + begin_s_r_z_vperp_vpa_region() + @loop_s_r_z is ir iz begin + @loop_vperp_vpa ivperp ivpa begin + # first compute local (in z,r) Rosenbluth potential coefficients, summing over all s' + ( (fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp], fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], + fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp], + fk.Rosenbluth_dHdvperp[ivpa,ivperp]) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[iz,ir,:], + upar_in[iz,ir,:],vth_in[iz,ir,:],vpa,vperp,ivpa,ivperp,n_ion_species) ) + + # now form the collisional fluxes at this s,z,r + ( (fk.Cflux_vpa[ivpa,ivperp],fk.Cflux_vperp[ivpa,ivperp]) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp,iz,ir,is], + pdf_buffer_1[ivpa,ivperp,iz,ir,is],pdf_buffer_2[ivpa,ivperp,iz,ir,is], + fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp],fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], + fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp],fk.Rosenbluth_dHdvperp[ivpa,ivperp], + mi,mip,vperp.grid[ivperp]) ) + + # now overwrite the buffer arrays with the local values as we no longer need dFdvpa or dFdvperp at s,r,z + pdf_buffer_1[ivpa,ivperp,iz,ir,is] = fk.Cflux_vpa[ivpa,ivperp] + pdf_buffer_2[ivpa,ivperp,iz,ir,is] = fk.Cflux_vperp[ivpa,ivperp] + end + + end + + # now differentiate the fluxes to obtain the explicit operator + + # d Cflux_vpa / d vpa + begin_s_r_z_vperp_region() + @loop_s_r_z_vperp is ir iz ivperp begin + @views derivative!(vpa.scratch, pdf_buffer_1[:,ivperp,iz,ir,is], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp,iz,ir,is] = vpa.scratch + end + # (1/vperp) d Cflux_vperp / d vperp + begin_s_r_z_vpa_region() + @loop_s_r_z_vpa is ir iz ivpa begin + @views derivative!(vperp.scratch, pdf_buffer_2[ivpa,:,iz,ir,is], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:,iz,ir,is] = vperp.scratch[:]/vperp.grid[:] + end + + # now add the result to the outgoing pdf + # d F / d t = nu_ii * ( d Cflux_vpa / d vpa + (1/vperp) d Cflux_vperp / d vperp) + begin_s_r_z_vperp_vpa_region() + @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin + pdf_out[ivpa,ivperp,iz,ir,is] += dt*cfreqii*(pdf_buffer_1[ivpa,ivperp,iz,ir,is] + pdf_buffer_1[ivpa,ivperp,iz,ir,is]) + end +end + +# below are a series of functions that can be used to test the calculation +# of the Rosenbluth potentials for a shifted Maxwellian +# or provide an estimate for collisional coefficients + +# 1D derivative functions + +function dGdeta(eta::mk_float) + # d \tilde{G} / d eta + dGdeta_fac = (1.0/sqrt(pi))*exp(-eta^2)/eta + (1.0 - 0.5/(eta^2))*erf(eta) + return dGdeta_fac +end + +function ddGddeta(eta::mk_float) + # d / d eta ( (1/ eta) d \tilde{G} d eta + ddGddeta_fac = (1.5/(eta^2) - 1.0)*erf(eta)/(eta^2) - (3.0/sqrt(pi))*exp(-eta^2)/(eta^3) + return ddGddeta_fac +end + +function dHdeta(eta::mk_float) + dHdeta_fac = (2.0/sqrt(pi))*(exp(-eta^2))/eta - erf(eta)/(eta^2) + return dHdeta_fac end + +# functions of vpa & vperp +function eta_func(upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + speed = sqrt( (vpa.grid[ivpa] - upar)^2 + vperp.grid[ivperp]^2)/vth + return speed +end + +function d2Gdvpa2(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta) + ddGddeta(eta)*((vpa.grid[ivpa] - upar)^2)/(vth^2) + d2Gdvpa2_fac = fac*dens/(eta*vth) + return d2Gdvpa2_fac +end + +function d2Gdvperpdvpa(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = ddGddeta(eta)*vperp.grid[ivperp]*(vpa.grid[ivpa] - upar)/(vth^2) + d2Gdvperpdvpa_fac = fac*dens/(eta*vth) + return d2Gdvperpdvpa_fac +end + +function d2Gdvperp2(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta) + ddGddeta(eta)*(vperp.grid[ivperp]^2)/(vth^2) + d2Gdvperp2_fac = fac*dens/(eta*vth) + return d2Gdvperp2_fac +end + +function dGdvperp(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta)*vperp.grid[ivperp]*dens/(vth*eta) + return fac +end + +function dHdvperp(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dHdeta(eta)*vperp.grid[ivperp]*dens/(eta*vth^3) + return fac +end + +function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dHdeta(eta)*(vpa.grid[ivpa]-upar)*dens/(eta*vth^3) + return fac +end + +end \ No newline at end of file diff --git a/src/input_structs.jl b/src/input_structs.jl index ace98aa53..20b84a636 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -66,6 +66,7 @@ mutable struct advance_info r_diffusion::Bool #flag to control how r bc is imposed when r diffusion terms are present vpa_diffusion::Bool #flag to control how vpa bc is imposed when vpa diffusion terms are present explicit_fp_collisions::Bool + explicit_fp_F_FM_collisions::Bool explicit_krook_collisions::Bool end @@ -299,6 +300,8 @@ mutable struct collisions_input constant_ionization_rate::Bool # ion-ion self collision frequency nuii::mk_float + # ion-ion self collision frequency with C[F_s,F_Ms'] operator + nuii_pitch::mk_float # ion-ion Krook operator collision frequency nuii_krook::mk_float end diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 9a002b5c7..bf171c256 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -183,6 +183,7 @@ function mk_input(scan_input=Dict()) collisions.ionization = get(scan_input, "ionization_frequency", collisions.charge_exchange) collisions.constant_ionization_rate = get(scan_input, "constant_ionization_rate", false) collisions.nuii = get(scan_input, "nuii", 0.0) + collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) collisions.nuii_krook = get(scan_input, "nuii_krook", 0.0) # parameters related to the time stepping @@ -862,8 +863,9 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) ionization = 0.0 constant_ionization_rate = false nuii = 0.0 + nuii_pitch = 0.0 nuii_krook = 0.0 - collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_krook) + collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_pitch, nuii_krook) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength diff --git a/src/time_advance.jl b/src/time_advance.jl index 4515fa1c2..20ad7b2b4 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -41,7 +41,7 @@ using ..continuity: continuity_equation! using ..force_balance: force_balance! using ..energy_equation: energy_equation! using ..em_fields: setup_em_fields, update_phi! -using ..fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! +using ..fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions!, explicit_fokker_planck_collisions_Maxwellian_coefficients! using ..collision_models: explicit_krook_collisions! #using ..semi_lagrange: setup_semi_lagrange using Dates @@ -210,6 +210,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, else explicit_fp_collisions = false end + if collisions.nuii_pitch > 0.0 && vperp.n > 1 + explicit_fp_F_FM_collisions = true + else + explicit_fp_F_FM_collisions = false + end if collisions.nuii_krook > 0.0 explicit_krook_collisions = true else @@ -222,7 +227,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance = advance_info(advance_vpa_advection, advance_z_advection, advance_r_advection, advance_neutral_z_advection, advance_neutral_r_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, advance_ionization_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, advance_energy, rk_coefs, - manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_krook_collisions) + manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_fp_F_FM_collisions, explicit_krook_collisions) if z.discretization == "chebyshev_pseudospectral" @@ -1042,6 +1047,12 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf,composition,collisions,dt,fp_arrays, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) end + if advance.explicit_fp_F_FM_collisions + explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, + fvec_in.density, fvec_in.upar, moments.charged.vth, + composition, collisions, dt, fp_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + end if advance.explicit_krook_collisions explicit_krook_collisions!(fvec_out.pdf, fvec_in.pdf, fvec_in.density, fvec_in.upar, moments.charged.vth, composition, collisions, dt, r, z, vperp, vpa) From a14aa52d51de732d6b8f39946441a274531e5c6f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 26 May 2023 15:52:28 +0100 Subject: [PATCH 024/331] Test to comfirm that C[F,F_Maxwellian] operator vanishes for sufficiently high resolution when using F = F_Maxwellian. --- fkpl_test.jl | 264 +++++++++++++++++++++++++++++++++++-------- src/fokker_planck.jl | 77 ++++++++----- 2 files changed, 266 insertions(+), 75 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 6dd58c5c1..9174bde56 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -36,6 +36,11 @@ function H_Maxwellian(vpa,vperp,ivpa,ivperp) return H end +function pressure(ppar,pperp) + pres = (1.0/3.0)*(ppar + 2.0*pperp) + return pres +end + #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) # #Gamma = 0.0 # #return Gamma @@ -110,19 +115,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: init_fokker_planck_collisions + using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients + using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs using moment_kinetics.type_definitions: mk_float, mk_int + using moment_kinetics.calculus: derivative! + using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure + + test_Rosenbluth_integrals = false discretization = "chebyshev_pseudospectral" #discretization = "finite_difference" # define inputs needed for the test - vpa_ngrid = 3 #number of points per element - vpa_nelement_local = 3 # number of elements per rank + vpa_ngrid = 17 #number of points per element + vpa_nelement_local = 20 # number of elements per rank vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 6.0 #physical box size in reference units + vpa_L = 12.0 #physical box size in reference units bc = "zero" - vperp_ngrid = 3 #number of points per element - vperp_nelement_local = 3 # number of elements per rank + vperp_ngrid = 17 #number of points per element + vperp_nelement_local = 20 # number of elements per rank vperp_nelement_global = vperp_nelement_local # total number of elements vperp_L = 6.0 #physical box size in reference units bc = "zero" @@ -144,8 +155,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("made inputs") vpa = define_coordinate(vpa_input) vperp = define_coordinate(vperp_input) - println(vperp.grid) - println(vperp.wgts) + #println(vperp.grid) + #println(vperp.wgts) vpa_spectral = setup_chebyshev_pseudospectral(vpa) vperp_spectral = setup_chebyshev_pseudospectral(vperp) @@ -157,9 +168,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ ms = 1.0 msp = 1.0 - fkarrays = init_fokker_planck_collisions(vperp, vpa) Cssp = Array{mk_float,2}(undef,nvpa,nvperp) + Cssp_err = Array{mk_float,2}(undef,nvpa,nvperp) fs_in = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) @@ -167,69 +178,224 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_err = Array{mk_float,2}(undef,nvpa,nvperp) H_err = Array{mk_float,2}(undef,nvpa,nvperp) #H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vperp_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vperp_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vperp_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vpa_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vpa_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vpa_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = exp( - vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2 ) - G_Maxwell[ivpa,ivperp] = G_Maxwellian(vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(vpa,vperp,ivpa,ivperp) + if test_Rosenbluth_integrals + + fkarrays = init_fokker_planck_collisions(vperp, vpa) + + + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = exp( - vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2 ) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(vpa,vperp,ivpa,ivperp) + #Gam_vpa_Maxwell[ivpa,ivperp] = 0.0 + Gam_vperp_Maxwell[ivpa,ivperp] = 0.0 + end + end + + fsp_in = fs_in + + # evaluate the Rosenbluth potentials + @views calculate_Rosenbluth_potentials!(fkarrays.Rosenbluth_G, fkarrays.Rosenbluth_H, + fsp_in, fkarrays.elliptic_integral_E_factor,fkarrays.elliptic_integral_K_factor, + fkarrays.buffer_vpavperp_1,vperp,vpa) + + #@views calculate_Rosenbluth_H_from_G!(H_check,G_Maxwell, + # vpa,vpa_spectral,vperp,vperp_spectral,Bmag, + # fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) + @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) + @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) + #@. H_check_err = abs(H_check - H_Maxwell) + println("max(G_err)",maximum(G_err)) + println("max(H_err)",maximum(H_err)) + #println("max(H_check_err)",maximum(H_check_err)) + zero = 1.0e-3 + #println(G_Maxwell[41,:]) + #println(G_Maxwell[:,1]) + for ivperp in 1:nvperp + for ivpa in 1:nvpa + if (maximum(G_err[ivpa,ivperp]) > zero) + #println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) + #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) + #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) + end + #H_err[ivpa,ivperp] + end + end + #println(H_Maxwell[:,1]) + #println(fkarrays.Rosenbluth_H[:,1]) + #zero = 0.1 + for ivperp in 1:nvperp + for ivpa in 1:nvpa + if (maximum(H_err[ivpa,ivperp]) > zero) + ##println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) + #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) + end + #H_err[ivpa,ivperp] + end end end - fsp_in = fs_in + d2Gdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp = Array{mk_float,2}(undef,nvpa,nvperp) + dfn = Array{mk_float,2}(undef,nvpa,nvperp) + pdf_buffer_1 = Array{mk_float,2}(undef,nvpa,nvperp) + pdf_buffer_2 = Array{mk_float,2}(undef,nvpa,nvperp) + n_ion_species = 1 + dens_in = Array{mk_float,1}(undef,n_ion_species) + upar_in = Array{mk_float,1}(undef,n_ion_species) + vth_in = Array{mk_float,1}(undef,n_ion_species) - # evaluate the Rosenbluth potentials - @views calculate_Rosenbluth_potentials!(fkarrays.Rosenbluth_G, fkarrays.Rosenbluth_H, - fsp_in, fkarrays.elliptic_integral_E_factor,fkarrays.elliptic_integral_K_factor, - fkarrays.buffer_vpavperp_1,vperp,vpa) - - #@views calculate_Rosenbluth_H_from_G!(H_check,G_Maxwell, - # vpa,vpa_spectral,vperp,vperp_spectral,Bmag, - # fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) - @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) - @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) - #@. H_check_err = abs(H_check - H_Maxwell) - println("max(G_err)",maximum(G_err)) - println("max(H_err)",maximum(H_err)) - #println("max(H_check_err)",maximum(H_check_err)) - zero = 1.0e-3 - #println(G_Maxwell[41,:]) - #println(G_Maxwell[:,1]) + # 2D isotropic Maxwellian test + # assign a known isotropic Maxwellian distribution in normalised units + dens = 1.0#3.0/4.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + pperp = 2.0/3.0 + pres = get_pressure(ppar,pperp) + mass = 1.0 + vth = sqrt(pres/(dens*mass)) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + vpa_val = vpa.grid[ivpa] + vperp_val = vperp.grid[ivperp] + dfn[ivpa,ivperp] = (dens/vth^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vth^2 ) + end + end + pdf_in = dfn + dens_in[1] = get_density(dfn,vpa,vperp) + upar_in[1] = get_upar(dfn,vpa,vperp,dens_in[1]) + ppar_in = get_ppar(dfn,vpa,vperp,upar_in[1]) + pperp_in = get_pperp(dfn,vpa,vperp) + pres_in = pressure(ppar_in,pperp_in) + vth_in[1] = sqrt(pres_in/(dens_in[1]*mass)) + mi = mass + mip = mi + + println("Isotropic 2D Maxwellian") + println("dens_test: ", dens_in[1], " dens: ", dens, " error: ", abs(dens_in[1]-dens)) + println("upar_test: ", upar_in[1], " upar: ", upar, " error: ", abs(upar_in[1]-upar)) + println("vth_test: ", vth_in[1], " vth: ", vth, " error: ", abs(vth_in[1]-vth)) + + for ivperp in 1:nvperp for ivpa in 1:nvpa - if (maximum(G_err[ivpa,ivperp]) > zero) - #println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) - #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) - #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) - end - #H_err[ivpa,ivperp] + Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vth_in[1], + mip,dens_in[1],upar_in[1],vth_in[1], + vpa,vperp,ivpa,ivperp) end end - #println(H_Maxwell[:,1]) - #println(fkarrays.Rosenbluth_H[:,1]) - #zero = 0.1 + + # d F / d vpa + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, pdf_in[:,ivperp], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp] = vpa.scratch + end + # d F / d vperp + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, pdf_in[ivpa,:], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:] = vperp.scratch + end + for ivperp in 1:nvperp for ivpa in 1:nvpa - if (maximum(H_err[ivpa,ivperp]) > zero) - #println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) - println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) - end - #H_err[ivpa,ivperp] + ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian + ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, + Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[:], + upar_in[:],vth_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) + + # now form the collisional fluxes at this s,z,r + ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp], + pdf_buffer_1[ivpa,ivperp],pdf_buffer_2[ivpa,ivperp], + Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, + mi,mip,vperp.grid[ivperp]) ) + + + d2Gdvpa2[ivpa,ivperp] = Rosenbluth_d2Gdvpa2 + d2Gdvperpdvpa[ivpa,ivperp] = Rosenbluth_d2Gdvperpdvpa + d2Gdvperp2[ivpa,ivperp] = Rosenbluth_d2Gdvperp2 + dHdvpa[ivpa,ivperp] = Rosenbluth_dHdvpa + dHdvperp[ivpa,ivperp] = Rosenbluth_dHdvperp + Gam_vpa[ivpa,ivperp] = Cflux_vpa + Gam_vperp[ivpa,ivperp] = Cflux_vperp end end + @. Gam_vpa_err = abs(Gam_vpa - Gam_vpa_Maxwell) + @. Gam_vperp_err = abs(Gam_vperp - Gam_vperp_Maxwell) + max_Gam_vpa_err = maximum(Gam_vpa_err) + max_Gam_vperp_err = maximum(Gam_vperp_err) + println("max(Gam_vpa_err): ",max_Gam_vpa_err) + println("max(Gam_vperp_err): ",max_Gam_vperp_err) + + # d F / d vpa + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, Gam_vpa[:,ivperp], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp] = vpa.scratch + end + # d F / d vperp + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, Gam_vperp[ivpa,:], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:] = vperp.scratch/vperp.grid + end + + @. Cssp = pdf_buffer_1 + pdf_buffer_2 + @. Cssp_err = abs(Cssp) + max_Cssp_err = maximum(Cssp_err) + println("max(Cssp_err): ",max_Cssp_err) + + zero = 1.0e-6 + #if max_Gam_vpa_err > zero + @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Cssp.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa_err.pdf") + savefig(outfile) + #end + #if max_Gam_vperp_err > zero + @views heatmap(vperp.grid, vpa.grid, Gam_vperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp_err.pdf") + savefig(outfile) + #end + + ## evaluate the collision operator with numerically computed G & H #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6b099ae86..239e1edde 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -7,6 +7,8 @@ module fokker_planck export init_fokker_planck_collisions export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! +export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +export Cflux_vpa_Maxwellian_inputs using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int @@ -24,14 +26,14 @@ struct fokkerplanck_arrays_struct elliptic_integral_E_factor::Array{mk_float,4} elliptic_integral_K_factor::Array{mk_float,4} Rosenbluth_G::Array{mk_float,2} - Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} - Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} - Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} + #Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} + #Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} + #Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} Rosenbluth_H::Array{mk_float,2} - Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} - Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} - Cflux_vpa::MPISharedArray{mk_float,2} - Cflux_vperp::MPISharedArray{mk_float,2} + #Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} + #Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} + #Cflux_vpa::MPISharedArray{mk_float,2} + #Cflux_vperp::MPISharedArray{mk_float,2} buffer_vpavperp_1::Array{mk_float,2} buffer_vpavperp_2::Array{mk_float,2} #Cssp_result_vpavperp::Array{mk_float,2} @@ -48,22 +50,22 @@ function allocate_fokkerplanck_arrays(vperp,vpa) elliptic_integral_E_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) elliptic_integral_K_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) Rosenbluth_G = allocate_float(nvpa,nvperp) - Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) - Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) - Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) Rosenbluth_H = allocate_float(nvpa,nvperp) - Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) - Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) - Cflux_vpa = allocate_shared_float(nvpa,nvperp) - Cflux_vperp = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) + #Cflux_vpa = allocate_shared_float(nvpa,nvperp) + #Cflux_vperp = allocate_shared_float(nvpa,nvperp) buffer_vpavperp_1 = allocate_float(nvpa,nvperp) buffer_vpavperp_2 = allocate_float(nvpa,nvperp) #Cssp_result_vpavperp = allocate_float(nvpa,nvperp) return fokkerplanck_arrays_struct(elliptic_integral_E_factor,elliptic_integral_K_factor, - Rosenbluth_G,Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, - Rosenbluth_H,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - Cflux_vpa,Cflux_vperp, + Rosenbluth_G,#Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, + Rosenbluth_H,#Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, + #Cflux_vpa,Cflux_vperp, buffer_vpavperp_1,buffer_vpavperp_2) #Cssp_result_vpavperp) end @@ -181,6 +183,8 @@ function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, ms,msp,vperp_val) # fill in value at (ivpa,ivperp) Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa - 2.0*(ms/msp)*F*dHdvpa + #Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa # - 2.0*(ms/msp)*F*dHdvpa + #Cflux_vpa = - 2.0*(ms/msp)*F*dHdvpa Cflux_vperp = vperp_val* ( dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp ) return Cflux_vpa, Cflux_vperp end @@ -355,21 +359,24 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ @loop_s_r_z is ir iz begin @loop_vperp_vpa ivperp ivpa begin # first compute local (in z,r) Rosenbluth potential coefficients, summing over all s' - ( (fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp], fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], - fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp], - fk.Rosenbluth_dHdvperp[ivpa,ivperp]) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[iz,ir,:], + # ((fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp], fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], + #fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp], + #fk.Rosenbluth_dHdvperp[ivpa,ivperp]) + ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, + Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[iz,ir,:], upar_in[iz,ir,:],vth_in[iz,ir,:],vpa,vperp,ivpa,ivperp,n_ion_species) ) # now form the collisional fluxes at this s,z,r - ( (fk.Cflux_vpa[ivpa,ivperp],fk.Cflux_vperp[ivpa,ivperp]) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp,iz,ir,is], + ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp,iz,ir,is], pdf_buffer_1[ivpa,ivperp,iz,ir,is],pdf_buffer_2[ivpa,ivperp,iz,ir,is], - fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp],fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], - fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp],fk.Rosenbluth_dHdvperp[ivpa,ivperp], + Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, mi,mip,vperp.grid[ivperp]) ) # now overwrite the buffer arrays with the local values as we no longer need dFdvpa or dFdvperp at s,r,z - pdf_buffer_1[ivpa,ivperp,iz,ir,is] = fk.Cflux_vpa[ivpa,ivperp] - pdf_buffer_2[ivpa,ivperp,iz,ir,is] = fk.Cflux_vperp[ivpa,ivperp] + pdf_buffer_1[ivpa,ivperp,iz,ir,is] = Cflux_vpa + pdf_buffer_2[ivpa,ivperp,iz,ir,is] = Cflux_vperp end end @@ -409,6 +416,12 @@ function dGdeta(eta::mk_float) return dGdeta_fac end +function d2Gdeta2(eta::mk_float) + # d \tilde{G} / d eta + d2Gdeta2_fac = erf(eta)/(eta^3) - (2.0/sqrt(pi))*exp(-eta^2)/(eta^2) + return d2Gdeta2_fac +end + function ddGddeta(eta::mk_float) # d / d eta ( (1/ eta) d \tilde{G} d eta ddGddeta_fac = (1.5/(eta^2) - 1.0)*erf(eta)/(eta^2) - (3.0/sqrt(pi))*exp(-eta^2)/(eta^3) @@ -472,4 +485,16 @@ function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, return fac end -end \ No newline at end of file +function Cflux_vpa_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, + msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, + vpa,vperp,ivpa,ivperp) + etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) + eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) + fac = -2.0*denss*denssp*(vpa.grid[ivpa]-upars)*exp( -eta^2)/(vthsp*vths^5) + fac *= (d2Gdeta2(etap) + (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap) + #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap + #fac *= d2Gdeta2(etap) + return fac +end + +end From cf7990f2108b2156f3d2f14f1aac34de32f2a160 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 31 May 2023 08:23:39 +0100 Subject: [PATCH 025/331] Switch off costly initialisation of (as yet unused) arrays, and modify constant ionisation source --- src/fokker_planck.jl | 2 +- src/ionization.jl | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 239e1edde..df984e63a 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -121,7 +121,7 @@ function that initialises the arrays needed for Fokker Planck collisions function init_fokker_planck_collisions(vperp,vpa) fokkerplanck_arrays = allocate_fokkerplanck_arrays(vperp,vpa) - if vperp.n > 1 + if vperp.n > 1 && false @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, fokkerplanck_arrays.elliptic_integral_K_factor, vperp,vpa) diff --git a/src/ionization.jl b/src/ionization.jl index fdf307f81..c9e894fd9 100644 --- a/src/ionization.jl +++ b/src/ionization.jl @@ -24,12 +24,23 @@ function constant_ionization_source!(f_out, vpa, vperp, z, r, composition, colli # e.g., width=0.15. Possibly narrower widths would require more vpa # resolution, which then causes crashes due to overshoots giving # negative f?? - width = 0.5 + width = 1.0 + zwidth = 0.25 rwidth = 0.25 + vperpwidth = 1.0 + if vperp.n > 1 + prefac = 1.0/vperpwidth^2 + else + prefac = 1.0 + end + # loop below relies on vperp[1] = 0 when vperp.n = 1 @loop_s is begin - @loop_r_z_vpa ir iz ivpa begin + @loop_r_z_vperp_vpa ir iz ivperp ivpa begin + zfac = exp( - (z.grid[iz]/zwidth)^2) rfac = exp( - (r.grid[ir]/rwidth)^2) - f_out[ivpa,1,iz,ir,is] += dt*rfac*collisions.ionization/width*exp(-(vpa.grid[ivpa]/width)^2) + vperpfac = exp( - (vperp.grid[ivperp]/vperpwidth)^2) + vpafac = vpa.grid[ivpa]^2 + f_out[ivpa,ivperp,iz,ir,is] += dt*prefac*rfac*zfac*vperpfac*vpafac*collisions.ionization/width*exp(-(vpa.grid[ivpa]/width)^2) end end From a60fe8ec64c7a9ea8f5a37cb371668f39fb09b35 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 31 May 2023 09:20:03 +0100 Subject: [PATCH 026/331] Modification of source to avoid buildup of particles at z = 0, and corresponding input file for a 1D2V simulation that shows reasonably smooth behaviour with Krook+Modified Fokker-Planck operator C[F,F_Maxwellian]. --- ...volve_nel_r_1_z_8_vpa_8_vperp_8_pitch.toml | 94 +++++++++++++++++++ src/ionization.jl | 4 +- 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 runs/1D-wall_evolve_nel_r_1_z_8_vpa_8_vperp_8_pitch.toml diff --git a/runs/1D-wall_evolve_nel_r_1_z_8_vpa_8_vperp_8_pitch.toml b/runs/1D-wall_evolve_nel_r_1_z_8_vpa_8_vperp_8_pitch.toml new file mode 100644 index 000000000..2baf43f42 --- /dev/null +++ b/runs/1D-wall_evolve_nel_r_1_z_8_vpa_8_vperp_8_pitch.toml @@ -0,0 +1,94 @@ +use_manufactured_solns_for_init = true +use_manufactured_solns_for_advance = false +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +run_name = "1D-wall_evolve_nel_r_1_z_8_vpa_8_vperp_8_pitch" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 1.0 +constant_ionization_rate = true +nuii_krook = 1.0 +nuii_pitch = 10.0 +nstep = 10000 +dt = 0.0005 +nwrite = 200 +nwrite_dfns = 200 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 5 +z_nelement = 8 +z_nelement_local = 8 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 17 +vpa_nelement = 8 +vpa_L = 6.0 +vpa_bc = "zero" +vpa_discretization = "chebyshev_pseudospectral" +vperp_ngrid = 17 +vperp_nelement = 8 +vperp_L = 3.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +vperp_discretization = "chebyshev_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 diff --git a/src/ionization.jl b/src/ionization.jl index c9e894fd9..11b558826 100644 --- a/src/ionization.jl +++ b/src/ionization.jl @@ -36,10 +36,10 @@ function constant_ionization_source!(f_out, vpa, vperp, z, r, composition, colli # loop below relies on vperp[1] = 0 when vperp.n = 1 @loop_s is begin @loop_r_z_vperp_vpa ir iz ivperp ivpa begin - zfac = exp( - (z.grid[iz]/zwidth)^2) + zfac = ((z.grid[iz]/zwidth)^2)*exp( - (z.grid[iz]/zwidth)^2) rfac = exp( - (r.grid[ir]/rwidth)^2) vperpfac = exp( - (vperp.grid[ivperp]/vperpwidth)^2) - vpafac = vpa.grid[ivpa]^2 + vpafac = 1.0 #vpa.grid[ivpa]^2 f_out[ivpa,ivperp,iz,ir,is] += dt*prefac*rfac*zfac*vperpfac*vpafac*collisions.ionization/width*exp(-(vpa.grid[ivpa]/width)^2) end end From 5092c10b906225c40577a7097714085065968bee Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 31 May 2023 13:02:34 +0100 Subject: [PATCH 027/331] Added masses to lowest level calculation of ppar, pperp, vth, for use with the collision operator test, and later use throughout the code when different species masses are supported. --- src/velocity_moments.jl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 65919452b..c436326f1 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -256,17 +256,18 @@ function update_ppar_species!(ppar, ff, vpa, vperp, z, r, upar) @boundscheck r.n == size(ff, 4) || throw(BoundsError(ff)) @boundscheck z.n == size(ppar, 1) || throw(BoundsError(ppar)) @boundscheck r.n == size(ppar, 2) || throw(BoundsError(ppar)) + mass = 1.0 # only reference mass currently supported for all species @loop_r_z ir iz begin #ppar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), # vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) - ppar[iz,ir] = get_ppar(@view(ff[:,:,iz,ir]), vpa, vperp, upar[iz,ir]) + ppar[iz,ir] = get_ppar(@view(ff[:,:,iz,ir]), vpa, vperp, upar[iz,ir], mass) end return nothing end -function get_ppar(ff, vpa, vperp, upar) +function get_ppar(ff, vpa, vperp, upar, mass) @. vpa.scratch = vpa.grid - upar - return 2.0*integrate_over_vspace(@view(ff[:,:]), vpa.scratch, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) + return 2.0*mass*integrate_over_vspace(@view(ff[:,:]), vpa.scratch, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) end function update_pperp!(pperp, pdf, vpa, vperp, z, r, composition) @@ -291,31 +292,32 @@ function update_pperp_species!(pperp, ff, vpa, vperp, z, r) @boundscheck r.n == size(ff, 4) || throw(BoundsError(ff)) @boundscheck z.n == size(pperp, 1) || throw(BoundsError(pperp)) @boundscheck r.n == size(pperp, 2) || throw(BoundsError(pperp)) + mass = 1.0 # only reference mass currently supported for all species @loop_r_z ir iz begin - pperp[iz,ir] = get_pperp(@view(ff[:,:,iz,ir]), vpa, vperp) + pperp[iz,ir] = get_pperp(@view(ff[:,:,iz,ir]), vpa, vperp, mass) end return nothing end -function get_pperp(ff, vpa, vperp) - return integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 2, vperp.wgts) +function get_pperp(ff, vpa, vperp, mass) + return mass*integrate_over_vspace(@view(ff[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 2, vperp.wgts) end function update_vth!(vth, ppar, pperp, dens, vperp, z, r, composition) @boundscheck composition.n_ion_species == size(vth,3) || throw(BoundsError(vth)) @boundscheck r.n == size(vth,2) || throw(BoundsError(vth)) @boundscheck z.n == size(vth,1) || throw(BoundsError(vth)) - + mass = 1.0 # only reference mass currently supported for all species begin_s_r_z_region() if vperp.n > 1 #2V definition @loop_s_r_z is ir iz begin piso = get_pressure(ppar[iz,ir,is],pperp[iz,ir,is]) - vth[iz,ir,is] = sqrt(piso/dens[iz,ir,is]) + vth[iz,ir,is] = sqrt(piso/(mass*dens[iz,ir,is])) end else #1V definition @loop_s_r_z is ir iz begin - vth[iz,ir,is] = sqrt(ppar[iz,ir,is]/dens[iz,ir,is]) + vth[iz,ir,is] = sqrt(ppar[iz,ir,is]/(mass*dens[iz,ir,is])) end end end From c4b7fec46b6c5702343520d7d7d35467c48cd624 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 31 May 2023 13:04:37 +0100 Subject: [PATCH 028/331] Added test for collision operator C[F,F_Maxwellian] fluxes Cflux_vpa and Cflux_vperp for the case where F is also a Maxwellian. This test demonstrates a correct implementation when the fluxes are nonzero because the Maxwellians have different moments. --- fkpl_test.jl | 92 ++++++++++++++++++++++++++++++++------------ src/fokker_planck.jl | 24 ++++++++++-- 2 files changed, 87 insertions(+), 29 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 9174bde56..59ca3e443 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -116,7 +116,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients - using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs + using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -128,12 +128,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test vpa_ngrid = 17 #number of points per element - vpa_nelement_local = 20 # number of elements per rank + vpa_nelement_local = 8 # number of elements per rank vpa_nelement_global = vpa_nelement_local # total number of elements vpa_L = 12.0 #physical box size in reference units bc = "zero" vperp_ngrid = 17 #number of points per element - vperp_nelement_local = 20 # number of elements per rank + vperp_nelement_local = 8 # number of elements per rank vperp_nelement_global = vperp_nelement_local # total number of elements vperp_L = 6.0 #physical box size in reference units bc = "zero" @@ -202,7 +202,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_Maxwell[ivpa,ivperp] = G_Maxwellian(vpa,vperp,ivpa,ivperp) H_Maxwell[ivpa,ivperp] = H_Maxwellian(vpa,vperp,ivpa,ivperp) #Gam_vpa_Maxwell[ivpa,ivperp] = 0.0 - Gam_vperp_Maxwell[ivpa,ivperp] = 0.0 + #Gam_vperp_Maxwell[ivpa,ivperp] = 0.0 end end @@ -257,50 +257,84 @@ if abspath(PROGRAM_FILE) == @__FILE__ Gam_vpa = Array{mk_float,2}(undef,nvpa,nvperp) Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) Gam_vperp = Array{mk_float,2}(undef,nvpa,nvperp) - dfn = Array{mk_float,2}(undef,nvpa,nvperp) + dfns = Array{mk_float,2}(undef,nvpa,nvperp) + dfnsp = Array{mk_float,2}(undef,nvpa,nvperp) pdf_buffer_1 = Array{mk_float,2}(undef,nvpa,nvperp) pdf_buffer_2 = Array{mk_float,2}(undef,nvpa,nvperp) n_ion_species = 1 dens_in = Array{mk_float,1}(undef,n_ion_species) upar_in = Array{mk_float,1}(undef,n_ion_species) - vth_in = Array{mk_float,1}(undef,n_ion_species) + vths_in = Array{mk_float,1}(undef,n_ion_species) + densp_in = Array{mk_float,1}(undef,n_ion_species) + uparp_in = Array{mk_float,1}(undef,n_ion_species) + vthsp_in = Array{mk_float,1}(undef,n_ion_species) + + function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) + end # 2D isotropic Maxwellian test # assign a known isotropic Maxwellian distribution in normalised units + # first argument (derivatives) dens = 1.0#3.0/4.0 upar = 2.0/3.0 ppar = 2.0/3.0 pperp = 2.0/3.0 pres = get_pressure(ppar,pperp) - mass = 1.0 - vth = sqrt(pres/(dens*mass)) + mi = 1.0 + vths = get_vth(pres,dens,mi) + # second argument (potentials) + densp = 2.0#3.0/4.0 + uparp = 1.0/3.0#3.0/4.0 + pparp = 4.0/3.0 + pperpp = 4.0/3.0 + presp = get_pressure(pparp,pperpp) + mip = 1.5 + vthsp = get_vth(presp,densp,mip) for ivperp in 1:vperp.n for ivpa in 1:vpa.n vpa_val = vpa.grid[ivpa] vperp_val = vperp.grid[ivperp] - dfn[ivpa,ivperp] = (dens/vth^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vth^2 ) + dfns[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vths^2 ) + dfnsp[ivpa,ivperp] = (densp/vthsp^3)*exp( - ((vpa_val-uparp)^2 + vperp_val^2)/vthsp^2 ) end end - pdf_in = dfn - dens_in[1] = get_density(dfn,vpa,vperp) - upar_in[1] = get_upar(dfn,vpa,vperp,dens_in[1]) - ppar_in = get_ppar(dfn,vpa,vperp,upar_in[1]) - pperp_in = get_pperp(dfn,vpa,vperp) + pdf_in = dfns + dens_in[1] = get_density(dfns,vpa,vperp) + upar_in[1] = get_upar(dfns,vpa,vperp,dens_in[1]) + ppar_in = get_ppar(dfns,vpa,vperp,upar_in[1],mi) + pperp_in = get_pperp(dfns,vpa,vperp,mi) pres_in = pressure(ppar_in,pperp_in) - vth_in[1] = sqrt(pres_in/(dens_in[1]*mass)) - mi = mass - mip = mi - - println("Isotropic 2D Maxwellian") + vths_in[1] = get_vth(pres_in,dens_in[1],mi) + + densp_in[1] = get_density(dfnsp,vpa,vperp) + uparp_in[1] = get_upar(dfnsp,vpa,vperp,densp_in[1]) + pparp_in = get_ppar(dfnsp,vpa,vperp,uparp_in[1],mip) + pperpp_in = get_pperp(dfnsp,vpa,vperp,mip) + presp_in = pressure(pparp_in,pperpp_in) + vthsp_in[1] = get_vth(presp_in,densp_in[1],mip) + + println("Isotropic 2D Maxwellian: first argument (derivatives)") println("dens_test: ", dens_in[1], " dens: ", dens, " error: ", abs(dens_in[1]-dens)) println("upar_test: ", upar_in[1], " upar: ", upar, " error: ", abs(upar_in[1]-upar)) - println("vth_test: ", vth_in[1], " vth: ", vth, " error: ", abs(vth_in[1]-vth)) + println("ppar_test: ", ppar_in, " ppar: ", ppar, " error: ", abs(ppar_in-ppar)) + println("pperp_test: ", pperp_in, " pperp: ", pperp, " error: ", abs(pperp_in-pperp)) + println("vth_test: ", vths_in[1], " vth: ", vths, " error: ", abs(vths_in[1]-vths)) + println("Isotropic 2D Maxwellian: second argument (potentials)") + println("dens_test: ", densp_in[1], " dens: ", densp, " error: ", abs(densp_in[1]-densp)) + println("upar_test: ", uparp_in[1], " upar: ", uparp, " error: ", abs(uparp_in[1]-uparp)) + println("ppar_test: ", pparp_in, " ppar: ", pparp, " error: ", abs(pparp_in-pparp)) + println("pperp_test: ", pperpp_in, " pperp: ", pperpp, " error: ", abs(pperpp_in-pperpp)) + println("vth_test: ", vthsp_in[1], " vth: ", vthsp, " error: ", abs(vthsp_in[1]-vthsp)) for ivperp in 1:nvperp for ivpa in 1:nvpa - Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vth_in[1], - mip,dens_in[1],upar_in[1],vth_in[1], + Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], + mip,densp_in[1],uparp_in[1],vthsp_in[1], + vpa,vperp,ivpa,ivperp) + Gam_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], + mip,densp_in[1],uparp_in[1],vthsp_in[1], vpa,vperp,ivpa,ivperp) end end @@ -321,8 +355,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, - Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[:], - upar_in[:],vth_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) + Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(densp_in[:], + uparp_in[:],vthsp_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) # now form the collisional fluxes at this s,z,r ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp], @@ -363,9 +397,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Cssp = pdf_buffer_1 + pdf_buffer_2 @. Cssp_err = abs(Cssp) max_Cssp_err = maximum(Cssp_err) + zero = 1.0e-6 + if ( abs(dens - densp) > zero || abs(upar - uparp) > zero || abs(vths - vthsp) > zero) + println("Cssp test not supported for F_Ms /= F_Ms', ignore result") + end println("max(Cssp_err): ",max_Cssp_err) - zero = 1.0e-6 + #if max_Gam_vpa_err > zero @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -389,6 +427,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Gam_vperp.pdf") savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp_Maxwell.pdf") + savefig(outfile) @views heatmap(vperp.grid, vpa.grid, Gam_vperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Gam_vperp_err.pdf") diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index df984e63a..6ee010fb0 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -8,7 +8,7 @@ export init_fokker_planck_collisions export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients -export Cflux_vpa_Maxwellian_inputs +export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int @@ -490,11 +490,27 @@ function Cflux_vpa_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_floa vpa,vperp,ivpa,ivperp) etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) - fac = -2.0*denss*denssp*(vpa.grid[ivpa]-upars)*exp( -eta^2)/(vthsp*vths^5) - fac *= (d2Gdeta2(etap) + (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap) + prefac = -2.0*denss*denssp*exp( -eta^2)/(vthsp*vths^5) + (fac = (vpa.grid[ivpa]-uparsp)*(d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) + + (uparsp - upars)*( dGdeta(etap) + ((vpa.grid[ivpa]-uparsp)^2/vthsp^2)*ddGddeta(etap) )/etap ) + Cflux = prefac*fac #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap #fac *= d2Gdeta2(etap) - return fac + return Cflux +end + +function Cflux_vperp_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, + msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, + vpa,vperp,ivpa,ivperp) + etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) + eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) + prefac = -2.0*(vperp.grid[ivperp]^2)*denss*denssp*exp( -eta^2)/(vthsp*vths^5) + (fac = (d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) + + ((uparsp - upars)*(vpa.grid[ivpa]-uparsp)/vthsp^2)*ddGddeta(etap)/etap ) + Cflux = prefac*fac + #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap + #fac *= d2Gdeta2(etap) + return Cflux end end From 689c5ec3d05a955277067503e924f23052bbf68f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 6 Jun 2023 09:59:30 +0100 Subject: [PATCH 029/331] Corrected spelling error -> . --- src/chebyshev.jl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index b3c67ee7f..90a29ac06 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -38,7 +38,7 @@ struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs end struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} - lobotto::chebyshev_base_info{TForward, TBackward} + lobatto::chebyshev_base_info{TForward, TBackward} radau::chebyshev_base_info{TForward, TBackward} end @@ -47,12 +47,12 @@ create arrays needed for explicit Chebyshev pseudospectral treatment and create the plans for the forward and backward fast Fourier transforms """ function setup_chebyshev_pseudospectral(coord) - lobotto = setup_chebyshev_pseudospectral_lobotto(coord) + lobatto = setup_chebyshev_pseudospectral_lobatto(coord) radau = setup_chebyshev_pseudospectral_radau(coord) - return chebyshev_info(lobotto,radau) + return chebyshev_info(lobatto,radau) end -function setup_chebyshev_pseudospectral_lobotto(coord) +function setup_chebyshev_pseudospectral_lobatto(coord) # ngrid_fft is the number of grid points in the extended domain # in z = cos(theta). this is necessary to turn a cosine transform on [0,π] # into a complex transform on [0,2π], which is more efficient in FFTW @@ -185,7 +185,7 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds - @boundscheck nelement == size(chebyshev.lobotto.f,2) || throw(BoundsError(chebyshev.lobotto.f)) + @boundscheck nelement == size(chebyshev.lobatto.f,2) || throw(BoundsError(chebyshev.lobatto.f)) @boundscheck nelement == size(chebyshev.radau.f,2) || throw(BoundsError(chebyshev.radau.f)) @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) # note that one must multiply by 2*nelement/L to get derivative @@ -208,12 +208,12 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) for i ∈ 1:coord.ngrid df[i,j] *= scale_factor end - else #differentiate using the Lobotto scheme + else #differentiate using the Lobatto scheme imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.lobotto.f[:,j], chebyshev.lobotto.df, chebyshev.lobotto.fext, chebyshev.lobotto.forward, coord) + chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) # and multiply by scaling factor needed to go # from Chebyshev z coordinate to actual z for i ∈ 1:coord.ngrid @@ -233,7 +233,7 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.lobotto.f[:,j], chebyshev.lobotto.df, chebyshev.lobotto.fext, chebyshev.lobotto.forward, coord) + chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) # and multiply by scaling factor needed to go # from Chebyshev z coordinate to actual z for i ∈ 1:coord.ngrid @@ -412,7 +412,7 @@ function chebyshev_interpolate_single_element(newgrid, f, j, coord, chebyshev) scale = 2.0 / (coord.grid[imax] - coord.grid[imin]) # Get Chebyshev coefficients - chebyshev_forward_transform!(cheby_f, chebyshev.lobotto.fext, f, chebyshev.lobotto.forward, coord.ngrid) + chebyshev_forward_transform!(cheby_f, chebyshev.lobatto.fext, f, chebyshev.lobatto.forward, coord.ngrid) for (i, x) ∈ enumerate(newgrid) z = scale * (x - shift) @@ -460,7 +460,7 @@ function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, sca wgts = zeros(mk_float, n) # calculate the modified Chebshev moments of the first kind μ = chebyshevmoments(ngrid) - wgts_lobotto = clenshawcurtisweights(μ)*scale_factor + wgts_lobatto = clenshawcurtisweights(μ)*scale_factor wgts_radau = chebyshev_radau_weights(μ, ngrid)*scale_factor @inbounds begin # calculate the weights within a single element and @@ -469,9 +469,9 @@ function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, sca if nelement_local > 1 for j ∈ 2:nelement_local # account for double-counting of points at inner element boundaries - wgts[imin[j]-1] += wgts_lobotto[1] + wgts[imin[j]-1] += wgts_lobatto[1] # assign weights for interior of elements and one boundary point - wgts[imin[j]:imax[j]] .= wgts_lobotto[2:ngrid] + wgts[imin[j]:imax[j]] .= wgts_lobatto[2:ngrid] end end end From 3d643aac7406ea98b6b1aa578462d62b0a576fab Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 6 Jun 2023 12:20:35 +0100 Subject: [PATCH 030/331] Add an option < cheb_option > which takes either the string values or . This option switches between the two methods of taking the Chebyshev derivative, by either taking the FFT in place (previous method) or mutiplying by a matrix that acts directly on the function at the collocation points. Change which option is used by changing cheb_option in moment_kinetics_inputs.jl. The tests are upgraded to be able to test the option specified in the script. Tests which simultaneously cover both options could be desirable. The methods show comparable speed at runtime. --- src/chebyshev.jl | 254 +++++++++++++++++++++++------ src/coordinates.jl | 4 +- src/input_structs.jl | 4 + src/moment_kinetics_input.jl | 50 ++++-- src/time_advance.jl | 28 ++-- test/calculus_tests.jl | 32 ++-- test/interpolation_tests.jl | 3 +- test/nonlinear_sound_wave_tests.jl | 7 +- test/wall_bc_tests.jl | 3 +- 9 files changed, 288 insertions(+), 97 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index 90a29ac06..9e6a14abb 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -12,6 +12,7 @@ export chebyshev_spectral_derivative! export chebyshev_info export chebyshev_base_info +using LinearAlgebra: mul! using FFTW using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_complex @@ -33,8 +34,10 @@ struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs # plan for the complex-to-complex, in-place, forward Fourier transform on Chebyshev-Gauss-Lobatto/Radau grid forward::TForward # plan for the complex-to-complex, in-place, backward Fourier transform on Chebyshev-Gauss-Lobatto/Radau grid - #backward_transform::FFTW.cFFTWPlan + # backward_transform::FFTW.cFFTWPlan backward::TBackward + # elementwise differentiation matrix (ngrid*ngrid) + Dmat::Array{mk_float,2} end struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} @@ -65,9 +68,12 @@ function setup_chebyshev_pseudospectral_lobatto(coord) # setup the plans for the forward and backward Fourier transforms forward_transform = plan_fft!(fext, flags=FFTW.MEASURE) backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) + # create array for differentiation matrix + Dmat = allocate_float(coord.ngrid, coord.ngrid) + cheb_derivative_matrix_elementwise!(Dmat,coord.ngrid,coord.L,coord.nelement_global) # return a structure containing the information needed to carry out # a 1D Chebyshev transform - return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform) + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) end function setup_chebyshev_pseudospectral_radau(coord) @@ -83,9 +89,12 @@ function setup_chebyshev_pseudospectral_radau(coord) # setup the plans for the forward and backward Fourier transforms forward_transform = plan_fft!(fext, flags=FFTW.MEASURE) backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) + # create array for differentiation matrix + Dmat = allocate_float(coord.ngrid, coord.ngrid) + cheb_derivative_matrix_elementwise_FFT!(Dmat, coord, fcheby, dcheby, fext, forward_transform) # return a structure containing the information needed to carry out # a 1D Chebyshev transform - return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform) + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) end """ @@ -188,57 +197,89 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) @boundscheck nelement == size(chebyshev.lobatto.f,2) || throw(BoundsError(chebyshev.lobatto.f)) @boundscheck nelement == size(chebyshev.radau.f,2) || throw(BoundsError(chebyshev.radau.f)) @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) - # note that one must multiply by 2*nelement/L to get derivative - # in scaled coordinate - scale_factor = 2.0*float(coord.nelement_global)/coord.L - # scale factor is (length of a single element/2)^{-1} - - # variable k will be used to avoid double counting of overlapping point - # at element boundaries (see below for further explanation) - k = 0 - j = 1 # the first element - if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + + if coord.cheb_option == "matrix" + # variable k will be used to avoid double counting of overlapping point + # at element boundaries (see below for further explanation) + k = 0 + j = 1 # the first element imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element - imax = coord.imax[j] - @views chebyshev_radau_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.radau.f[:,j], chebyshev.radau.df, chebyshev.radau.fext, chebyshev.radau.forward, coord) - # and multiply by scaling factor needed to go - # from Chebyshev z coordinate to actual z - for i ∈ 1:coord.ngrid - df[i,j] *= scale_factor + imax = coord.imax[j] + if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + @views mul!(df[:,j],chebyshev.radau.Dmat[:,:],ff[imin:imax]) + else #differentiate using the Lobatto scheme + @views mul!(df[:,j],chebyshev.lobatto.Dmat[:,:],ff[imin:imax]) end - else #differentiate using the Lobatto scheme - imin = coord.imin[j]-k - # imax is the maximum index on the full grid for this (jth) element - imax = coord.imax[j] - @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) - # and multiply by scaling factor needed to go - # from Chebyshev z coordinate to actual z - for i ∈ 1:coord.ngrid - df[i,j] *= scale_factor + # calculate the Chebyshev derivative on each element + @inbounds for j ∈ 2:nelement + # imin is the minimum index on the full grid for this (jth) element + # the 'k' below accounts for the fact that the first element includes + # both boundary points, while each additional element shares a boundary + # point with neighboring elements. the choice was made when defining + # coord.imin to exclude the lower boundary point in each element other + # than the first so that no point is double-counted + k = 1 + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views mul!(df[:,j],chebyshev.lobatto.Dmat[:,:],ff[imin:imax]) end - end - # calculate the Chebyshev derivative on each element - @inbounds for j ∈ 2:nelement - # imin is the minimum index on the full grid for this (jth) element - # the 'k' below accounts for the fact that the first element includes - # both boundary points, while each additional element shares a boundary - # point with neighboring elements. the choice was made when defining - # coord.imin to exclude the lower boundary point in each element other - # than the first so that no point is double-counted - k = 1 - imin = coord.imin[j]-k - # imax is the maximum index on the full grid for this (jth) element - imax = coord.imax[j] - @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], - chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) - # and multiply by scaling factor needed to go - # from Chebyshev z coordinate to actual z - for i ∈ 1:coord.ngrid - df[i,j] *= scale_factor - end + elseif coord.cheb_option == "FFT" + # note that one must multiply by 2*nelement/L to get derivative + # in scaled coordinate + scale_factor = 2.0*float(coord.nelement_global)/coord.L + # scale factor is (length of a single element/2)^{-1} + + # variable k will be used to avoid double counting of overlapping point + # at element boundaries (see below for further explanation) + k = 0 + j = 1 # the first element + if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views chebyshev_radau_derivative_single_element!(df[:,j], ff[imin:imax], + chebyshev.radau.f[:,j], chebyshev.radau.df, chebyshev.radau.fext, chebyshev.radau.forward, coord) + # and multiply by scaling factor needed to go + # from Chebyshev z coordinate to actual z + for i ∈ 1:coord.ngrid + df[i,j] *= scale_factor + end + else #differentiate using the Lobatto scheme + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], + chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) + # and multiply by scaling factor needed to go + # from Chebyshev z coordinate to actual z + for i ∈ 1:coord.ngrid + df[i,j] *= scale_factor + end + end + # calculate the Chebyshev derivative on each element + @inbounds for j ∈ 2:nelement + # imin is the minimum index on the full grid for this (jth) element + # the 'k' below accounts for the fact that the first element includes + # both boundary points, while each additional element shares a boundary + # point with neighboring elements. the choice was made when defining + # coord.imin to exclude the lower boundary point in each element other + # than the first so that no point is double-counted + k = 1 + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views chebyshev_derivative_single_element!(df[:,j], ff[imin:imax], + chebyshev.lobatto.f[:,j], chebyshev.lobatto.df, chebyshev.lobatto.fext, chebyshev.lobatto.forward, coord) + # and multiply by scaling factor needed to go + # from Chebyshev z coordinate to actual z + for i ∈ 1:coord.ngrid + df[i,j] *= scale_factor + end + end + else + println("ERROR: ", coord.cheb_option, " NOT SUPPORTED") end return nothing end @@ -256,6 +297,7 @@ function chebyshev_derivative_single_element!(df, ff, cheby_f, cheby_df, cheby_f end + """ Chebyshev transform f to get Chebyshev spectral coefficients """ @@ -708,4 +750,116 @@ function chebyshev_radau_forward_transform!(chebyf, fext, ff, transform, n) chebyshev_radau_backward_transform!(df, cheby_fext, cheby_df, forward, coord.ngrid) end + +""" +derivative matrix for Gauss-Lobatto points using the analytical specification from +Chapter 8.2 from Trefethen 1994 +https://people.maths.ox.ac.uk/trefethen/8all.pdf +full list of Chapters may be obtained here +https://people.maths.ox.ac.uk/trefethen/pdetext.html +""" + function cheb_derivative_matrix_elementwise!(D::Array{Float64,2},n::Int64,L::Float64,nelement::Int64) + + # define Gauss-Lobatto Chebyshev points in reversed order x_j = { -1, ... , 1} + # consistent with use in elements of the grid + x = Array{Float64,1}(undef,n) + for j in 1:n + x[j] = cospi((n-j)/(n-1)) + end + + # zero matrix before allocating values + D[:,:] .= 0.0 + + # top row + j = 1 + c_j = 2.0 + c_k = 1.0 + for k in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + k = n + c_k = 2.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + + # bottom row + j = n + c_j = 2.0 + c_k = 1.0 + for k in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + k = 1 + c_k = 2.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + + #left column + k = 1 + c_j = 1.0 + c_k = 2.0 + for j in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + + #right column + k = n + c_j = 1.0 + c_k = 2.0 + for j in 2:n-1 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + + + # top left, bottom right + #D[n,n] = (2.0*(n - 1.0)^2 + 1.0)/6.0 + #D[1,1] = -(2.0*(n - 1.0)^2 + 1.0)/6.0 + # interior rows and columns + for j in 2:n-1 + #D[j,j] = Djj(x,j) + for k in 2:n-1 + if j == k + continue + end + c_k = 1.0 + c_j = 1.0 + D[j,k] = Djk(x,j,k,c_j,c_k) + end + end + + # calculate diagonal entries to guarantee that + # D * (1, 1, ..., 1, 1) = (0, 0, ..., 0, 0) + for j in 1:n + D[j,j] = -sum(D[j,:]) + end + + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + end + function Djk(x::Array{Float64,1},j::Int64,k::Int64,c_j::Float64,c_k::Float64) + return (c_j/c_k)*((-1)^(k+j))/(x[j] - x[k]) + end + """ + derivative matrix for Chebyshev grid using the FFT + """ + function cheb_derivative_matrix_elementwise_FFT!(D::Array{Float64,2}, coord, f, df, fext, forward) + ff_buffer = Array{Float64,1}(undef,coord.ngrid) + df_buffer = Array{Float64,1}(undef,coord.ngrid) + # use response matrix approach to calculate derivative matrix D + for j in 1:coord.ngrid + ff_buffer .= 0.0 + ff_buffer[j] = 1.0 + @views chebyshev_radau_derivative_single_element!(df_buffer[:], ff_buffer[:], + f[:,1], df, fext, forward, coord) + @. D[:,j] = df_buffer[:] # assign appropriate column of derivative matrix + end + # correct diagonal elements to gurantee numerical stability + # gives D*[1.0, 1.0, ... 1.0] = [0.0, 0.0, ... 0.0] + for j in 1:coord.ngrid + D[j,j] = 0.0 + D[j,j] = -sum(D[j,:]) + end + + #multiply by scale factor for element length + D .= (2.0*float(coord.nelement_global)/coord.L).*D + end + end diff --git a/src/coordinates.jl b/src/coordinates.jl index 93ac7359e..8251fe428 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -51,6 +51,8 @@ struct coordinate discretization::String # if the discretization is finite differences, fd_option provides the precise scheme fd_option::String + # if the discretization is chebyshev_pseudospectral, cheb_option chooses whether to use FFT or differentiation matrices for d / d coord + cheb_option::String # bc is the boundary condition option for this coordinate bc::String # wgts contains the integration weights associated with each grid point @@ -145,7 +147,7 @@ function define_coordinate(input, parallel_io::Bool=false) end return 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, input.discretization, input.fd_option, + cell_width, igrid, ielement, imin, imax, input.discretization, input.fd_option, input.cheb_option, input.bc, wgts, uniform_grid, duniform_dgrid, scratch, copy(scratch), copy(scratch), scratch_2d, copy(scratch_2d), advection, send_buffer, receive_buffer, input.comm, local_io_range, global_io_range) diff --git a/src/input_structs.jl b/src/input_structs.jl index 20b84a636..9f2b70d8e 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -118,6 +118,8 @@ mutable struct grid_input_mutable discretization::String # finite difference option (only used if discretization is "finite_difference") fd_option::String + # cheb option (only used if discretization is "chebyshev_pseudospectral") + cheb_option::String # boundary option bc::String # mutable struct containing advection speed options @@ -145,6 +147,8 @@ struct grid_input discretization::String # finite difference option (only used if discretization is "finite_difference") fd_option::String + # cheb option (only used if discretization is "chebyshev_pseudospectral") + cheb_option::String # boundary option bc::String # struct containing advection speed options diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index bf171c256..974c8f12c 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -352,37 +352,37 @@ function mk_input(scan_input=Dict()) z_advection_immutable = advection_input(z.advection.option, z.advection.constant_speed, z.advection.frequency, z.advection.oscillation_amplitude) z_immutable = grid_input("z", z.ngrid, z.nelement_global, z.nelement_local, nrank_z, irank_z, z.L, - z.discretization, z.fd_option, z.bc, z_advection_immutable, comm_sub_z) + z.discretization, z.fd_option, z.cheb_option, z.bc, z_advection_immutable, comm_sub_z) r_advection_immutable = advection_input(r.advection.option, r.advection.constant_speed, r.advection.frequency, r.advection.oscillation_amplitude) r_immutable = grid_input("r", r.ngrid, r.nelement_global, r.nelement_local, nrank_r, irank_r, r.L, - r.discretization, r.fd_option, r.bc, r_advection_immutable, comm_sub_r) + r.discretization, r.fd_option, r.cheb_option, r.bc, r_advection_immutable, comm_sub_r) # for dimensions below which do not currently use distributed-memory MPI # assign dummy values to nrank, irank and comm of coord struct vpa_advection_immutable = advection_input(vpa.advection.option, vpa.advection.constant_speed, vpa.advection.frequency, vpa.advection.oscillation_amplitude) vpa_immutable = grid_input("vpa", vpa.ngrid, vpa.nelement_global, vpa.nelement_local, 1, 0, vpa.L, - vpa.discretization, vpa.fd_option, vpa.bc, vpa_advection_immutable, MPI.COMM_NULL) + vpa.discretization, vpa.fd_option, vpa.cheb_option, vpa.bc, vpa_advection_immutable, MPI.COMM_NULL) vperp_advection_immutable = advection_input(vperp.advection.option, vperp.advection.constant_speed, vperp.advection.frequency, vperp.advection.oscillation_amplitude) vperp_immutable = grid_input("vperp", vperp.ngrid, vperp.nelement_global, vperp.nelement_local, 1, 0, vperp.L, - vperp.discretization, vperp.fd_option, vperp.bc, vperp_advection_immutable, MPI.COMM_NULL) + vperp.discretization, vperp.fd_option, vperp.cheb_option, vperp.bc, vperp_advection_immutable, MPI.COMM_NULL) gyrophase_advection_immutable = advection_input(gyrophase.advection.option, gyrophase.advection.constant_speed, gyrophase.advection.frequency, gyrophase.advection.oscillation_amplitude) gyrophase_immutable = grid_input("gyrophase", gyrophase.ngrid, gyrophase.nelement_global, gyrophase.nelement_local, 1, 0, gyrophase.L, - gyrophase.discretization, gyrophase.fd_option, gyrophase.bc, gyrophase_advection_immutable, MPI.COMM_NULL) + gyrophase.discretization, gyrophase.fd_option, gyrophase.cheb_option, gyrophase.bc, gyrophase_advection_immutable, MPI.COMM_NULL) vz_advection_immutable = advection_input(vz.advection.option, vz.advection.constant_speed, vz.advection.frequency, vz.advection.oscillation_amplitude) vz_immutable = grid_input("vz", vz.ngrid, vz.nelement_global, vz.nelement_local, 1, 0, vz.L, - vz.discretization, vz.fd_option, vz.bc, vz_advection_immutable, MPI.COMM_NULL) + vz.discretization, vz.fd_option, vz.cheb_option, vz.bc, vz_advection_immutable, MPI.COMM_NULL) vr_advection_immutable = advection_input(vr.advection.option, vr.advection.constant_speed, vr.advection.frequency, vr.advection.oscillation_amplitude) vr_immutable = grid_input("vr", vr.ngrid, vr.nelement_global, vr.nelement_local, 1, 0, vr.L, - vr.discretization, vr.fd_option, vr.bc, vr_advection_immutable, MPI.COMM_NULL) + vr.discretization, vr.fd_option, vr.cheb_option, vr.bc, vr_advection_immutable, MPI.COMM_NULL) vzeta_advection_immutable = advection_input(vzeta.advection.option, vzeta.advection.constant_speed, vzeta.advection.frequency, vzeta.advection.oscillation_amplitude) vzeta_immutable = grid_input("vzeta", vzeta.ngrid, vzeta.nelement_global, vzeta.nelement_local, 1, 0, vzeta.L, - vzeta.discretization, vzeta.fd_option, vzeta.bc, vzeta_advection_immutable, MPI.COMM_NULL) + vzeta.discretization, vzeta.fd_option, vzeta.cheb_option, vzeta.bc, vzeta_advection_immutable, MPI.COMM_NULL) species_charged_immutable = Array{species_parameters,1}(undef,n_ion_species) species_neutral_immutable = Array{species_parameters,1}(undef,n_neutral_species) @@ -474,6 +474,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) conservation = true #advective_form = false evolve_moments = evolve_moments_options(evolve_density, evolve_parallel_flow, evolve_parallel_pressure, conservation)#advective_form) + # cheb option switch + cheb_option = "FFT" # "matrix" # #################### parameters related to the z grid ###################### # ngrid_z is number of grid points per element ngrid_z = 100 @@ -497,6 +499,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) #finite_difference_option_z = "first_order_upwind" #finite_difference_option_z = "second_order_upwind" finite_difference_option_z = "third_order_upwind" + #cheb_option_z = "FFT" # "matrix" + cheb_option_z = cheb_option # determine the option used for the advection speed in z # supported options are "constant" and "oscillating", # in addition to the "default" option which uses dz/dt = vpa as the advection speed @@ -512,7 +516,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_z, oscillation_amplitude_z) # create a mutable structure containing the input info related to the z grid z = grid_input_mutable("z", ngrid_z, nelement_global_z, nelement_local_z, L_z, - discretization_option_z, finite_difference_option_z, boundary_option_z, + discretization_option_z, finite_difference_option_z, cheb_option_z, boundary_option_z, advection_z) #################### parameters related to the r grid ###################### # ngrid_r is number of grid points per element @@ -537,6 +541,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) #finite_difference_option_r = "first_order_upwind" #finite_difference_option_r = "second_order_upwind" finite_difference_option_r = "third_order_upwind" + #cheb_option_r = "FFT" #"matrix" + cheb_option_r = cheb_option # determine the option used for the advection speed in r # supported options are "constant" and "oscillating", # in addition to the "default" option which uses dr/dt = vpa as the advection speed @@ -552,7 +558,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_r, oscillation_amplitude_r) # create a mutable structure containing the input info related to the r grid r = grid_input_mutable("r", ngrid_r, nelement_global_r, nelement_local_r, L_r, - discretization_option_r, finite_difference_option_r, boundary_option_r, + discretization_option_r, finite_difference_option_r, cheb_option_r, boundary_option_r, advection_r) ############################################################################ ################### parameters related to the vpa grid ##################### @@ -575,6 +581,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # supported options are "third_order_upwind", "second_order_upwind" and "first_order_upwind" #finite_difference_option_vpa = "second_order_upwind" finite_difference_option_vpa = "third_order_upwind" + #cheb_option_vpa = "FFT" # "matrix" + cheb_option_vpa = cheb_option # determine the option used for the advection speed in vpa # supported options are "constant" and "oscillating", # in addition to the "default" option which uses dvpa/dt = q*Ez/m as the advection speed @@ -590,7 +598,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_vpa, oscillation_amplitude_vpa) # create a mutable structure containing the input info related to the vpa grid vpa = grid_input_mutable("vpa", ngrid_vpa, nelement_vpa, nelement_vpa, L_vpa, - discretization_option_vpa, finite_difference_option_vpa, boundary_option_vpa, + discretization_option_vpa, finite_difference_option_vpa, cheb_option_vpa, boundary_option_vpa, advection_vpa) ############################################################################ ################### parameters related to the vperp grid ##################### @@ -613,6 +621,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # supported options are "third_order_upwind", "second_order_upwind" and "first_order_upwind" #finite_difference_option_vperp = "second_order_upwind" finite_difference_option_vperp = "third_order_upwind" + #cheb_option_vperp = "FFT" # "matrix" + cheb_option_vperp = cheb_option # determine the option used for the advection speed in vperp # supported options are "constant" and "oscillating", advection_option_vperp = "default" @@ -627,7 +637,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_vperp, oscillation_amplitude_vperp) # create a mutable structure containing the input info related to the vperp grid vperp = grid_input_mutable("vperp", ngrid_vperp, nelement_vperp, nelement_vperp, L_vperp, - discretization_option_vperp, finite_difference_option_vperp, boundary_option_vperp, + discretization_option_vperp, finite_difference_option_vperp, cheb_option_vperp, boundary_option_vperp, advection_vperp) ############################################################################ ################### parameters related to the gyrophase grid ##################### @@ -642,6 +652,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) boundary_option_gyrophase = "periodic" discretization_option_gyrophase = "finite_difference" finite_difference_option_gyrophase = "third_order_upwind" + #cheb_option_gyrophase = "FFT" #"matrix" + cheb_option_gyrophase = cheb_option advection_option_gyrophase = "default" advection_speed_gyrophase = 0.0 frequency_gyrophase = 1.0 @@ -650,7 +662,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_gyrophase, oscillation_amplitude_gyrophase) # create a mutable structure containing the input info related to the gyrophase grid gyrophase = grid_input_mutable("gyrophase", ngrid_gyrophase, nelement_gyrophase, nelement_gyrophase, L_gyrophase, - discretization_option_gyrophase, finite_difference_option_gyrophase, boundary_option_gyrophase, + discretization_option_gyrophase, finite_difference_option_gyrophase, cheb_option_gyrophase, boundary_option_gyrophase, advection_gyrophase) ############################################################################ ################### parameters related to the vr grid ##################### @@ -671,6 +683,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # supported options are "third_order_upwind", "second_order_upwind" and "first_order_upwind" #finite_difference_option_vr = "second_order_upwind" finite_difference_option_vr = "third_order_upwind" + #cheb_option_vr = "FFT" # "matrix" + cheb_option_vr = cheb_option # determine the option used for the advection speed in vr # supported options are "constant" and "oscillating", advection_option_vr = "default" @@ -685,7 +699,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_vr, oscillation_amplitude_vr) # create a mutable structure containing the input info related to the vr grid vr = grid_input_mutable("vr", ngrid_vr, nelement_vr, nelement_vr, L_vr, - discretization_option_vr, finite_difference_option_vr, boundary_option_vr, + discretization_option_vr, finite_difference_option_vr, cheb_option_vr, boundary_option_vr, advection_vr) ############################################################################ ################### parameters related to the vz grid ##################### @@ -706,6 +720,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # supported options are "third_order_upwind", "second_order_upwind" and "first_order_upwind" #finite_difference_option_vz = "second_order_upwind" finite_difference_option_vz = "third_order_upwind" + #cheb_option_vz = "FFT" # "matrix" + cheb_option_vz = cheb_option # determine the option used for the advection speed in vz # supported options are "constant" and "oscillating", advection_option_vz = "default" @@ -720,7 +736,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_vz, oscillation_amplitude_vz) # create a mutable structure containing the input info related to the vz grid vz = grid_input_mutable("vz", ngrid_vz, nelement_vz, nelement_vz, L_vz, - discretization_option_vz, finite_difference_option_vz, boundary_option_vz, + discretization_option_vz, finite_difference_option_vz, cheb_option_vz, boundary_option_vz, advection_vz) ############################################################################ ################### parameters related to the vzeta grid ##################### @@ -741,6 +757,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # supported options are "third_order_upwind", "second_order_upwind" and "first_order_upwind" #finite_difference_option_vzeta = "second_order_upwind" finite_difference_option_vzeta = "third_order_upwind" + #cheb_option_vzeta = "FFT" # "matrix" + cheb_option_vzeta = cheb_option # determine the option used for the advection speed in vzeta # supported options are "constant" and "oscillating", advection_option_vzeta = "default" @@ -755,7 +773,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) frequency_vzeta, oscillation_amplitude_vzeta) # create a mutable structure containing the input info related to the vzeta grid vzeta = grid_input_mutable("vzeta", ngrid_vzeta, nelement_vzeta, nelement_vzeta, L_vzeta, - discretization_option_vzeta, finite_difference_option_vzeta, boundary_option_vzeta, + discretization_option_vzeta, finite_difference_option_vzeta, cheb_option_vzeta, boundary_option_vzeta, advection_vzeta) ############################################################################# # define default values and create corresponding mutable structs holding diff --git a/src/time_advance.jl b/src/time_advance.jl index 20ad7b2b4..17adaa04c 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -235,11 +235,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms z_spectral = setup_chebyshev_pseudospectral(z) # obtain the local derivatives of the uniform z-grid with respect to the used z-grid - chebyshev_derivative!(z.duniform_dgrid, z.uniform_grid, z_spectral, z) + #chebyshev_derivative!(z.duniform_dgrid, z.uniform_grid, z_spectral, z) else # create dummy Bool variable to return in place of the above struct z_spectral = false - z.duniform_dgrid .= 1.0 + #z.duniform_dgrid .= 1.0 end if r.discretization == "chebyshev_pseudospectral" && r.n > 1 @@ -247,11 +247,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms r_spectral = setup_chebyshev_pseudospectral(r) # obtain the local derivatives of the uniform r-grid with respect to the used r-grid - chebyshev_derivative!(r.duniform_dgrid, r.uniform_grid, r_spectral, r) + #chebyshev_derivative!(r.duniform_dgrid, r.uniform_grid, r_spectral, r) else # create dummy Bool variable to return in place of the above struct r_spectral = false - r.duniform_dgrid .= 1.0 + #r.duniform_dgrid .= 1.0 end if vpa.discretization == "chebyshev_pseudospectral" @@ -259,11 +259,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms vpa_spectral = setup_chebyshev_pseudospectral(vpa) # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid - chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) + #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) else # create dummy Bool variable to return in place of the above struct vpa_spectral = false - vpa.duniform_dgrid .= 1.0 + #vpa.duniform_dgrid .= 1.0 end if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 @@ -271,11 +271,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms vperp_spectral = setup_chebyshev_pseudospectral(vperp) # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid - chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) + #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) else # create dummy Bool variable to return in place of the above struct vperp_spectral = false - vperp.duniform_dgrid .= 1.0 + #vperp.duniform_dgrid .= 1.0 end if vz.discretization == "chebyshev_pseudospectral" && vz.n > 1 @@ -283,11 +283,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms vz_spectral = setup_chebyshev_pseudospectral(vz) # obtain the local derivatives of the uniform vz-grid with respect to the used vz-grid - chebyshev_derivative!(vz.duniform_dgrid, vz.uniform_grid, vz_spectral, vz) + #chebyshev_derivative!(vz.duniform_dgrid, vz.uniform_grid, vz_spectral, vz) else # create dummy Bool variable to return in place of the above struct vz_spectral = false - vz.duniform_dgrid .= 1.0 + #vz.duniform_dgrid .= 1.0 end if vr.discretization == "chebyshev_pseudospectral" && vr.n > 1 @@ -295,11 +295,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms vr_spectral = setup_chebyshev_pseudospectral(vr) # obtain the local derivatives of the uniform vr-grid with respect to the used vr-grid - chebyshev_derivative!(vr.duniform_dgrid, vr.uniform_grid, vr_spectral, vr) + #chebyshev_derivative!(vr.duniform_dgrid, vr.uniform_grid, vr_spectral, vr) else # create dummy Bool variable to return in place of the above struct vr_spectral = false - vr.duniform_dgrid .= 1.0 + #vr.duniform_dgrid .= 1.0 end if vzeta.discretization == "chebyshev_pseudospectral" && vzeta.n > 1 @@ -307,11 +307,11 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # and create the plans for the forward and backward fast Chebyshev transforms vzeta_spectral = setup_chebyshev_pseudospectral(vzeta) # obtain the local derivatives of the uniform vzeta-grid with respect to the used vzeta-grid - chebyshev_derivative!(vzeta.duniform_dgrid, vzeta.uniform_grid, vzeta_spectral, vzeta) + #chebyshev_derivative!(vzeta.duniform_dgrid, vzeta.uniform_grid, vzeta_spectral, vzeta) else # create dummy Bool variable to return in place of the above struct vzeta_spectral = false - vzeta.duniform_dgrid .= 1.0 + #vzeta.duniform_dgrid .= 1.0 end # create an array of structs containing scratch arrays for the pdf and low-order moments diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index 354345e95..61da11c8e 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -11,7 +11,7 @@ using MPI using Random fd_fake_setup(x) = return false - +cheb_option_global = "matrix" # "FFT" function runtests() @testset "calculus" verbose=use_verbose begin println("calculus tests") @@ -37,6 +37,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -45,7 +46,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - discretization, fd_option, bc, adv_input, comm) + discretization, fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x @@ -88,6 +89,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -96,7 +98,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "finite_difference", fd_option, bc, adv_input, comm) + "finite_difference", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) @@ -135,6 +137,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "fourth_order_centered" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -143,7 +146,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "finite_difference", fd_option, bc, adv_input, comm) + "finite_difference", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) @@ -178,6 +181,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -186,7 +190,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "finite_difference", fd_option, bc, adv_input, comm) + "finite_difference", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) @@ -229,6 +233,7 @@ function runtests() L = 6.0 # fd_option and adv_input not actually used so given values unimportant adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -237,7 +242,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "finite_difference", fd_option, bc, adv_input, comm) + "finite_difference", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) @@ -437,6 +442,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -445,7 +451,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "chebyshev_pseudospectral", fd_option, bc, adv_input, comm) + "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x @@ -635,6 +641,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -643,7 +650,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "chebyshev_pseudospectral", fd_option, bc, adv_input, comm) + "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x @@ -681,6 +688,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -689,7 +697,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "chebyshev_pseudospectral", fd_option, bc, adv_input, comm) + "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x @@ -735,6 +743,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -743,7 +752,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "chebyshev_pseudospectral", fd_option, bc, adv_input, comm) + "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x @@ -952,6 +961,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -960,7 +970,7 @@ function runtests() comm = MPI.COMM_NULL # dummy value input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - "chebyshev_pseudospectral", fd_option, bc, adv_input, comm) + "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'x' x = define_coordinate(input) # create arrays needed for Chebyshev pseudospectral treatment in x diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index b36d775df..f8eccb110 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -40,9 +40,10 @@ function runtests() nrank_per_block = 0 # dummy value irank = 0 # dummy value comm = MPI.COMM_NULL # dummy value + cheb_option = "FFT" input = grid_input("coord", ngrid, nelement, nelement_local, nrank_per_block, irank, L, - discretization, fd_option, bc, adv_input, comm) + discretization, fd_option, cheb_option, bc, adv_input, comm) # create the coordinate struct 'z' z = define_coordinate(input) # For Chebyshev method, create arrays needed for Chebyshev pseudospectral diff --git a/test/nonlinear_sound_wave_tests.jl b/test/nonlinear_sound_wave_tests.jl index 31c932b73..d6803993b 100644 --- a/test/nonlinear_sound_wave_tests.jl +++ b/test/nonlinear_sound_wave_tests.jl @@ -305,12 +305,13 @@ function run_test(test_input, rtol; args...) # create the 'input' struct containing input info needed to create a coordinate # adv_input not actually used in this test so given values unimportant adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "FFT" nrank_per_block = 0 # dummy value irank = 0 # dummy value comm = MPI.COMM_NULL # dummy value input = grid_input("coord", test_input["z_ngrid"], test_input["z_nelement"], test_input["z_nelement"], nrank_per_block, irank, - z_L, test_input["z_discretization"], "", + z_L, test_input["z_discretization"], "", cheb_option, "periodic", #test_input["z_bc"], adv_input,comm) z = define_coordinate(input) @@ -321,7 +322,7 @@ function run_test(test_input, rtol; args...) end input = grid_input("coord", test_input["vpa_ngrid"], test_input["vpa_nelement"], test_input["vpa_nelement"], nrank_per_block, irank, - vpa_L, test_input["vpa_discretization"], "", + vpa_L, test_input["vpa_discretization"], "", cheb_option, test_input["vpa_bc"], adv_input, comm) vpa = define_coordinate(input) if test_input["vpa_discretization"] == "chebyshev_pseudospectral" @@ -402,7 +403,7 @@ function run_test(test_input, rtol; args...) 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) - newgrid_ppar_charged = interpolate_to_grid_z(expected.z, ppar_charged[:, :, tind], z, z_spectral) + newgrid_ppar_charged = 0.5*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_f_charged = interpolate_to_grid_z(expected.z, f_charged[:, :, :, tind], z, z_spectral) diff --git a/test/wall_bc_tests.jl b/test/wall_bc_tests.jl index 7deda8c3d..2e48785c1 100644 --- a/test/wall_bc_tests.jl +++ b/test/wall_bc_tests.jl @@ -185,12 +185,13 @@ function run_test(test_input, expected_phi, tolerance; args...) # create the 'input' struct containing input info needed to create a coordinate # adv_input not actually used in this test so given values unimportant adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "FFT" nrank_per_block = 0 # dummy value irank = 0 # dummy value comm = MPI.COMM_NULL # dummy value input = grid_input("coord", test_input["z_ngrid"], test_input["z_nelement"], test_input["z_nelement"], nrank_per_block, irank, 1.0, - test_input["z_discretization"], "", test_input["z_bc"], + test_input["z_discretization"], "", cheb_option, test_input["z_bc"], adv_input, comm) z = define_coordinate(input) if test_input["z_discretization"] == "chebyshev_pseudospectral" From 825f9f9b25e7fd91ef4224e2ffa25e967231ecae Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 6 Jun 2023 17:00:02 +0100 Subject: [PATCH 031/331] Proof of concept use of Gauss-Lobatto-Legendre Quadrature and Legendre-based Lagrange differentiation matrix. --- GaussLobattoLegendre_test.jl | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 GaussLobattoLegendre_test.jl diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl new file mode 100644 index 000000000..7c5c2af7a --- /dev/null +++ b/GaussLobattoLegendre_test.jl @@ -0,0 +1,103 @@ +using FastGaussQuadrature +using LegendrePolynomials: Pl +using LinearAlgebra: mul! +using Printf +using Plots +using LaTeXStrings +using MPI +using Measures + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.3f ", matrix[i,j]) + end + println("") + end + println("\n") + end + + function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") + end + """ + Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of + `Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. + """ + function legendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) + D[:,:] .= 0.0 + for ix in 1:ngrid + for ixp in 1:ngrid + if !(ix == ixp) + D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))/(x[ix]-x[ixp]) + end + end + end + # uncomment for analytical diagonal values + #D[1,1] = -0.25*(ngrid - 1)*ngrid + #D[ngrid,ngrid] = 0.25*(ngrid - 1)*ngrid + #for ix in 1:ngrid + # D[ix,ix] = 0.0 + #end + # get diagonal values from sum of nonzero off diagonal values + for ix in 1:ngrid + D[ix,ix] = -sum(D[ix,:]) + end + #for ix in 1:ngrid + # println(sum( @view(D[ix,:]))) + #end + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + + return nothing + end + + + #println("Hello world") + ngrid = 33 + x, w = gausslobatto(ngrid) + println("x: ",x) + println("w: ",w) + + f_exact = Array{Float64,1}(undef,ngrid) + df_exact = Array{Float64,1}(undef,ngrid) + df_num = Array{Float64,1}(undef,ngrid) + df_err = Array{Float64,1}(undef,ngrid) + + for ix in 1:ngrid + #f_exact = exp(-x[ix]^2) + #df_exact = -2.0*x[ix]*exp(-x[ix]^2) + + f_exact[ix] = -2.0*x[ix]*exp(-x[ix]^2) + df_exact[ix] = (4.0*x[ix]^2 - 2.0)*exp(-x[ix]^2) + end + F_exact = f_exact[end] - f_exact[1] + # do a test integration + F_num = sum(w.*df_exact) + F_err = abs(F_num - F_exact) + #for ix in 1:ngrid + # F_num += w[ix]*df_exact[ix] + #end + println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) + + Dmat = Array{Float64,2}(undef,ngrid,ngrid) + legendre_differentiation_matrix!(Dmat,x,ngrid,x[end]-x[1],1) + print_matrix(Dmat,"Dmat",ngrid,ngrid) + + mul!(df_num,Dmat,f_exact) + @. df_err = abs(df_num - df_exact) + println(df_num) + println(df_exact) + max_df_err = maximum(df_err) + println("max df_err: ",max_df_err) +end From 4621344c5b1dd98c31aad0236387622039f8dc36 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 6 Jun 2023 17:48:04 +0100 Subject: [PATCH 032/331] Gauss Radau Legendre differentiation matrix added to proof-of-concept script. --- GaussLobattoLegendre_test.jl | 82 +++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 7c5c2af7a..013a8313a 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -33,8 +33,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ """ Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of `Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. + Or https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html """ - function legendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) + function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) D[:,:] .= 0.0 for ix in 1:ngrid for ixp in 1:ngrid @@ -46,26 +47,87 @@ if abspath(PROGRAM_FILE) == @__FILE__ # uncomment for analytical diagonal values #D[1,1] = -0.25*(ngrid - 1)*ngrid #D[ngrid,ngrid] = 0.25*(ngrid - 1)*ngrid - #for ix in 1:ngrid + #for ix in 1:ngrid-1 # D[ix,ix] = 0.0 #end # get diagonal values from sum of nonzero off diagonal values for ix in 1:ngrid D[ix,ix] = -sum(D[ix,:]) end - #for ix in 1:ngrid - # println(sum( @view(D[ix,:]))) + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + return nothing + end + """ + From + https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html + """ + function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) + D[:,:] .= 0.0 + for ix in 1:ngrid + for ixp in 1:ngrid + if !(ix == ixp) + D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))*((1.0 - x[ixp])/(1.0 - x[ix]))/(x[ix]-x[ixp]) + end + end + end + # uncomment for analytical diagonal values + #D[1,1] = -0.25*(ngrid - 1)*(ngrid + 1) + #for ix in 2:ngrid + # D[ix,ix] = 0.5/(1.0 - x[ix]) #end + # get diagonal values from sum of nonzero off diagonal values + for ix in 1:ngrid + D[ix,ix] = -sum(D[ix,:]) + end #multiply by scale factor for element length D .= (2.0*float(nelement)/L).*D - return nothing end - - #println("Hello world") + # gauss lobatto test ngrid = 33 x, w = gausslobatto(ngrid) + println("Gauss Lobatto Legendre") + println("x: ",x) + println("w: ",w) + + f_exact = Array{Float64,1}(undef,ngrid) + df_exact = Array{Float64,1}(undef,ngrid) + df_num = Array{Float64,1}(undef,ngrid) + df_err = Array{Float64,1}(undef,ngrid) + + for ix in 1:ngrid + #f_exact = exp(-x[ix]^2) + #df_exact = -2.0*x[ix]*exp(-x[ix]^2) + + f_exact[ix] = -2.0*x[ix]*exp(-x[ix]^2) + df_exact[ix] = (4.0*x[ix]^2 - 2.0)*exp(-x[ix]^2) + end + F_exact = f_exact[end] - f_exact[1] + # do a test integration + F_num = sum(w.*df_exact) + F_err = abs(F_num - F_exact) + #for ix in 1:ngrid + # F_num += w[ix]*df_exact[ix] + #end + println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) + + Dmat = Array{Float64,2}(undef,ngrid,ngrid) + gausslobattolegendre_differentiation_matrix!(Dmat,x,ngrid,2.0,1) + print_matrix(Dmat,"Dmat",ngrid,ngrid) + + mul!(df_num,Dmat,f_exact) + @. df_err = abs(df_num - df_exact) + #println(df_num) + #println(df_exact) + max_df_err = maximum(df_err) + println("max df_err: ",max_df_err) + + # gauss radau test + ngrid = 33 + x, w = gaussradau(ngrid) + println("Gauss Radau Legendre") println("x: ",x) println("w: ",w) @@ -91,13 +153,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) Dmat = Array{Float64,2}(undef,ngrid,ngrid) - legendre_differentiation_matrix!(Dmat,x,ngrid,x[end]-x[1],1) + gaussradaulegendre_differentiation_matrix!(Dmat,x,ngrid,2.0,1) print_matrix(Dmat,"Dmat",ngrid,ngrid) mul!(df_num,Dmat,f_exact) @. df_err = abs(df_num - df_exact) - println(df_num) - println(df_exact) + #println(df_num) + #println(df_exact) max_df_err = maximum(df_err) println("max df_err: ",max_df_err) end From 4fbb642d2aa189c9ae3ee438aa8ca4bf330a7a68 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 22 Jun 2023 16:09:57 +0100 Subject: [PATCH 033/331] Addition of tests which check the specification of the coefficients derived for the Rosenbluth potentials -- these tests indicate that the coefficients are specified correctly. --- fkpl_test.jl | 198 ++++++++++++++++++++++++++++++++++++------- src/fokker_planck.jl | 24 +++++- 2 files changed, 188 insertions(+), 34 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 59ca3e443..6070374ff 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -5,14 +5,14 @@ using Measures using MPI using SpecialFunctions: erf -function eta_speed(vpa,vperp,ivpa,ivperp) - eta = sqrt(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2) +function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth return eta end -function G_Maxwellian(vpa,vperp,ivpa,ivperp) +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) # speed variable - eta = eta_speed(vpa,vperp,ivpa,ivperp) + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) zero = 1.0e-10 if eta < zero G = 2.0/sqrt(pi) @@ -20,11 +20,11 @@ function G_Maxwellian(vpa,vperp,ivpa,ivperp) # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) end - return G + return G*dens*vth end -function H_Maxwellian(vpa,vperp,ivpa,ivperp) +function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) # speed variable - eta = eta_speed(vpa,vperp,ivpa,ivperp) + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) zero = 1.0e-10 if eta < zero # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 @@ -33,7 +33,7 @@ function H_Maxwellian(vpa,vperp,ivpa,ivperp) # H_M = erf(eta)/eta H = erf(eta)/eta end - return H + return H*dens/vth end function pressure(ppar,pperp) @@ -41,6 +41,10 @@ function pressure(ppar,pperp) return pres end +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) # #Gamma = 0.0 # #return Gamma @@ -117,29 +121,72 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs + using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure - test_Rosenbluth_integrals = false + function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2Gdvpa2[:,ivperp] = vpa.scratch2 + end + end + + function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @. buffer_vpavperp[ivpa,:] = vperp.scratch + end + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) + @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2Gdvperp2[ivpa,:] = vperp.scratch2 + end + end + + function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) + @. dHdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) + @. dHdvperp[ivpa,:] = vperp.scratch + end + end + + test_Rosenbluth_integrals = true discretization = "chebyshev_pseudospectral" #discretization = "finite_difference" # define inputs needed for the test vpa_ngrid = 17 #number of points per element - vpa_nelement_local = 8 # number of elements per rank + vpa_nelement_local = 16 # number of elements per rank vpa_nelement_global = vpa_nelement_local # total number of elements vpa_L = 12.0 #physical box size in reference units bc = "zero" vperp_ngrid = 17 #number of points per element - vperp_nelement_local = 8 # number of elements per rank + vperp_nelement_local = 16 # number of elements per rank vperp_nelement_global = vperp_nelement_local # total number of elements vperp_L = 6.0 #physical box size in reference units bc = "zero" # fd_option and adv_input not actually used so given values unimportant fd_option = "fourth_order_centered" + cheb_option = "matrix" adv_input = advection_input("default", 1.0, 0.0, 0.0) nrank = 1 irank = 0 @@ -147,9 +194,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # create the 'input' struct containing input info needed to create a # coordinate vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, bc, adv_input,comm) + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, bc, adv_input,comm) + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate structs println("made inputs") @@ -174,10 +221,27 @@ if abspath(PROGRAM_FILE) == @__FILE__ fs_in = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #H_check = Array{mk_float,2}(undef,nvpa,nvperp) + H_check = Array{mk_float,2}(undef,nvpa,nvperp) G_err = Array{mk_float,2}(undef,nvpa,nvperp) H_err = Array{mk_float,2}(undef,nvpa,nvperp) - #H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) + + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_check = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) @@ -192,17 +256,30 @@ if abspath(PROGRAM_FILE) == @__FILE__ #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) if test_Rosenbluth_integrals - - fkarrays = init_fokker_planck_collisions(vperp, vpa) + + dens = 3.0/4.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + pperp = 2.0/3.0 + pres = get_pressure(ppar,pperp) + mi = 1.0 + vths = get_vth(pres,dens,mi) + + n_ion_species = 1 + dens_in = Array{mk_float,1}(undef,n_ion_species) + upar_in = Array{mk_float,1}(undef,n_ion_species) + vths_in = Array{mk_float,1}(undef,n_ion_species) + dens_in[1] = dens + upar_in[1] = upar + vths_in[1] = vths + fkarrays = init_fokker_planck_collisions(vperp, vpa, init_integral_factors = true) for ivperp in 1:nvperp for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = exp( - vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2 ) - G_Maxwell[ivpa,ivperp] = G_Maxwellian(vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(vpa,vperp,ivpa,ivperp) - #Gam_vpa_Maxwell[ivpa,ivperp] = 0.0 - #Gam_vperp_Maxwell[ivpa,ivperp] = 0.0 + fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) end end @@ -212,16 +289,77 @@ if abspath(PROGRAM_FILE) == @__FILE__ @views calculate_Rosenbluth_potentials!(fkarrays.Rosenbluth_G, fkarrays.Rosenbluth_H, fsp_in, fkarrays.elliptic_integral_E_factor,fkarrays.elliptic_integral_K_factor, fkarrays.buffer_vpavperp_1,vperp,vpa) - - #@views calculate_Rosenbluth_H_from_G!(H_check,G_Maxwell, - # vpa,vpa_spectral,vperp,vperp_spectral,Bmag, - # fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) + + #@views heatmap(vperp.grid, vpa.grid, fkarrays.Rosenbluth_G[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("fkpl_RosenG.pdf") + # savefig(outfile) + #@views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("fkpl_G_Maxwell.pdf") + # savefig(outfile) + + G_in = G_Maxwell # use analytical G to calculate a H for the check + @views calculate_Rosenbluth_H_from_G!(H_check,G_in, + vpa,vpa_spectral,vperp,vperp_spectral, + fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) + + numerical_G = false + if numerical_G + G_in = fkarrays.Rosenbluth_G + H_in = fkarrays.Rosenbluth_H + @views calculate_Rosenbluth_H_from_G!(H_in,G_in, + vpa,vpa_spectral,vperp,vperp_spectral, + fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) + # using numerical G, errors are much worse than using smooth analytical G + else + G_in = G_Maxwell # use analytical G for coeffs + H_in = H_Maxwell # use analytical H for coeffs + end + @views calculate_d2Gdvpa2!(d2Gdvpa2_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral) + @views calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral, fkarrays.buffer_vpavperp_1) + @views calculate_d2Gdvperp2!(d2Gdvperp2_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral) + @views calculate_dHdvperp!(dHdvperp_check,H_in,vpa,vpa_spectral,vperp,vperp_spectral) + @views calculate_dHdvpa!(dHdvpa_check,H_in,vpa,vpa_spectral,vperp,vperp_spectral) + + #@views heatmap(vperp.grid, vpa.grid, H_check[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("fkpl_RosenH.pdf") + # savefig(outfile) + #@views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + # windowsize = (360,240), margin = 15pt) + # outfile = string("fkpl_H_Maxwell.pdf") + # savefig(outfile) + + @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) - #@. H_check_err = abs(H_check - H_Maxwell) + @. H_check_err = abs(H_check - H_Maxwell) println("max(G_err)",maximum(G_err)) println("max(H_err)",maximum(H_err)) - #println("max(H_check_err)",maximum(H_check_err)) + println("max(H_check_err)",maximum(H_check_err)) + + for ivperp in 1:nvperp + for ivpa in 1:nvpa + ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian + ((d2Gdvpa2_Maxwell[ivpa,ivperp], d2Gdvperpdvpa_Maxwell[ivpa,ivperp], + d2Gdvperp2_Maxwell[ivpa,ivperp],dHdvpa_Maxwell[ivpa,ivperp], + dHdvperp_Maxwell[ivpa,ivperp]) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[:], + upar_in[:],vths_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) + end + end + + @. d2Gdvperpdvpa_err = abs(d2Gdvperpdvpa_check - d2Gdvperpdvpa_Maxwell) + @. d2Gdvpa2_err = abs(d2Gdvpa2_check - d2Gdvpa2_Maxwell) + @. d2Gdvperp2_err = abs(d2Gdvperp2_check - d2Gdvperp2_Maxwell) + @. dHdvperp_err = abs(dHdvperp_check - dHdvperp_Maxwell) + @. dHdvpa_err = abs(dHdvpa_check - dHdvpa_Maxwell) + println("max(dHdvpa_err)",maximum(dHdvpa_err)) + println("max(dHdvperp_err)",maximum(dHdvperp_err)) + println("max(d2Gdvperp2_err)",maximum(d2Gdvperp2_err)) + println("max(d2Gdvpa2_err)",maximum(d2Gdvpa2_err)) + println("max(d2Gdvperpdvpa_err)",maximum(d2Gdvperpdvpa_err)) + zero = 1.0e-3 #println(G_Maxwell[41,:]) #println(G_Maxwell[:,1]) @@ -269,10 +407,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ uparp_in = Array{mk_float,1}(undef,n_ion_species) vthsp_in = Array{mk_float,1}(undef,n_ion_species) - function get_vth(pres,dens,mass) - return sqrt(pres/(dens*mass)) - end - # 2D isotropic Maxwellian test # assign a known isotropic Maxwellian distribution in normalised units # first argument (derivatives) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6ee010fb0..e7194f6ac 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -9,6 +9,7 @@ export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +export calculate_Rosenbluth_H_from_G! using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int @@ -119,9 +120,9 @@ end function that initialises the arrays needed for Fokker Planck collisions """ -function init_fokker_planck_collisions(vperp,vpa) +function init_fokker_planck_collisions(vperp,vpa;init_integral_factors=false) fokkerplanck_arrays = allocate_fokkerplanck_arrays(vperp,vpa) - if vperp.n > 1 && false + if vperp.n > 1 && init_integral_factors @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, fokkerplanck_arrays.elliptic_integral_K_factor, vperp,vpa) @@ -151,6 +152,25 @@ function calculate_Rosenbluth_potentials!(Rosenbluth_G,Rosenbluth_H,fsp_in, end end + +""" +Computes the Laplacian of G in vpa vperp coordinates to obtain H +""" +function calculate_Rosenbluth_H_from_G!(Rosenbluth_H,Rosenbluth_G,vpa,vpa_spectral,vperp,vperp_spectral,buffer_vpavperp_1,buffer_vpavperp_2) + Rosenbluth_H .= 0.0 + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @views @. buffer_vpavperp_1[:,ivperp] = vpa.scratch2 + end + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp, vperp_spectral) + @. vperp.scratch = vperp.grid*vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @views @. buffer_vpavperp_2[ivpa,:] = vperp.scratch2/vperp.grid + end + @views @. Rosenbluth_H = 0.5*(buffer_vpavperp_1 + buffer_vpavperp_2) +end """ From cbd88e666d1d2c8a8bdb28c8aee610255d62d013 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 23 Jun 2023 09:12:05 +0100 Subject: [PATCH 034/331] Refactor test script so that tests of coefficients and collisional fluxes can be called as functions with nelement and ngrid as inputs. --- fkpl_test.jl | 632 +++++++++++++++++++++++++++------------------------ 1 file changed, 334 insertions(+), 298 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 6070374ff..6ea785dcd 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -127,7 +127,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:nvperp + for ivperp in 1:vperp.n @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) @. d2Gdvpa2[:,ivperp] = vpa.scratch2 @@ -135,18 +135,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) - for ivpa in 1:nvpa + for ivpa in 1:vpa.n @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) @. buffer_vpavperp[ivpa,:] = vperp.scratch end - for ivperp in 1:nvperp + for ivperp in 1:vperp.n @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch end end function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:nvpa + for ivpa in 1:vpa.n @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) @. d2Gdvperp2[ivpa,:] = vperp.scratch2 @@ -154,109 +154,98 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:nvperp + for ivperp in 1:vperp.n @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) @. dHdvpa[:,ivperp] = vpa.scratch end end function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:nvpa + for ivpa in 1:vpa.n @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) @. dHdvperp[ivpa,:] = vperp.scratch end end - test_Rosenbluth_integrals = true - - discretization = "chebyshev_pseudospectral" - #discretization = "finite_difference" - - # define inputs needed for the test - vpa_ngrid = 17 #number of points per element - vpa_nelement_local = 16 # number of elements per rank - vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 12.0 #physical box size in reference units - bc = "zero" - vperp_ngrid = 17 #number of points per element - vperp_nelement_local = 16 # number of elements per rank - vperp_nelement_global = vperp_nelement_local # total number of elements - vperp_L = 6.0 #physical box size in reference units - bc = "zero" - - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - - # create the coordinate structs - println("made inputs") - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - #println(vperp.grid) - #println(vperp.wgts) - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - - cfreqssp = 1.0 - ms = 1.0 - msp = 1.0 - - - Cssp = Array{mk_float,2}(undef,nvpa,nvperp) - Cssp_err = Array{mk_float,2}(undef,nvpa,nvperp) - fs_in = Array{mk_float,2}(undef,nvpa,nvperp) - G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_check = Array{mk_float,2}(undef,nvpa,nvperp) - G_err = Array{mk_float,2}(undef,nvpa,nvperp) - H_err = Array{mk_float,2}(undef,nvpa,nvperp) - H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) + function init_grids(nelement,ngrid) + discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + + # create the coordinate structs + println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + + return vpa, vperp, vpa_spectral, vperp_spectral + end - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_check = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + test_Rosenbluth_integrals = true + test_collision_operator_fluxes = true + ngrid = 9 + nelement = 8 - Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - - if test_Rosenbluth_integrals + function test_Rosenbluth_potentials(nelement,ngrid;numerical_G = false) + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_check = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) + + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_check = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + + # set up test Maxwellian dens = 3.0/4.0 upar = 2.0/3.0 ppar = 2.0/3.0 @@ -304,7 +293,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,vpa_spectral,vperp,vperp_spectral, fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) - numerical_G = false if numerical_G G_in = fkarrays.Rosenbluth_G H_in = fkarrays.Rosenbluth_H @@ -335,9 +323,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) @. H_check_err = abs(H_check - H_Maxwell) - println("max(G_err)",maximum(G_err)) - println("max(H_err)",maximum(H_err)) - println("max(H_check_err)",maximum(H_check_err)) + max_G_err = maximum(G_err) + max_H_err = maximum(H_err) + max_H_check_err = maximum(H_check_err) + println("max(G_err)",max_G_err) + println("max(H_err)",max_H_err) + println("max(H_check_err)",max_H_check_err) for ivperp in 1:nvperp for ivpa in 1:nvpa @@ -354,224 +345,269 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperp2_err = abs(d2Gdvperp2_check - d2Gdvperp2_Maxwell) @. dHdvperp_err = abs(dHdvperp_check - dHdvperp_Maxwell) @. dHdvpa_err = abs(dHdvpa_check - dHdvpa_Maxwell) - println("max(dHdvpa_err)",maximum(dHdvpa_err)) - println("max(dHdvperp_err)",maximum(dHdvperp_err)) - println("max(d2Gdvperp2_err)",maximum(d2Gdvperp2_err)) - println("max(d2Gdvpa2_err)",maximum(d2Gdvpa2_err)) - println("max(d2Gdvperpdvpa_err)",maximum(d2Gdvperpdvpa_err)) - - zero = 1.0e-3 - #println(G_Maxwell[41,:]) - #println(G_Maxwell[:,1]) + max_dHdvpa_err = maximum(dHdvpa_err) + max_dHdvperp_err = maximum(dHdvperp_err) + max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) + max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) + max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) + println("max(dHdvpa_err)",max_dHdvpa_err) + println("max(dHdvperp_err)",max_dHdvperp_err) + println("max(d2Gdvperp2_err)",max_d2Gdvperp2_err) + println("max(d2Gdvpa2_err)",max_d2Gdvpa2_err) + println("max(d2Gdvperpdvpa_err)",max_d2Gdvperpdvpa_err) + # + # zero = 1.0e-3 + # #println(G_Maxwell[41,:]) + # #println(G_Maxwell[:,1]) + # for ivperp in 1:nvperp + # #for ivpa in 1:nvpa + # # if (maximum(G_err[ivpa,ivperp]) > zero) + # # ##println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) + # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) + # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) + # # end + # # #H_err[ivpa,ivperp] + # #end + # end + # #println(H_Maxwell[:,1]) + # #println(fkarrays.Rosenbluth_H[:,1]) + # #zero = 0.1 + # for ivperp in 1:nvperp + # #for ivpa in 1:nvpa + # # if (maximum(H_err[ivpa,ivperp]) > zero) + # # ###println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) + # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) + # # end + # # #H_err[ivpa,ivperp] + # #end + # end + return max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err + end + + function test_collision_operator(nelement,ngrid) + + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + cfreqssp = 1.0 + ms = 1.0 + msp = 1.0 + + + Cssp = Array{mk_float,2}(undef,nvpa,nvperp) + Cssp_err = Array{mk_float,2}(undef,nvpa,nvperp) + + Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vperp_Herr = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) + #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) + + d2Gdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Gam_vperp = Array{mk_float,2}(undef,nvpa,nvperp) + dfns = Array{mk_float,2}(undef,nvpa,nvperp) + dfnsp = Array{mk_float,2}(undef,nvpa,nvperp) + pdf_buffer_1 = Array{mk_float,2}(undef,nvpa,nvperp) + pdf_buffer_2 = Array{mk_float,2}(undef,nvpa,nvperp) + n_ion_species = 1 + dens_in = Array{mk_float,1}(undef,n_ion_species) + upar_in = Array{mk_float,1}(undef,n_ion_species) + vths_in = Array{mk_float,1}(undef,n_ion_species) + densp_in = Array{mk_float,1}(undef,n_ion_species) + uparp_in = Array{mk_float,1}(undef,n_ion_species) + vthsp_in = Array{mk_float,1}(undef,n_ion_species) + + # 2D isotropic Maxwellian test + # assign a known isotropic Maxwellian distribution in normalised units + # first argument (derivatives) + dens = 1.0#3.0/4.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + pperp = 2.0/3.0 + pres = get_pressure(ppar,pperp) + mi = 1.0 + vths = get_vth(pres,dens,mi) + # second argument (potentials) + densp = 2.0#3.0/4.0 + uparp = 1.0/3.0#3.0/4.0 + pparp = 4.0/3.0 + pperpp = 4.0/3.0 + presp = get_pressure(pparp,pperpp) + mip = 1.5 + vthsp = get_vth(presp,densp,mip) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + vpa_val = vpa.grid[ivpa] + vperp_val = vperp.grid[ivperp] + dfns[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vths^2 ) + dfnsp[ivpa,ivperp] = (densp/vthsp^3)*exp( - ((vpa_val-uparp)^2 + vperp_val^2)/vthsp^2 ) + end + end + pdf_in = dfns + dens_in[1] = get_density(dfns,vpa,vperp) + upar_in[1] = get_upar(dfns,vpa,vperp,dens_in[1]) + ppar_in = get_ppar(dfns,vpa,vperp,upar_in[1],mi) + pperp_in = get_pperp(dfns,vpa,vperp,mi) + pres_in = pressure(ppar_in,pperp_in) + vths_in[1] = get_vth(pres_in,dens_in[1],mi) + + densp_in[1] = get_density(dfnsp,vpa,vperp) + uparp_in[1] = get_upar(dfnsp,vpa,vperp,densp_in[1]) + pparp_in = get_ppar(dfnsp,vpa,vperp,uparp_in[1],mip) + pperpp_in = get_pperp(dfnsp,vpa,vperp,mip) + presp_in = pressure(pparp_in,pperpp_in) + vthsp_in[1] = get_vth(presp_in,densp_in[1],mip) + + println("Isotropic 2D Maxwellian: first argument (derivatives)") + println("dens_test: ", dens_in[1], " dens: ", dens, " error: ", abs(dens_in[1]-dens)) + println("upar_test: ", upar_in[1], " upar: ", upar, " error: ", abs(upar_in[1]-upar)) + println("ppar_test: ", ppar_in, " ppar: ", ppar, " error: ", abs(ppar_in-ppar)) + println("pperp_test: ", pperp_in, " pperp: ", pperp, " error: ", abs(pperp_in-pperp)) + println("vth_test: ", vths_in[1], " vth: ", vths, " error: ", abs(vths_in[1]-vths)) + println("Isotropic 2D Maxwellian: second argument (potentials)") + println("dens_test: ", densp_in[1], " dens: ", densp, " error: ", abs(densp_in[1]-densp)) + println("upar_test: ", uparp_in[1], " upar: ", uparp, " error: ", abs(uparp_in[1]-uparp)) + println("ppar_test: ", pparp_in, " ppar: ", pparp, " error: ", abs(pparp_in-pparp)) + println("pperp_test: ", pperpp_in, " pperp: ", pperpp, " error: ", abs(pperpp_in-pperpp)) + println("vth_test: ", vthsp_in[1], " vth: ", vthsp, " error: ", abs(vthsp_in[1]-vthsp)) + + for ivperp in 1:nvperp for ivpa in 1:nvpa - if (maximum(G_err[ivpa,ivperp]) > zero) - #println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) - #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) - #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) - end - #H_err[ivpa,ivperp] + Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], + mip,densp_in[1],uparp_in[1],vthsp_in[1], + vpa,vperp,ivpa,ivperp) + Gam_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], + mip,densp_in[1],uparp_in[1],vthsp_in[1], + vpa,vperp,ivpa,ivperp) end end - #println(H_Maxwell[:,1]) - #println(fkarrays.Rosenbluth_H[:,1]) - #zero = 0.1 + + # d F / d vpa + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, pdf_in[:,ivperp], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp] = vpa.scratch + end + # d F / d vperp + for ivpa in 1:nvpa + @views derivative!(vperp.scratch, pdf_in[ivpa,:], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:] = vperp.scratch + end + for ivperp in 1:nvperp for ivpa in 1:nvpa - if (maximum(H_err[ivpa,ivperp]) > zero) - ##println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) - #println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) - end - #H_err[ivpa,ivperp] + ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian + ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, + Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(densp_in[:], + uparp_in[:],vthsp_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) + + # now form the collisional fluxes at this s,z,r + ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp], + pdf_buffer_1[ivpa,ivperp],pdf_buffer_2[ivpa,ivperp], + Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, + Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, + mi,mip,vperp.grid[ivperp]) ) + + + d2Gdvpa2[ivpa,ivperp] = Rosenbluth_d2Gdvpa2 + d2Gdvperpdvpa[ivpa,ivperp] = Rosenbluth_d2Gdvperpdvpa + d2Gdvperp2[ivpa,ivperp] = Rosenbluth_d2Gdvperp2 + dHdvpa[ivpa,ivperp] = Rosenbluth_dHdvpa + dHdvperp[ivpa,ivperp] = Rosenbluth_dHdvperp + Gam_vpa[ivpa,ivperp] = Cflux_vpa + Gam_vperp[ivpa,ivperp] = Cflux_vperp end end - end - - d2Gdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vperp = Array{mk_float,2}(undef,nvpa,nvperp) - dfns = Array{mk_float,2}(undef,nvpa,nvperp) - dfnsp = Array{mk_float,2}(undef,nvpa,nvperp) - pdf_buffer_1 = Array{mk_float,2}(undef,nvpa,nvperp) - pdf_buffer_2 = Array{mk_float,2}(undef,nvpa,nvperp) - n_ion_species = 1 - dens_in = Array{mk_float,1}(undef,n_ion_species) - upar_in = Array{mk_float,1}(undef,n_ion_species) - vths_in = Array{mk_float,1}(undef,n_ion_species) - densp_in = Array{mk_float,1}(undef,n_ion_species) - uparp_in = Array{mk_float,1}(undef,n_ion_species) - vthsp_in = Array{mk_float,1}(undef,n_ion_species) - - # 2D isotropic Maxwellian test - # assign a known isotropic Maxwellian distribution in normalised units - # first argument (derivatives) - dens = 1.0#3.0/4.0 - upar = 2.0/3.0 - ppar = 2.0/3.0 - pperp = 2.0/3.0 - pres = get_pressure(ppar,pperp) - mi = 1.0 - vths = get_vth(pres,dens,mi) - # second argument (potentials) - densp = 2.0#3.0/4.0 - uparp = 1.0/3.0#3.0/4.0 - pparp = 4.0/3.0 - pperpp = 4.0/3.0 - presp = get_pressure(pparp,pperpp) - mip = 1.5 - vthsp = get_vth(presp,densp,mip) - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - vpa_val = vpa.grid[ivpa] - vperp_val = vperp.grid[ivperp] - dfns[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vths^2 ) - dfnsp[ivpa,ivperp] = (densp/vthsp^3)*exp( - ((vpa_val-uparp)^2 + vperp_val^2)/vthsp^2 ) + + @. Gam_vpa_err = abs(Gam_vpa - Gam_vpa_Maxwell) + @. Gam_vperp_err = abs(Gam_vperp - Gam_vperp_Maxwell) + max_Gam_vpa_err = maximum(Gam_vpa_err) + max_Gam_vperp_err = maximum(Gam_vperp_err) + println("max(Gam_vpa_err): ",max_Gam_vpa_err) + println("max(Gam_vperp_err): ",max_Gam_vperp_err) + + # d F / d vpa + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, Gam_vpa[:,ivperp], vpa, vpa_spectral) + @. pdf_buffer_1[:,ivperp] = vpa.scratch end - end - pdf_in = dfns - dens_in[1] = get_density(dfns,vpa,vperp) - upar_in[1] = get_upar(dfns,vpa,vperp,dens_in[1]) - ppar_in = get_ppar(dfns,vpa,vperp,upar_in[1],mi) - pperp_in = get_pperp(dfns,vpa,vperp,mi) - pres_in = pressure(ppar_in,pperp_in) - vths_in[1] = get_vth(pres_in,dens_in[1],mi) - - densp_in[1] = get_density(dfnsp,vpa,vperp) - uparp_in[1] = get_upar(dfnsp,vpa,vperp,densp_in[1]) - pparp_in = get_ppar(dfnsp,vpa,vperp,uparp_in[1],mip) - pperpp_in = get_pperp(dfnsp,vpa,vperp,mip) - presp_in = pressure(pparp_in,pperpp_in) - vthsp_in[1] = get_vth(presp_in,densp_in[1],mip) - - println("Isotropic 2D Maxwellian: first argument (derivatives)") - println("dens_test: ", dens_in[1], " dens: ", dens, " error: ", abs(dens_in[1]-dens)) - println("upar_test: ", upar_in[1], " upar: ", upar, " error: ", abs(upar_in[1]-upar)) - println("ppar_test: ", ppar_in, " ppar: ", ppar, " error: ", abs(ppar_in-ppar)) - println("pperp_test: ", pperp_in, " pperp: ", pperp, " error: ", abs(pperp_in-pperp)) - println("vth_test: ", vths_in[1], " vth: ", vths, " error: ", abs(vths_in[1]-vths)) - println("Isotropic 2D Maxwellian: second argument (potentials)") - println("dens_test: ", densp_in[1], " dens: ", densp, " error: ", abs(densp_in[1]-densp)) - println("upar_test: ", uparp_in[1], " upar: ", uparp, " error: ", abs(uparp_in[1]-uparp)) - println("ppar_test: ", pparp_in, " ppar: ", pparp, " error: ", abs(pparp_in-pparp)) - println("pperp_test: ", pperpp_in, " pperp: ", pperpp, " error: ", abs(pperpp_in-pperpp)) - println("vth_test: ", vthsp_in[1], " vth: ", vthsp, " error: ", abs(vthsp_in[1]-vthsp)) - - - for ivperp in 1:nvperp + # d F / d vperp for ivpa in 1:nvpa - Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], - mip,densp_in[1],uparp_in[1],vthsp_in[1], - vpa,vperp,ivpa,ivperp) - Gam_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], - mip,densp_in[1],uparp_in[1],vthsp_in[1], - vpa,vperp,ivpa,ivperp) + @views derivative!(vperp.scratch, Gam_vperp[ivpa,:], vperp, vperp_spectral) + @. pdf_buffer_2[ivpa,:] = vperp.scratch/vperp.grid end - end - - # d F / d vpa - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, pdf_in[:,ivperp], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp] = vpa.scratch - end - # d F / d vperp - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, pdf_in[ivpa,:], vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:] = vperp.scratch - end - - for ivperp in 1:nvperp - for ivpa in 1:nvpa - ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian - ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, - Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(densp_in[:], - uparp_in[:],vthsp_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) - - # now form the collisional fluxes at this s,z,r - ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp], - pdf_buffer_1[ivpa,ivperp],pdf_buffer_2[ivpa,ivperp], - Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - mi,mip,vperp.grid[ivperp]) ) - - - d2Gdvpa2[ivpa,ivperp] = Rosenbluth_d2Gdvpa2 - d2Gdvperpdvpa[ivpa,ivperp] = Rosenbluth_d2Gdvperpdvpa - d2Gdvperp2[ivpa,ivperp] = Rosenbluth_d2Gdvperp2 - dHdvpa[ivpa,ivperp] = Rosenbluth_dHdvpa - dHdvperp[ivpa,ivperp] = Rosenbluth_dHdvperp - Gam_vpa[ivpa,ivperp] = Cflux_vpa - Gam_vperp[ivpa,ivperp] = Cflux_vperp + + @. Cssp = pdf_buffer_1 + pdf_buffer_2 + @. Cssp_err = abs(Cssp) + max_Cssp_err = maximum(Cssp_err) + zero = 1.0e-6 + if ( abs(dens - densp) > zero || abs(upar - uparp) > zero || abs(vths - vthsp) > zero) + println("Cssp test not supported for F_Ms /= F_Ms', ignore result") end + println("max(Cssp_err): ",max_Cssp_err) + + + #if max_Gam_vpa_err > zero + @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Cssp.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vpa_err.pdf") + savefig(outfile) + #end + #if max_Gam_vperp_err > zero + @views heatmap(vperp.grid, vpa.grid, Gam_vperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Gam_vperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_Gam_vperp_err.pdf") + savefig(outfile) + #end + return max_Gam_vpa_err, max_Gam_vperp_err, max_Cssp_err end - @. Gam_vpa_err = abs(Gam_vpa - Gam_vpa_Maxwell) - @. Gam_vperp_err = abs(Gam_vperp - Gam_vperp_Maxwell) - max_Gam_vpa_err = maximum(Gam_vpa_err) - max_Gam_vperp_err = maximum(Gam_vperp_err) - println("max(Gam_vpa_err): ",max_Gam_vpa_err) - println("max(Gam_vperp_err): ",max_Gam_vperp_err) - - # d F / d vpa - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, Gam_vpa[:,ivperp], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp] = vpa.scratch - end - # d F / d vperp - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, Gam_vperp[ivpa,:], vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:] = vperp.scratch/vperp.grid + if test_Rosenbluth_integrals + ((max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, + max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, + max_d2Gdvperpdvpa_err) = test_Rosenbluth_potentials(nelement,ngrid)) end - @. Cssp = pdf_buffer_1 + pdf_buffer_2 - @. Cssp_err = abs(Cssp) - max_Cssp_err = maximum(Cssp_err) - zero = 1.0e-6 - if ( abs(dens - densp) > zero || abs(upar - uparp) > zero || abs(vths - vthsp) > zero) - println("Cssp test not supported for F_Ms /= F_Ms', ignore result") + if test_collision_operator_fluxes + ((max_Gam_vpa_err, max_Gam_vperp_err, max_Cssp_err) + = test_collision_operator(nelement,ngrid)) end - println("max(Cssp_err): ",max_Cssp_err) - - - #if max_Gam_vpa_err > zero - @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Cssp.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa_err.pdf") - savefig(outfile) - #end - #if max_Gam_vperp_err > zero - @views heatmap(vperp.grid, vpa.grid, Gam_vperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp_err.pdf") - savefig(outfile) - #end - - ## evaluate the collision operator with numerically computed G & H #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, From 59f6c4795e49ef3c3f0cc4ab3fecf44e5ffed2c3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 23 Jun 2023 11:26:21 +0100 Subject: [PATCH 035/331] Addition of plots which demonstrate the convergence (or lack thereof) of the calculation of the coefficients appearing the collision operator, using analytically specified G, and a numerically calculated G. The fluxes are tested for Mawellian distributions with analytical coefficients. --- fkpl_test.jl | 120 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 11 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 6ea785dcd..89068094b 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -45,6 +45,12 @@ function get_vth(pres,dens,mass) return sqrt(pres/(dens*mass)) end +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) # #Gamma = 0.0 # #return Gamma @@ -211,8 +217,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ test_Rosenbluth_integrals = true test_collision_operator_fluxes = true - ngrid = 9 - nelement = 8 + #ngrid = 9 + #nelement = 8 function test_Rosenbluth_potentials(nelement,ngrid;numerical_G = false) vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) @@ -563,7 +569,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max(Cssp_err): ",max_Cssp_err) - #if max_Gam_vpa_err > zero + if max_Gam_vpa_err > zero && false @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Cssp.pdf") @@ -580,8 +586,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Gam_vpa_err.pdf") savefig(outfile) - #end - #if max_Gam_vperp_err > zero + end + if max_Gam_vperp_err > zero && false @views heatmap(vperp.grid, vpa.grid, Gam_vperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Gam_vperp.pdf") @@ -594,19 +600,111 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("fkpl_Gam_vperp_err.pdf") savefig(outfile) - #end + end return max_Gam_vpa_err, max_Gam_vperp_err, max_Cssp_err end if test_Rosenbluth_integrals - ((max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, - max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, - max_d2Gdvperpdvpa_err) = test_Rosenbluth_potentials(nelement,ngrid)) + ngrid = 8 + nscan = 5 + nelement_list = Int[2, 4, 8, 16, 32] + max_G_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_H_check_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_label = L"(1/N_{el})^{n_g - 1}" + + + for iscan in 1:nscan + nelement = nelement_list[iscan] + ((max_G_err[iscan], max_H_err[iscan], + max_H_check_err[iscan], max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan]) + = test_Rosenbluth_potentials(nelement,ngrid)) + end + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + plot(nelement_list, [max_G_err,max_H_check_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err, expected], + xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_analytical_test.pdf" + savefig(outfile) + println(outfile) + + for iscan in 1:nscan + nelement = nelement_list[iscan] + ((max_G_err[iscan], max_H_err[iscan], + max_H_check_err[iscan], max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan]) + = test_Rosenbluth_potentials(nelement,ngrid,numerical_G = true)) + end + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + plot(nelement_list, [max_G_err,max_H_check_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err, expected], + xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_numerical_test.pdf" + savefig(outfile) + println(outfile) end if test_collision_operator_fluxes - ((max_Gam_vpa_err, max_Gam_vperp_err, max_Cssp_err) - = test_collision_operator(nelement,ngrid)) + ngrid = 8 + nscan = 5 + nelement_list = Int[2, 4, 8, 16, 32] + max_Gam_vpa_err = Array{mk_float,1}(undef,nscan) + max_Gam_vperp_err = Array{mk_float,1}(undef,nscan) + max_Cssp_err = Array{mk_float,1}(undef,nscan) + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + + for iscan in 1:nscan + ((max_Gam_vpa_err[iscan], max_Gam_vperp_err[iscan], max_Cssp_err[iscan]) + = test_collision_operator(nelement_list[iscan],ngrid)) + end + fontsize = 10 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Gam_vpa_label = L"\epsilon(\Gamma_{\|\|})" + Gam_vperp_label = L"\epsilon(\Gamma_{\perp})" + Cssp_err_label = L"\epsilon(C)" + expected_label = L"(1/N_{el})^{n_g - 1}" + plot(nelement_list, [max_Gam_vpa_err,max_Gam_vperp_err, expected], + xlabel=xlabel, label=[Gam_vpa_label Gam_vperp_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_fluxes_test.pdf" + savefig(outfile) + println(outfile) end ## evaluate the collision operator with numerically computed G & H #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") From 51d41b26e9733c46e52b49b5d3e89a465b1f24fb Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 27 Jun 2023 09:39:37 +0100 Subject: [PATCH 036/331] Redefine Gamma_perp flux to exclude the vperp that comes from the Jacobian in the divergence. --- fkpl_test.jl | 21 +++++++++++---------- src/fokker_planck.jl | 11 ++++++----- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 89068094b..cf4798c81 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -215,7 +215,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return vpa, vperp, vpa_spectral, vperp_spectral end - test_Rosenbluth_integrals = true + test_Rosenbluth_integrals = false#true test_collision_operator_fluxes = true #ngrid = 9 #nelement = 8 @@ -449,12 +449,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ mi = 1.0 vths = get_vth(pres,dens,mi) # second argument (potentials) - densp = 2.0#3.0/4.0 - uparp = 1.0/3.0#3.0/4.0 - pparp = 4.0/3.0 - pperpp = 4.0/3.0 + densp = 1.0 #2.0#3.0/4.0 + uparp = 2.0/3.0#1.0/3.0#3.0/4.0 + pparp = 2.0/3.0#4.0/3.0 + pperpp = 2.0/3.0#4.0/3.0 presp = get_pressure(pparp,pperpp) - mip = 1.5 + mip = 1.0#1.5 vthsp = get_vth(presp,densp,mip) for ivperp in 1:vperp.n for ivpa in 1:vpa.n @@ -528,7 +528,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ pdf_buffer_1[ivpa,ivperp],pdf_buffer_2[ivpa,ivperp], Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - mi,mip,vperp.grid[ivperp]) ) + mi,mip) ) d2Gdvpa2[ivpa,ivperp] = Rosenbluth_d2Gdvpa2 @@ -548,14 +548,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max(Gam_vpa_err): ",max_Gam_vpa_err) println("max(Gam_vperp_err): ",max_Gam_vperp_err) - # d F / d vpa + # d Gam_|| / d vpa for ivperp in 1:nvperp @views derivative!(vpa.scratch, Gam_vpa[:,ivperp], vpa, vpa_spectral) @. pdf_buffer_1[:,ivperp] = vpa.scratch end - # d F / d vperp + # (1/vperp) d vperp Gam_perp / d vperp for ivpa in 1:nvpa - @views derivative!(vperp.scratch, Gam_vperp[ivpa,:], vperp, vperp_spectral) + @views @. vperp.scratch2 = vperp.grid*Gam_vperp[ivpa,:] + @views derivative!(vperp.scratch, vperp.scratch2, vperp, vperp_spectral) @. pdf_buffer_2[ivpa,:] = vperp.scratch/vperp.grid end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index e7194f6ac..0212f1aff 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -200,12 +200,12 @@ calculates the collisional fluxes given input F_s and G_sp, H_sp """ function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dHdvpa,dHdvperp, - ms,msp,vperp_val) + ms,msp) # fill in value at (ivpa,ivperp) Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa - 2.0*(ms/msp)*F*dHdvpa #Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa # - 2.0*(ms/msp)*F*dHdvpa #Cflux_vpa = - 2.0*(ms/msp)*F*dHdvpa - Cflux_vperp = vperp_val* ( dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp ) + Cflux_vperp = dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp return Cflux_vpa, Cflux_vperp end @@ -392,7 +392,7 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ pdf_buffer_1[ivpa,ivperp,iz,ir,is],pdf_buffer_2[ivpa,ivperp,iz,ir,is], Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - mi,mip,vperp.grid[ivperp]) ) + mi,mip) ) # now overwrite the buffer arrays with the local values as we no longer need dFdvpa or dFdvperp at s,r,z pdf_buffer_1[ivpa,ivperp,iz,ir,is] = Cflux_vpa @@ -412,7 +412,8 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ # (1/vperp) d Cflux_vperp / d vperp begin_s_r_z_vpa_region() @loop_s_r_z_vpa is ir iz ivpa begin - @views derivative!(vperp.scratch, pdf_buffer_2[ivpa,:,iz,ir,is], vperp, vperp_spectral) + @views @. vperp.scratch2 = vperp.grid*pdf_buffer_2[ivpa,:,iz,ir,is] + @views derivative!(vperp.scratch, vperp.scratch2, vperp, vperp_spectral) @. pdf_buffer_2[ivpa,:,iz,ir,is] = vperp.scratch[:]/vperp.grid[:] end @@ -524,7 +525,7 @@ function Cflux_vperp_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_fl vpa,vperp,ivpa,ivperp) etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) - prefac = -2.0*(vperp.grid[ivperp]^2)*denss*denssp*exp( -eta^2)/(vthsp*vths^5) + prefac = -2.0*(vperp.grid[ivperp])*denss*denssp*exp( -eta^2)/(vthsp*vths^5) (fac = (d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) + ((uparsp - upars)*(vpa.grid[ivpa]-uparsp)/vthsp^2)*ddGddeta(etap)/etap ) Cflux = prefac*fac From 97c788e607320c852c4dd5fb9c40b83db43166c4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 27 Jun 2023 09:40:54 +0100 Subject: [PATCH 037/331] Changes for Krook operator MMS test plot --- run_MMS_test.jl | 9 ++++++++- src/plot_MMS_sequence.jl | 35 ++++++++++++++++++++++++++++------- src/post_processing.jl | 13 +++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/run_MMS_test.jl b/run_MMS_test.jl index 6f3abed4c..50f8f9a14 100644 --- a/run_MMS_test.jl +++ b/run_MMS_test.jl @@ -14,7 +14,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ #test_option = "collisionless_wall-1D-1V-constant-Er" #test_option = "collisionless_wall-1D-1V-constant-Er-zngrid-5" #test_option = "collisionless_wall-1D-1V-constant-Er-ngrid-5" - test_option = "collisionless_wall-1D-1V-constant-Er-ngrid-5-opt" + #test_option = "collisionless_wall-1D-1V-constant-Er-ngrid-5-opt" + test_option = "krook_wall-1D-2V" #test_option = "collisionless_wall-1D-3V" #test_option = "collisionless_wall-2D-3V" #test_option = "collisionless_wall-2D-3V-Er-zero-at-plate" @@ -171,6 +172,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ ] scan_type = "vpaz_nelement" scan_name = "1D-1V-wall_cheb" + elseif test_option == "krook_wall-1D-2V" + # Krook wall test, no sheath for electrons, no radial coordinate + path_list = ["runs/1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook","runs/1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook", + "runs/1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook" ] + scan_type = "vpavperpz_nelement" + scan_name = "1D-2V-wall_cheb_krook" end mk.plot_MMS_sequence.get_MMS_error_data(path_list,scan_type,scan_name) end diff --git a/src/plot_MMS_sequence.jl b/src/plot_MMS_sequence.jl index d9dd849a5..4b9d9f6e8 100644 --- a/src/plot_MMS_sequence.jl +++ b/src/plot_MMS_sequence.jl @@ -18,7 +18,8 @@ using ..post_processing: compare_charged_pdf_symbolic_test, compare_fields_symbo using ..post_processing: compare_moments_symbolic_test, compare_neutral_pdf_symbolic_test using ..post_processing: read_distributed_zr_data!, construct_global_zr_grids using ..post_processing: allocate_global_zr_neutral_moments, allocate_global_zr_charged_moments -using ..post_processing: allocate_global_zr_fields, get_geometry_and_composition, get_coords_nelement +using ..post_processing: allocate_global_zr_fields, get_geometry_and_composition +using ..post_processing: get_coords_nelement, get_coords_ngrid using ..array_allocation: allocate_float using ..type_definitions: mk_float, mk_int using ..load_data: open_readonly_output_file @@ -32,6 +33,12 @@ using ..moment_kinetics_input: mk_input, read_input_file import Base: get +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + # assume in function below that we have a list of simulations # where only a single nelement parameter is varied # we plot the MMS error measurements as a fn of nelement @@ -46,7 +53,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) neutral_density_error_sequence = zeros(mk_float,nsimulation) neutral_pdf_error_sequence = zeros(mk_float,nsimulation) nelement_sequence = zeros(mk_int,nsimulation) - + expected_scaling = zeros(mk_float,nsimulation) # declare local variables that are needed outside "nsimulation" loop below local n_neutral_species @@ -65,6 +72,8 @@ function get_MMS_error_data(path_list,scan_type,scan_name) # composition, species, collisions, geometry, drive_input = mk_input(scan_input) z_nelement, r_nelement, vpa_nelement, vperp_nelement, vz_nelement, vr_nelement, vzeta_nelement = get_coords_nelement(scan_input) + z_ngrid, r_ngrid, vpa_ngrid, vperp_ngrid, + vz_ngrid, vr_ngrid, vzeta_ngrid = get_coords_ngrid(scan_input) if scan_type == "vpa_nelement" # get the number of elements for plot nelement_sequence[isim] = vpa_nelement @@ -112,6 +121,13 @@ function get_MMS_error_data(path_list,scan_type,scan_name) else println("ERROR: scan_type = ",scan_type," requires vpa_nelement = z_nelement/4") end + elseif scan_type == "vpavperpz_nelement" + nelement = z_nelement + if nelement == vpa_nelement && nelement == vperp_nelement + nelement_sequence[isim] = nelement + else + println("ERROR: scan_type = ",scan_type," requires vpa_nelement = vperp_nelement = z_nelement") + end elseif scan_type == "vpaz_nelement" nelement = z_nelement if nelement == vpa_nelement @@ -129,7 +145,8 @@ function get_MMS_error_data(path_list,scan_type,scan_name) else println("ERROR: scan_type = ",scan_type," is unsupported") end - + expected_nelement_scaling!(expected_scaling,nelement_sequence,z_ngrid,nsimulation) + # open the netcdf file and give it the handle 'fid' fid = open_readonly_output_file(run_name,"moments") # load block data on iblock=0 @@ -209,7 +226,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) end geometry, composition = get_geometry_and_composition(scan_input,n_ion_species,n_neutral_species) - manufactured_solns_list = manufactured_solutions(Lr_in,Lz,r_bc,z_bc,geometry,composition,nr_local) + manufactured_solns_list = manufactured_solutions(Lr_in,Lz,r_bc,z_bc,geometry,composition,nr_local,nvperp) dfni_func = manufactured_solns_list.dfni_func densi_func = manufactured_solns_list.densi_func dfnn_func = manufactured_solns_list.dfnn_func @@ -292,6 +309,8 @@ function get_MMS_error_data(path_list,scan_type,scan_name) ylabel_phi = L"\varepsilon(\widetilde{\phi})" ylabel_Er = L"\varepsilon(\widetilde{E}_r)" ylabel_Ez = L"\varepsilon(\widetilde{E}_z)" + ylabel_Ez = L"\varepsilon(\widetilde{E}_z)" + expected_label = L"(1/N_{el})^{n_g - 1}" if scan_type == "vpa_nelement" xlabel = L"v_{||}"*" "*L"N_{element}" elseif scan_type == "vperp_nelement" @@ -308,6 +327,8 @@ function get_MMS_error_data(path_list,scan_type,scan_name) xlabel = L"z"*" "*L"N_{element}" elseif scan_type == "zr_nelement" xlabel = L"z "*" & "*L"r "*" "*L"N_{element}" + elseif scan_type == "vpavperpz_nelement" + xlabel = L"N_{element}(z) = N_{element}(v_\perp) = N_{element}(v_{||})" elseif scan_type == "vpazr_nelement0.25" xlabel = L"N_{element}(z) = N_{element}(r) = N_{element}(v_{||})/4" elseif scan_type == "vpaz_nelement0.25" @@ -336,7 +357,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) ytick_sequence = Array([1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) elseif scan_name == "2D-sound-wave_cheb_cxiz" ytick_sequence = Array([1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - elseif scan_name == "1D-1V-wall_cheb" + elseif scan_name == "1D-1V-wall_cheb" || scan_name == "1D-2V-wall_cheb_krook" ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) elseif scan_name == "1D-3V-wall_cheb-updated" || scan_name == "1D-3V-wall_cheb-new-dfni-Er" || scan_name == "1D-3V-wall_cheb-new-dfni" || scan_name == "2D-sound-wave_cheb" ytick_sequence = Array([1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) @@ -370,8 +391,8 @@ function get_MMS_error_data(path_list,scan_type,scan_name) savefig(outfile) println(outfile) - plot(nelement_sequence, [ion_density_error_sequence,phi_error_sequence,Ez_error_sequence,ion_pdf_error_sequence], xlabel=xlabel, - label=[ylabel_ion_density ylabel_phi ylabel_Ez ylabel_ion_pdf], ylabel="", + plot(nelement_sequence, [ion_density_error_sequence,phi_error_sequence,Ez_error_sequence,ion_pdf_error_sequence,expected_scaling], xlabel=xlabel, + label=[ylabel_ion_density ylabel_phi ylabel_Ez ylabel_ion_pdf expected_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_sequence, nelement_sequence), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize) outfile = outprefix*"_fields_and_ion_pdf_no_Er.pdf" diff --git a/src/post_processing.jl b/src/post_processing.jl index d021954b9..c2f070921 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -13,6 +13,7 @@ export allocate_global_zr_charged_moments export allocate_global_zr_neutral_moments export allocate_global_zr_fields export get_coords_nelement +export get_coords_ngrid # packages using Plots @@ -224,6 +225,18 @@ function get_coords_nelement(scan_input) return z_nelement, r_nelement, vpa_nelement, vperp_nelement, vz_nelement, vr_nelement, vzeta_nelement end +function get_coords_ngrid(scan_input) + # use 1 as default because these values should be set in input .toml + z_ngrid = get(scan_input, "z_ngrid", 1) + r_ngrid = get(scan_input, "r_ngrid", 1) + vpa_ngrid = get(scan_input, "vpa_ngrid", 1) + vperp_ngrid = get(scan_input, "vperp_ngrid", 1) + vz_ngrid = get(scan_input, "vz_ngrid", 1) + vr_ngrid = get(scan_input, "vr_ngrid", 1) + vzeta_ngrid = get(scan_input, "vzeta_ngrid", 1) + return z_ngrid, r_ngrid, vpa_ngrid, vperp_ngrid, vz_ngrid, vr_ngrid, vzeta_ngrid +end + function get_geometry_and_composition(scan_input,n_ion_species,n_neutral_species) # set geometry_input # MRH need to get this in way that does not duplicate code From a6f06ccbbb37de8be113cb31d9c2f28c7fbdf52c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 27 Jun 2023 09:42:13 +0100 Subject: [PATCH 038/331] Krook operator test input files --- ...MS_nel_r_1_z_16_vpa_16_vperp_16_krook.toml | 91 +++++++++++++++++++ ...l_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook.toml | 91 +++++++++++++++++++ ...l_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook.toml | 91 +++++++++++++++++++ ...l_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook.toml | 91 +++++++++++++++++++ 4 files changed, 364 insertions(+) create mode 100644 runs/1D-wall_MMS_nel_r_1_z_16_vpa_16_vperp_16_krook.toml create mode 100644 runs/1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook.toml create mode 100644 runs/1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook.toml create mode 100644 runs/1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook.toml diff --git a/runs/1D-wall_MMS_nel_r_1_z_16_vpa_16_vperp_16_krook.toml b/runs/1D-wall_MMS_nel_r_1_z_16_vpa_16_vperp_16_krook.toml new file mode 100644 index 000000000..f7bc243f0 --- /dev/null +++ b/runs/1D-wall_MMS_nel_r_1_z_16_vpa_16_vperp_16_krook.toml @@ -0,0 +1,91 @@ +use_manufactured_solns_for_advance = true +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +run_name = "1D-wall_MMS_nel_r_1_z_16_vpa_16_vperp_16_krook" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +nuii_krook = 1.0 +nstep = 8000 +dt = 0.000125 +nwrite = 800 +nwrite_dfns = 800 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 17 +z_nelement = 16 +z_nelement_local = 16 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 17 +vpa_nelement = 16 +vpa_L = 12.0 +vpa_bc = "zero" +vpa_discretization = "chebyshev_pseudospectral" +vperp_ngrid = 17 +vperp_nelement = 16 +vperp_L = 6.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +vperp_discretization = "chebyshev_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 diff --git a/runs/1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook.toml b/runs/1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook.toml new file mode 100644 index 000000000..927a1a488 --- /dev/null +++ b/runs/1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook.toml @@ -0,0 +1,91 @@ +use_manufactured_solns_for_advance = true +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +run_name = "1D-wall_MMS_nel_r_1_z_2_vpa_2_vperp_2_krook" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +nuii_krook = 1.0 +nstep = 1000 +dt = 0.001 +nwrite = 100 +nwrite_dfns = 100 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 17 +z_nelement = 2 +z_nelement_local = 2 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 17 +vpa_nelement = 2 +vpa_L = 12.0 +vpa_bc = "zero" +vpa_discretization = "chebyshev_pseudospectral" +vperp_ngrid = 17 +vperp_nelement = 2 +vperp_L = 6.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +vperp_discretization = "chebyshev_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 diff --git a/runs/1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook.toml b/runs/1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook.toml new file mode 100644 index 000000000..3a7eb17db --- /dev/null +++ b/runs/1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook.toml @@ -0,0 +1,91 @@ +use_manufactured_solns_for_advance = true +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +run_name = "1D-wall_MMS_nel_r_1_z_4_vpa_4_vperp_4_krook" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +nuii_krook = 1.0 +nstep = 2000 +dt = 0.0005 +nwrite = 200 +nwrite_dfns = 200 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 17 +z_nelement = 4 +z_nelement_local = 4 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 17 +vpa_nelement = 4 +vpa_L = 12.0 +vpa_bc = "zero" +vpa_discretization = "chebyshev_pseudospectral" +vperp_ngrid = 17 +vperp_nelement = 4 +vperp_L = 6.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +vperp_discretization = "chebyshev_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 diff --git a/runs/1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook.toml b/runs/1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook.toml new file mode 100644 index 000000000..ea08c71ea --- /dev/null +++ b/runs/1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook.toml @@ -0,0 +1,91 @@ +use_manufactured_solns_for_advance = true +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +run_name = "1D-wall_MMS_nel_r_1_z_8_vpa_8_vperp_8_krook" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +nuii_krook = 1.0 +nstep = 4000 +dt = 0.00025 +nwrite = 400 +nwrite_dfns = 400 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 17 +z_nelement = 8 +z_nelement_local = 8 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 17 +vpa_nelement = 8 +vpa_L = 12.0 +vpa_bc = "zero" +vpa_discretization = "chebyshev_pseudospectral" +vperp_ngrid = 17 +vperp_nelement = 8 +vperp_L = 6.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +vperp_discretization = "chebyshev_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 From cf2ec3db884358f5e6251889d01584668f3a0e1d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 1 Jul 2023 12:03:37 +0100 Subject: [PATCH 039/331] Add an array containing the igrid index on the full grid, as a function of element and local grid index --- src/coordinates.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/coordinates.jl b/src/coordinates.jl index 8251fe428..4b9d2c177 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -47,6 +47,8 @@ struct coordinate imin::Array{mk_int,1} # imax[j] contains the maximum index on the full grid for element j imax::Array{mk_int,1} + # igrid_full[i,j] contains the index of the full grid for the elemental grid point i, on element j + igrid_full::Array{mk_int,2} # discretization option for the grid discretization::String # if the discretization is finite differences, fd_option provides the precise scheme @@ -105,7 +107,7 @@ function define_coordinate(input, parallel_io::Bool=false) input.nelement_local, n_local) # obtain (local) index mapping from the grid within each element # to the full grid - imin, imax = elemental_to_full_grid_map(input.ngrid, input.nelement_local) + imin, imax, igrid_full = elemental_to_full_grid_map(input.ngrid, input.nelement_local) # initialize the grid and the integration weights associated with the grid # also obtain the Chebyshev theta grid and spacing if chosen as discretization option grid, wgts, uniform_grid = init_grid(input.ngrid, input.nelement_global, @@ -147,7 +149,7 @@ function define_coordinate(input, parallel_io::Bool=false) end return 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, input.discretization, input.fd_option, input.cheb_option, + 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), scratch_2d, copy(scratch_2d), advection, send_buffer, receive_buffer, input.comm, local_io_range, global_io_range) @@ -293,6 +295,7 @@ indices on the full grid for each element function elemental_to_full_grid_map(ngrid, nelement) imin = allocate_int(nelement) imax = allocate_int(nelement) + igrid_full = allocate_int(ngrid, nelement) @inbounds begin # the first element contains ngrid entries imin[1] = 1 @@ -305,8 +308,14 @@ function elemental_to_full_grid_map(ngrid, nelement) imax[i] = imin[i] + ngrid - 2 end end + + for j in 1:nelement + for i in 1:ngrid + igrid_full[i,j] = i + (j - 1)*(ngrid - 1) + end + end end - return imin, imax + return imin, imax, igrid_full end end From b942bec2867be4d290c2f67f0a215090fa449ecc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 1 Jul 2023 12:05:13 +0100 Subject: [PATCH 040/331] Integration script where the Rosenbluth potential G is computed using a Lagrange polynomial expansion of the distrubution F. In principle, this allows for more accuracte calculation of G without increasing the number of collocation points in the main solver. --- fkpl_test.jl | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index cf4798c81..4e5ec36ff 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -3,7 +3,8 @@ using Plots using LaTeXStrings using Measures using MPI -using SpecialFunctions: erf +using SpecialFunctions: erf, ellipe +using FastGaussQuadrature function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth @@ -216,7 +217,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end test_Rosenbluth_integrals = false#true - test_collision_operator_fluxes = true + test_collision_operator_fluxes = false#true + test_Lagrange_integral = true #ngrid = 9 #nelement = 8 @@ -390,6 +392,170 @@ if abspath(PROGRAM_FILE) == @__FILE__ return max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err end + function test_Lagrange_Rosenbluth_potentials(ngrid,nelement) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) + Gs = Array{mk_float,2}(undef,nvpa,nvperp) + G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = Array{mk_float,2}(undef,nvpa,nvperp) + + # set up test Maxwellian + dens = 1.0 #3.0/4.0 + upar = 0.0 #2.0/3.0 + ppar = 1.0 #2.0/3.0 + pperp = 1.0# 2.0/3.0 + pres = get_pressure(ppar,pperp) + mi = 1.0 + vths = get_vth(pres,dens,mi) + + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) + end + end + + function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax + end + + function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes + end + """ + Lagrange polynomial + args: + j - index of l_j from list of nodes + x_nodes - array of x node values + x - point where interpolated value is returned + """ + function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly + end + + function get_scaled_x_w!(x_scaled, w_scaled, x, w, node_min, node_max) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled = scale*x + shift + @. w_scaled = scale*w + return nothing + end + + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x, w = gausslegendre(nquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + + # precalculated weights, integrating over Lagrange polynomials + for ivperp in 1:nvperp + for ivpa in 1:nvpa + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + # loop over elements and grid points within elements on primed coordinate + for ielement_vperp in 1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperp) + vperp_max = vperp_nodes[end] + if ielement_vperp > 1 # Gauss-Lobatto + vperp_min = vperp_nodes[1] + else # adjust for the Gauss-Radau element + vperp_min = 0.0 + end + get_scaled_x_w!(x_vperp, w_vperp, x, w, vperp_min, vperp_max) + + for ielement_vpa in 1:vpa.nelement_local + + vpa_nodes = get_nodes(vpa,ielement_vpa) + # assumme Gauss-Lobatto elements + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + get_scaled_x_w!(x_vpa, w_vpa, x, w, vpa_min, vpa_max) + + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad + for kvpa in 1:nquad + denom = (vpa_val - x_vpa[kvpa])^2 + (vperp_val + x_vperp[kvperp])^2 + mm = 4.0*vperp_val*x_vperp[kvperp]/denom + prefac = sqrt(denom) + elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi + + (G_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + end + end + end + end + end + end + end + end + + # use precalculated weights to calculate Gs using nodal values of fs + for ivperp in 1:nvperp + for ivpa in 1:nvpa + Gs[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] + end + end + end + end + + @. G_err = abs(Gs - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + return nothing + end + function test_collision_operator(nelement,ngrid) vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) @@ -707,6 +873,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) println(outfile) end + + if test_Lagrange_integral + ngrid = 9 + nelement = 6 + test_Lagrange_Rosenbluth_potentials(ngrid,nelement) + end ## evaluate the collision operator with numerically computed G & H #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, From 66ab79baefac0edc6b73de4eeff01377808f9ab3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 4 Jul 2023 11:59:53 +0100 Subject: [PATCH 041/331] Parallelised outer loop of Rosenbluth potential test integration routine. --- fkpl_test.jl | 111 +++++++++++++++++++++++++++---------------- src/communication.jl | 1 + 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 4e5ec36ff..0bd3d265e 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -5,6 +5,25 @@ using Measures using MPI using SpecialFunctions: erf, ellipe using FastGaussQuadrature +using Dates + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth @@ -118,21 +137,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") - import moment_kinetics - using moment_kinetics.input_structs: grid_input, advection_input - using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! - using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! - #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! - using moment_kinetics.fokker_planck: init_fokker_planck_collisions - using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients - using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs - using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! - using moment_kinetics.type_definitions: mk_float, mk_int - using moment_kinetics.calculus: derivative! - using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure - function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) for ivperp in 1:vperp.n @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) @@ -205,7 +209,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate structs - println("made inputs") + #println("made inputs") vpa = define_coordinate(vpa_input) vperp = define_coordinate(vperp_input) #println(vperp.grid) @@ -399,9 +403,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ nvperp = vperp.n nvpa = vpa.n + # Set up MPI + initialize_comms!() + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) - G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) - Gs = Array{mk_float,2}(undef,nvpa,nvperp) + #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) + G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Gs = allocate_shared_float(nvpa,nvperp) + #Gs = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) G_err = Array{mk_float,2}(undef,nvpa,nvperp) @@ -477,8 +495,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) # precalculated weights, integrating over Lagrange polynomials - for ivperp in 1:nvperp - for ivpa in 1:nvpa + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + #for ivperp in 1:nvperp + # for ivpa in 1:nvpa vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @. G_weights[ivpa,ivperp,:,:] = 0.0 @@ -523,36 +543,45 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end - end + #end end + _block_synchronize() + # use precalculated weights to calculate Gs using nodal values of fs - for ivperp in 1:nvperp - for ivpa in 1:nvpa + @loop_vperp_vpa ivperp ivpa begin + #for ivperp in 1:nvperp + #for ivpa in 1:nvpa Gs[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] end end - end + #end end - @. G_err = abs(Gs - G_Maxwell) - max_G_err = maximum(G_err) - println("max_G_err: ",max_G_err) - @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_err.pdf") - savefig(outfile) + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + begin_serial_region() + @serial_region begin + @. G_err = abs(Gs - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end return nothing end @@ -875,8 +904,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral - ngrid = 9 - nelement = 6 + ngrid = 5 + nelement = 4 test_Lagrange_Rosenbluth_potentials(ngrid,nelement) end ## evaluate the collision operator with numerically computed G & H diff --git a/src/communication.jl b/src/communication.jl index c1ccb3f78..198c4f38a 100644 --- a/src/communication.jl +++ b/src/communication.jl @@ -16,6 +16,7 @@ export allocate_shared, block_rank, block_size, comm_block, comm_inter_block, iblock_index, comm_world, finalize_comms!, initialize_comms!, global_rank, MPISharedArray, global_size export setup_distributed_memory_MPI +export _block_synchronize using MPI using SHA From fe934c1983763a0ace942e3506ce9de8d4ce2fc0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 5 Jul 2023 09:23:37 +0100 Subject: [PATCH 042/331] Added calculation of H by numerical integration, using Lagrange interpolating polynomials to avoid the integrand quadrature points coinciding with the collocation points. The results suggest that H is computed properly with similar errors to the calculation of G for low resolution, but the result does not converge rapidly with increasing resolution. This may be due to the fact that the integrand diverges like 1/|v - v_prime|. --- fkpl_test.jl | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 0bd3d265e..85ec3c624 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -3,7 +3,7 @@ using Plots using LaTeXStrings using Measures using MPI -using SpecialFunctions: erf, ellipe +using SpecialFunctions: erf, ellipe, ellipk using FastGaussQuadrature using Dates @@ -423,6 +423,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) G_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Hs = allocate_shared_float(nvpa,nvperp) + #Gs = Array{mk_float,2}(undef,nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = Array{mk_float,2}(undef,nvpa,nvperp) + # set up test Maxwellian dens = 1.0 #3.0/4.0 upar = 0.0 #2.0/3.0 @@ -436,6 +442,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivpa in 1:nvpa fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) end end @@ -502,6 +509,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @. G_weights[ivpa,ivperp,:,:] = 0.0 + @. H_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate for ielement_vperp in 1:vperp.nelement_local @@ -532,11 +540,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ denom = (vpa_val - x_vpa[kvpa])^2 + (vperp_val + x_vperp[kvperp])^2 mm = 4.0*vperp_val*x_vperp[kvperp]/denom prefac = sqrt(denom) - elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi + G_elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi + H_elliptic_integral_factor = 2.0*ellipk(mm)/(pi*prefac) (G_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* - elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + G_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + + (H_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + H_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) end end end @@ -553,9 +566,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ #for ivperp in 1:nvperp #for ivpa in 1:nvpa Gs[ivpa,ivperp] = 0.0 + Hs[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] + Hs[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] end end #end @@ -566,6 +581,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ end begin_serial_region() @serial_region begin + @. H_err = abs(Hs - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_err: ",max_H_err) + @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_err.pdf") + savefig(outfile) + @. G_err = abs(Gs - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) From fca463846c510b22aa4238672809c638034311c0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 5 Jul 2023 10:17:38 +0100 Subject: [PATCH 043/331] Addition of computation of d2Gdvpa2 and dGdvperp via the Green`s function and integration by parts. The integrand is therefore not divergent anywhere. The cost is that Fs must be differentiated, meaning that this method may break down for sufficiently high order of derivatives. Further experimentation is required to determine if the coefficients dHdvpa and dHdvperp can be obtained by this route. --- fkpl_test.jl | 130 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 32 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 85ec3c624..cd3269db2 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -18,6 +18,7 @@ using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -416,12 +417,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ end fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Gs = allocate_shared_float(nvpa,nvperp) + d2Gsdvpa2 = allocate_shared_float(nvpa,nvperp) + dGsdvperp = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) G_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hs = allocate_shared_float(nvpa,nvperp) @@ -443,8 +453,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(dens,upar,vths,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) end end + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + end + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) + @. dfsdvperp[ivpa,:] = vperp.scratch + end function get_imin_imax(coord,iel) j = iel @@ -509,6 +530,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @. G_weights[ivpa,ivperp,:,:] = 0.0 + @. G1_weights[ivpa,ivperp,:,:] = 0.0 @. H_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate for ielement_vperp in 1:vperp.nelement_local @@ -541,12 +563,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ mm = 4.0*vperp_val*x_vperp[kvperp]/denom prefac = sqrt(denom) G_elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe(mm) - 2.0*(1.0 - mm)*ellipk(mm) )/(3.0*mm) H_elliptic_integral_factor = 2.0*ellipk(mm)/(pi*prefac) (G_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* G_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + G1_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + (H_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* H_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) @@ -565,10 +592,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ @loop_vperp_vpa ivperp ivpa begin #for ivperp in 1:nvperp #for ivpa in 1:nvpa + d2Gsdvpa2[ivpa,ivperp] = 0.0 + dGsdvperp[ivpa,ivperp] = 0.0 Gs[ivpa,ivperp] = 0.0 Hs[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa + d2Gsdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvpa2[ivpap,ivperpp] + dGsdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfsdvperp[ivpap,ivperpp] Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] Hs[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] end @@ -576,42 +607,77 @@ if abspath(PROGRAM_FILE) == @__FILE__ #end end - @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - end begin_serial_region() @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. H_err = abs(Hs - H_Maxwell) max_H_err = maximum(H_err) println("max_H_err: ",max_H_err) - @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_err.pdf") - savefig(outfile) - + if false + @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_err.pdf") + savefig(outfile) + end + @. dGdvperp_err = abs(dGsdvperp - dGdvperp_Maxwell) + max_dGdvperp_err = maximum(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err) + if true + @views heatmap(vperp.grid, vpa.grid, dGsdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvpa2_err = abs(d2Gsdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + if true + @views heatmap(vperp.grid, vpa.grid, d2Gsdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end @. G_err = abs(Gs - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) - @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_err.pdf") - savefig(outfile) + if false + @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end end return nothing end @@ -849,7 +915,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for iscan in 1:nscan - nelement = nelement_list[iscan] + local nelement = nelement_list[iscan] ((max_G_err[iscan], max_H_err[iscan], max_H_check_err[iscan], max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], @@ -876,7 +942,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println(outfile) for iscan in 1:nscan - nelement = nelement_list[iscan] + local nelement = nelement_list[iscan] ((max_G_err[iscan], max_H_err[iscan], max_H_check_err[iscan], max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], @@ -935,8 +1001,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral - ngrid = 5 - nelement = 4 + ngrid = 9 + nelement = 8 test_Lagrange_Rosenbluth_potentials(ngrid,nelement) end ## evaluate the collision operator with numerically computed G & H From 316057b96e2b45cb2d3331569de7421d52df1dec Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 6 Jul 2023 10:21:20 +0100 Subject: [PATCH 044/331] Addition of the numerical calculation of d2Gdvperp2 and d2Gdvperpdvpa. H is computed using the identity which relates H = (1/2) del2 G, with comparable levels of error to the result of calculating the other coefficients. --- fkpl_test.jl | 103 +++++++++++++++++++++++++++++++++++++++---- src/fokker_planck.jl | 2 + 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index cd3269db2..3e9c3be27 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -18,7 +18,7 @@ using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -419,12 +419,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ fs_in = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Gs = allocate_shared_float(nvpa,nvperp) d2Gsdvpa2 = allocate_shared_float(nvpa,nvperp) dGsdvperp = allocate_shared_float(nvpa,nvperp) + d2Gsdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gsdvperp2 = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) G_err = Array{mk_float,2}(undef,nvpa,nvperp) @@ -432,18 +438,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) dGdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hs = allocate_shared_float(nvpa,nvperp) + Hs_from_Gs = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) H_err = Array{mk_float,2}(undef,nvpa,nvperp) # set up test Maxwellian dens = 1.0 #3.0/4.0 - upar = 0.0 #2.0/3.0 - ppar = 1.0 #2.0/3.0 - pperp = 1.0# 2.0/3.0 + upar = 2.0/3.0 + ppar = 2.0/3.0 + pperp = 2.0/3.0 pres = get_pressure(ppar,pperp) mi = 1.0 vths = get_vth(pres,dens,mi) @@ -455,6 +466,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(dens,upar,vths,vpa,vperp,ivpa,ivperp) dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(dens,upar,vths,vpa,vperp,ivpa,ivperp) end end for ivperp in 1:nvperp @@ -465,6 +478,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivpa in 1:vpa.n @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) @. dfsdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fsdvperp2[ivpa,:] = vperp.scratch2 + end + for ivperp in 1:nvperp + @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch end function get_imin_imax(coord,iel) @@ -531,6 +550,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_val = vpa.grid[ivpa] @. G_weights[ivpa,ivperp,:,:] = 0.0 @. G1_weights[ivpa,ivperp,:,:] = 0.0 + @. G2_weights[ivpa,ivperp,:,:] = 0.0 + @. G3_weights[ivpa,ivperp,:,:] = 0.0 @. H_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate for ielement_vperp in 1:vperp.nelement_local @@ -564,6 +585,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ prefac = sqrt(denom) G_elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe(mm) - 2.0*(1.0 - mm)*ellipk(mm) )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe(mm) + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk(mm) )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe(mm) - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk(mm) )/(15.0*mm^2) H_elliptic_integral_factor = 2.0*ellipk(mm)/(pi*prefac) (G_weights[ivpa,ivperp,ivpap,ivperpp] += @@ -574,6 +597,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* G1_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + (G2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + G2_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + + (G3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + G3_elliptic_integral_factor*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + (H_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* H_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) @@ -594,30 +625,52 @@ if abspath(PROGRAM_FILE) == @__FILE__ #for ivpa in 1:nvpa d2Gsdvpa2[ivpa,ivperp] = 0.0 dGsdvperp[ivpa,ivperp] = 0.0 + d2Gsdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gsdvperp2[ivpa,ivperp] = 0.0 Gs[ivpa,ivperp] = 0.0 Hs[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa d2Gsdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvpa2[ivpap,ivperpp] dGsdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfsdvperp[ivpap,ivperpp] + d2Gsdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvperpdvpa[ivpap,ivperpp] + d2Gsdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfsdvperp[ivpap,ivperpp] Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] Hs[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] end end #end + + (Hs_from_Gs[ivpa,ivperp] = 0.5*( d2Gsdvpa2[ivpa,ivperp] + + d2Gsdvperp2[ivpa,ivperp] + + (1.0/vperp.grid[ivperp])*dGsdvperp[ivpa,ivperp])) end + plot_H = true + plot_d2Gdvperp2 = true + plot_d2Gdvperpdvpa = true + plot_dGdvperp = true + plot_d2Gdvpa2 = true + plot_G = true + begin_serial_region() @serial_region begin println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. H_err = abs(Hs - H_Maxwell) max_H_err = maximum(H_err) println("max_H_err: ",max_H_err) - if false + @. H_err = abs(Hs_from_Gs - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_from_G_err: ",max_H_err) + if plot_H @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_H_lagrange.pdf") savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Hs_from_Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_from_G_lagrange.pdf") + savefig(outfile) @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_H_Maxwell.pdf") @@ -627,10 +680,44 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_H_err.pdf") savefig(outfile) end + @. d2Gdvperp2_err = abs(d2Gsdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gsdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) + end + @. d2Gdvperpdvpa_err = abs(d2Gsdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gsdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) + end @. dGdvperp_err = abs(dGsdvperp - dGdvperp_Maxwell) max_dGdvperp_err = maximum(dGdvperp_err) println("max_dGdvperp_err: ",max_dGdvperp_err) - if true + if plot_dGdvperp @views heatmap(vperp.grid, vpa.grid, dGsdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_dGdvperp_lagrange.pdf") @@ -647,7 +734,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvpa2_err = abs(d2Gsdvpa2 - d2Gdvpa2_Maxwell) max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) - if true + if plot_d2Gdvpa2 @views heatmap(vperp.grid, vpa.grid, d2Gsdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") @@ -664,7 +751,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G_err = abs(Gs - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) - if false + if plot_G @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_G_lagrange.pdf") diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 0212f1aff..20cf2d133 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -11,6 +11,8 @@ export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficient export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs export calculate_Rosenbluth_H_from_G! +export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 + using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float From fe8abf1eaf948a1b58b57233118d4ad65ea422f8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 6 Jul 2023 11:03:05 +0100 Subject: [PATCH 045/331] Added calculation of dHdvpa and dHdvperp via differentiation of (1/2)del2 G. The errors are larger than for the G deriviatives (by a factor of 10 for nelement = 8 ngrid = 9), but possibly acceptably so because the absolute errror remains around 1e-5. --- fkpl_test.jl | 99 +++++++++++++++++++++++++++++++++++++++----- src/fokker_planck.jl | 1 + 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 3e9c3be27..539adab9b 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -19,6 +19,7 @@ using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Max using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -413,7 +414,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vzeta=1, vr=1, vz=1) @serial_region begin - println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) end fs_in = Array{mk_float,2}(undef,nvpa,nvperp) @@ -446,15 +447,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hs = allocate_shared_float(nvpa,nvperp) Hs_from_Gs = allocate_shared_float(nvpa,nvperp) + dHsdvpa = allocate_shared_float(nvpa,nvperp) + dHsdvperp = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) H_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end # set up test Maxwellian dens = 1.0 #3.0/4.0 - upar = 2.0/3.0 - ppar = 2.0/3.0 - pperp = 2.0/3.0 + upar = 0.5 #2.0/3.0 + ppar = 1.0 #2.0/3.0 + pperp = 1.0 #2.0/3.0 pres = get_pressure(ppar,pperp) mi = 1.0 vths = get_vth(pres,dens,mi) @@ -468,6 +480,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vths,vpa,vperp,ivpa,ivperp) d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(dens,upar,vths,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(dens,upar,vths,vpa,vperp,ivpa,ivperp) end end for ivperp in 1:nvperp @@ -535,12 +549,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + # get Gauss-Legendre points and weights on (-1,1) nquad = 2*ngrid x, w = gausslegendre(nquad) x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + # precalculated weights, integrating over Lagrange polynomials begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -617,7 +641,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ #end end - _block_synchronize() + #_block_synchronize() + begin_serial_region() + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + + begin_vperp_vpa_region() # use precalculated weights to calculate Gs using nodal values of fs @loop_vperp_vpa ivperp ivpa begin @@ -646,12 +676,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ (1.0/vperp.grid[ivperp])*dGsdvperp[ivpa,ivperp])) end - plot_H = true - plot_d2Gdvperp2 = true - plot_d2Gdvperpdvpa = true - plot_dGdvperp = true - plot_d2Gdvpa2 = true - plot_G = true + begin_vperp_region() + @loop_vperp ivperp begin + @views derivative!(vpa.scratch, Hs_from_Gs[:,ivperp], vpa, vpa_spectral) + @. dHsdvpa[:,ivperp] = vpa.scratch + end + begin_vpa_region() + @loop_vpa ivpa begin + @views derivative!(vperp.scratch, Hs_from_Gs[ivpa,:], vperp, vperp_spectral) + @. dHsdvperp[ivpa,:] = vperp.scratch + end + + plot_H = false #true + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + plot_G = false #true begin_serial_region() @serial_region begin @@ -662,6 +705,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H_err = abs(Hs_from_Gs - H_Maxwell) max_H_err = maximum(H_err) println("max_H_from_G_err: ",max_H_err) + @. dHdvperp_err = abs(dHsdvperp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err) + @. dHdvpa_err = abs(dHsdvpa - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err) if plot_H @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -680,6 +729,34 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_H_err.pdf") savefig(outfile) end + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHsdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) + end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHsdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) + end @. d2Gdvperp2_err = abs(d2Gsdvperp2 - d2Gdvperp2_Maxwell) max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 20cf2d133..c6e4284e0 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -12,6 +12,7 @@ export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs export calculate_Rosenbluth_H_from_G! export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +export dHdvpa, dHdvperp using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int From 076df3eba4aad9f1605d83af333636bff6192dc9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 6 Jul 2023 11:16:20 +0100 Subject: [PATCH 046/331] Optimised weights calculation by avoiding repeat evaluations of expension functions --- fkpl_test.jl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 539adab9b..d19708ea8 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -607,30 +607,34 @@ if abspath(PROGRAM_FILE) == @__FILE__ denom = (vpa_val - x_vpa[kvpa])^2 + (vperp_val + x_vperp[kvperp])^2 mm = 4.0*vperp_val*x_vperp[kvperp]/denom prefac = sqrt(denom) - G_elliptic_integral_factor = 2.0*ellipe(mm)*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe(mm) - 2.0*(1.0 - mm)*ellipk(mm) )/(3.0*mm) - G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe(mm) + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk(mm) )/(15.0*mm^2) - G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe(mm) - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk(mm) )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk(mm)/(pi*prefac) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa]) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp]) (G_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + lagrange_poly_vpa*lagrange_poly_vperp* G_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) (G1_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + lagrange_poly_vpa*lagrange_poly_vperp* G1_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) (G2_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + lagrange_poly_vpa*lagrange_poly_vperp* G2_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) (G3_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + lagrange_poly_vpa*lagrange_poly_vperp* G3_elliptic_integral_factor*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) (H_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa])*lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp])* + lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) end end From f5bb24cf7fd2b800e63be7ecdd8eefc4fa9ab748 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 6 Jul 2023 11:45:11 +0100 Subject: [PATCH 047/331] Reduced evaluations of precomputed arrays, with only minor speedup for weights calculation --- fkpl_test.jl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index d19708ea8..749dc34c5 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -604,8 +604,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # carry out integration over Lagrange polynomial at this node, on this element for kvperp in 1:nquad for kvpa in 1:nquad - denom = (vpa_val - x_vpa[kvpa])^2 + (vperp_val + x_vperp[kvperp])^2 - mm = 4.0*vperp_val*x_vperp[kvperp]/denom + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = 4.0*vperp_val*x_kvperp/denom prefac = sqrt(denom) ellipe_mm = ellipe(mm) ellipk_mm = ellipk(mm) @@ -614,28 +618,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_vpa[kvpa]) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_vperp[kvperp]) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) (G_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) (G1_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) (G2_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* - G2_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) (G3_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* - G3_elliptic_integral_factor*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) (H_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_vperp[kvperp]*w_vperp[kvperp]*w_vpa[kvpa]*2.0/sqrt(pi)) + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end end end @@ -1170,7 +1174,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral ngrid = 9 - nelement = 8 + nelement = 4 test_Lagrange_Rosenbluth_potentials(ngrid,nelement) end ## evaluate the collision operator with numerically computed G & H From 27b33175e617dbd56d74e865c58e363f82435c34 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 6 Jul 2023 15:52:24 +0100 Subject: [PATCH 048/331] Added plot for convergence test of Rosenbluth potentials. --- fkpl_test.jl | 84 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 749dc34c5..bac1ff30c 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -224,7 +224,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ test_Rosenbluth_integrals = false#true test_collision_operator_fluxes = false#true - test_Lagrange_integral = true + test_Lagrange_integral = false #true + test_Lagrange_integral_scan = true #ngrid = 9 #nelement = 8 @@ -398,7 +399,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err end - function test_Lagrange_Rosenbluth_potentials(ngrid,nelement) + function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) # set up grids for input Maxwellian vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) # set up necessary inputs for collision operator functions @@ -406,7 +407,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ nvpa = vpa.n # Set up MPI - initialize_comms!() + if standalone + initialize_comms!() + end setup_distributed_memory_MPI(1,1,1,1) looping.setup_loop_ranges!(block_rank[], block_size[]; s=1, sn=1, @@ -434,15 +437,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gsdvperp2 = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - G_err = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = allocate_shared_float(nvpa,nvperp) d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dGdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hs = allocate_shared_float(nvpa,nvperp) @@ -451,11 +454,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHsdvperp = allocate_shared_float(nvpa,nvperp) #Gs = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_err = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = allocate_shared_float(nvpa,nvperp) dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) @serial_region begin @@ -851,7 +854,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end end - return nothing + _block_synchronize() + if standalone + finalize_comms!() + end + println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + + return maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err) end function test_collision_operator(nelement,ngrid) @@ -1175,7 +1184,56 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral ngrid = 9 nelement = 4 - test_Lagrange_Rosenbluth_potentials(ngrid,nelement) + test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) + end + if test_Lagrange_integral_scan + initialize_comms!() + ngrid = 9 + nscan = 3 + #nelement_list = Int[2, 4, 8, 16, 32] + nelement_list = Int[2, 4, 8] + max_G_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_label = L"(1/N_{el})^{n_g - 1}" + + for iscan in 1:nscan + local nelement = nelement_list[iscan] + ((max_G_err[iscan], max_H_err[iscan], + max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], + max_dGdvperp_err[iscan]) + = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) + end + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], + xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_numerical_lagrange_integration_test.pdf" + savefig(outfile) + println(outfile) + finalize_comms!() end ## evaluate the collision operator with numerically computed G & H #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") From df31c3069eecfafaf761a97055ed4779062e6da5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 11 Jul 2023 14:24:49 +0100 Subject: [PATCH 049/331] Changes exploring weak form second derivatives on the GLL grid --- GaussLobattoLegendre_test.jl | 172 +++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 10 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 013a8313a..7165c6776 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -85,28 +85,133 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + """ + result of the inner product of Legendre polys of order k + """ + function Legendre_h_n(k) + h_n = 2.0/(2.0*k + 1) + return h_n + end + """ + difference prefac between Gauss-Legendre + and Gauss-Legendre-Lobatto points for the mass matrix + """ + function alpha_n(N) + gamma_n = 2.0/N + h_n = Legendre_h_n(N) + alpha = (h_n - gamma_n)/(gamma_n^2) + return alpha + end + + function beta_n(N) + gamma_n = 2.0/N + h_n = Legendre_h_n(N) + beta = (gamma_n - h_n)/(gamma_n*h_n) + return beta + end + + """ + assign Gauss-Legendre-Lobatto mass matrix on a 1D line with Jacobian = 1 + """ + function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L) + N = ngrid - 1 + alpha = alpha_n(N) + MM .= 0.0 + ## off diagonal components + for i in 1:ngrid + for j in 1:ngrid + MM[i,j] = alpha*wgts[i]*wgts[j]*Pl(x[i],N)*Pl(x[j],N) + end + end + ## diagonal components + for i in 1:ngrid + MM[i,i] += wgts[i] + end + @. MM *= (L/2.0) + return nothing + end + """ + exact inverse of Gauss-Legendre-Lobatto mass matrix for testing + """ + function GaussLegendreLobatto_inverse_mass_matrix!(MM,ngrid,x,wgts,L) + N = ngrid - 1 + beta = beta_n(N) + MM .= 0.0 + ## off diagonal components + for i in 1:ngrid + for j in 1:ngrid + MM[i,j] = beta*Pl(x[i],N)*Pl(x[j],N) + end + end + ## diagonal components + for i in 1:ngrid + MM[i,i] += 1.0/wgts[i] + end + @. MM *= 1.0/(L/2.0) + return nothing + end + """ + Gauss-Legendre-Lobatto S matrix Sjk = < lj | l'k > + Use that Djk = l'k(xj) + """ + function GaussLegendreLobatto_S_matrix!(SS,ngrid,DD,wgts,L) + N = ngrid - 1 + SS .= 0.0 + for j in 1:ngrid + for i in 1:ngrid + SS[i,j] += (L/2.0)*wgts[i]*DD[i,j] + end + end + return nothing + end + """ + Gauss-Legendre-Lobatto K matrix Kjk = -< l'j | l'k > + Use that Djk = l'k(xj) + """ + function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L) + N = ngrid - 1 + KK .= 0.0 + for j in 1:ngrid + for i in 1:ngrid + for m in 1:ngrid + KK[i,j] -= (L/2.0)*wgts[m]*DD[m,i]*DD[m,j] + end + end + end + return nothing + end # gauss lobatto test - ngrid = 33 + ngrid = 5 x, w = gausslobatto(ngrid) println("Gauss Lobatto Legendre") println("x: ",x) println("w: ",w) + Lx = 2.0 + xx = (Lx/2.0)*copy(x) + ww = (Lx/2.0)*copy(w) f_exact = Array{Float64,1}(undef,ngrid) df_exact = Array{Float64,1}(undef,ngrid) df_num = Array{Float64,1}(undef,ngrid) df_err = Array{Float64,1}(undef,ngrid) + d2f_exact = Array{Float64,1}(undef,ngrid) + d2f_num = Array{Float64,1}(undef,ngrid) + d2f_err = Array{Float64,1}(undef,ngrid) for ix in 1:ngrid #f_exact = exp(-x[ix]^2) #df_exact = -2.0*x[ix]*exp(-x[ix]^2) - f_exact[ix] = -2.0*x[ix]*exp(-x[ix]^2) - df_exact[ix] = (4.0*x[ix]^2 - 2.0)*exp(-x[ix]^2) + #f_exact[ix] = -2.0*xx[ix]*exp(-xx[ix]^2) + #df_exact[ix] = (4.0*xx[ix]^2 - 2.0)*exp(-xx[ix]^2) + #d2f_exact[ix] = (12.0 - 8.0*xx[ix]^2)*xx[ix]*exp(-xx[ix]^2) + f_exact[ix] = (xx[ix]^2 - 1.0)^2 + df_exact[ix] = 4.0*xx[ix]*(xx[ix]^2 - 1.0) + d2f_exact[ix] = 12.0*xx[ix]^2 - 4.0 end F_exact = f_exact[end] - f_exact[1] # do a test integration - F_num = sum(w.*df_exact) + F_num = sum(ww.*df_exact) F_err = abs(F_num - F_exact) #for ix in 1:ngrid # F_num += w[ix]*df_exact[ix] @@ -114,18 +219,65 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) Dmat = Array{Float64,2}(undef,ngrid,ngrid) - gausslobattolegendre_differentiation_matrix!(Dmat,x,ngrid,2.0,1) - print_matrix(Dmat,"Dmat",ngrid,ngrid) + Dmat2 = Array{Float64,2}(undef,ngrid,ngrid) + D2mat = Array{Float64,2}(undef,ngrid,ngrid) + Dmat_test = Array{Float64,2}(undef,ngrid,ngrid) + Dmat_err = Array{Float64,2}(undef,ngrid,ngrid) + gausslobattolegendre_differentiation_matrix!(Dmat,x,ngrid,Lx,1) + mul!(Dmat2,Dmat,Dmat) + #print_matrix(Dmat,"Dmat",ngrid,ngrid) + + Mmat = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendreLobatto_mass_matrix!(Mmat,ngrid,x,w,Lx) + #print_matrix(Mmat,"Mmat",ngrid,ngrid) + + IMmat = Array{Float64,2}(undef,ngrid,ngrid) + II_test = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendreLobatto_inverse_mass_matrix!(IMmat,ngrid,x,w,Lx) + #print_matrix(IMmat,"IMmat",ngrid,ngrid) + + mul!(II_test,Mmat,IMmat) + #print_matrix(II_test,"II_test",ngrid,ngrid) + + Smat = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendreLobatto_S_matrix!(Smat,ngrid,Dmat,w,Lx) + #print_matrix(Smat,"Smat",ngrid,ngrid) + mul!(Dmat_test,IMmat,Smat) + @. Dmat_err = abs(Dmat_test - Dmat ) + println("max_Dmat_err: ",maximum(Dmat_err)) + #print_matrix(Dmat_test,"Dmat_test",ngrid,ngrid) + + Kmat = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendreLobatto_K_matrix!(Kmat,ngrid,Dmat,w,Lx) + #print_matrix(Kmat,"Kmat",ngrid,ngrid) + mul!(D2mat,IMmat,Kmat) + #print_matrix(D2mat,"D2mat",ngrid,ngrid) + mul!(df_num,Dmat,f_exact) @. df_err = abs(df_num - df_exact) - #println(df_num) - #println(df_exact) + mul!(d2f_num,D2mat,f_exact) + @. d2f_err = abs(d2f_num - d2f_exact) + println(df_num) + println(df_exact) + println(d2f_num) + println(d2f_exact) max_df_err = maximum(df_err) - println("max df_err: ",max_df_err) + max_d2f_err = maximum(d2f_err) + println("max df_err (Dmat): ",max_df_err) + println("max d2f_err (weak D2mat): ",max_d2f_err) + # test by differentiating twice + mul!(d2f_num,Dmat2,f_exact) + @. d2f_err = abs(d2f_num - d2f_exact) + max_d2f_err = maximum(d2f_err) + + println("max d2f_err (Dmat*Dmat): ",max_d2f_err) + println(d2f_num) + println(d2f_exact) + # gauss radau test - ngrid = 33 + ngrid = 9 x, w = gaussradau(ngrid) println("Gauss Radau Legendre") println("x: ",x) From 66b61c8b2c432346fd77b2bbbce92a53e558b1ac Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 11 Jul 2023 14:34:33 +0100 Subject: [PATCH 050/331] created module file with Gauss Legendre related functions. --- GaussLobattoLegendre_test.jl | 152 +---------------------------- src/gausslegendre.jl | 180 +++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 149 deletions(-) create mode 100644 src/gausslegendre.jl diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 7165c6776..9375bdf3d 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -7,6 +7,9 @@ using LaTeXStrings using MPI using Measures +import moment_kinetics +using moment_kinetics.gausslegendre + if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") @@ -30,156 +33,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("") println("\n") end - """ - Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of - `Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. - Or https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html - """ - function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) - D[:,:] .= 0.0 - for ix in 1:ngrid - for ixp in 1:ngrid - if !(ix == ixp) - D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))/(x[ix]-x[ixp]) - end - end - end - # uncomment for analytical diagonal values - #D[1,1] = -0.25*(ngrid - 1)*ngrid - #D[ngrid,ngrid] = 0.25*(ngrid - 1)*ngrid - #for ix in 1:ngrid-1 - # D[ix,ix] = 0.0 - #end - # get diagonal values from sum of nonzero off diagonal values - for ix in 1:ngrid - D[ix,ix] = -sum(D[ix,:]) - end - #multiply by scale factor for element length - D .= (2.0*float(nelement)/L).*D - return nothing - end - """ - From - https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html - """ - function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) - D[:,:] .= 0.0 - for ix in 1:ngrid - for ixp in 1:ngrid - if !(ix == ixp) - D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))*((1.0 - x[ixp])/(1.0 - x[ix]))/(x[ix]-x[ixp]) - end - end - end - # uncomment for analytical diagonal values - #D[1,1] = -0.25*(ngrid - 1)*(ngrid + 1) - #for ix in 2:ngrid - # D[ix,ix] = 0.5/(1.0 - x[ix]) - #end - # get diagonal values from sum of nonzero off diagonal values - for ix in 1:ngrid - D[ix,ix] = -sum(D[ix,:]) - end - #multiply by scale factor for element length - D .= (2.0*float(nelement)/L).*D - return nothing - end - """ - result of the inner product of Legendre polys of order k - """ - function Legendre_h_n(k) - h_n = 2.0/(2.0*k + 1) - return h_n - end - """ - difference prefac between Gauss-Legendre - and Gauss-Legendre-Lobatto points for the mass matrix - """ - function alpha_n(N) - gamma_n = 2.0/N - h_n = Legendre_h_n(N) - alpha = (h_n - gamma_n)/(gamma_n^2) - return alpha - end - - function beta_n(N) - gamma_n = 2.0/N - h_n = Legendre_h_n(N) - beta = (gamma_n - h_n)/(gamma_n*h_n) - return beta - end - - """ - assign Gauss-Legendre-Lobatto mass matrix on a 1D line with Jacobian = 1 - """ - function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L) - N = ngrid - 1 - alpha = alpha_n(N) - MM .= 0.0 - ## off diagonal components - for i in 1:ngrid - for j in 1:ngrid - MM[i,j] = alpha*wgts[i]*wgts[j]*Pl(x[i],N)*Pl(x[j],N) - end - end - ## diagonal components - for i in 1:ngrid - MM[i,i] += wgts[i] - end - @. MM *= (L/2.0) - return nothing - end - """ - exact inverse of Gauss-Legendre-Lobatto mass matrix for testing - """ - function GaussLegendreLobatto_inverse_mass_matrix!(MM,ngrid,x,wgts,L) - N = ngrid - 1 - beta = beta_n(N) - MM .= 0.0 - ## off diagonal components - for i in 1:ngrid - for j in 1:ngrid - MM[i,j] = beta*Pl(x[i],N)*Pl(x[j],N) - end - end - ## diagonal components - for i in 1:ngrid - MM[i,i] += 1.0/wgts[i] - end - @. MM *= 1.0/(L/2.0) - return nothing - end - """ - Gauss-Legendre-Lobatto S matrix Sjk = < lj | l'k > - Use that Djk = l'k(xj) - """ - function GaussLegendreLobatto_S_matrix!(SS,ngrid,DD,wgts,L) - N = ngrid - 1 - SS .= 0.0 - for j in 1:ngrid - for i in 1:ngrid - SS[i,j] += (L/2.0)*wgts[i]*DD[i,j] - end - end - return nothing - end - """ - Gauss-Legendre-Lobatto K matrix Kjk = -< l'j | l'k > - Use that Djk = l'k(xj) - """ - function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L) - N = ngrid - 1 - KK .= 0.0 - for j in 1:ngrid - for i in 1:ngrid - for m in 1:ngrid - KK[i,j] -= (L/2.0)*wgts[m]*DD[m,i]*DD[m,j] - end - end - end - return nothing - end # gauss lobatto test ngrid = 5 x, w = gausslobatto(ngrid) diff --git a/src/gausslegendre.jl b/src/gausslegendre.jl new file mode 100644 index 000000000..a73213bda --- /dev/null +++ b/src/gausslegendre.jl @@ -0,0 +1,180 @@ +""" +module for Gauss-Legendre-Lobatto and Gauss-Legendre-Radau spectral element grids +""" +module gausslegendre + +export gausslobattolegendre_differentiation_matrix! +export gaussradaulegendre_differentiation_matrix! +export GaussLegendreLobatto_mass_matrix! +export GaussLegendreLobatto_inverse_mass_matrix! +export GaussLegendreLobatto_K_matrix! +export GaussLegendreLobatto_S_matrix! + +using FastGaussQuadrature +using LegendrePolynomials: Pl +using LinearAlgebra: mul! + +""" +Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of +`Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. +Or https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html + +D -- differentiation matrix +x -- Gauss-Legendre-Lobatto points in [-1,1] +ngrid -- number of points per element (incl. boundary points) +L -- total length of full domain +nelement -- total number of elements +""" +function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) + D[:,:] .= 0.0 + for ix in 1:ngrid + for ixp in 1:ngrid + if !(ix == ixp) + D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))/(x[ix]-x[ixp]) + end + end + end + # uncomment for analytical diagonal values + #D[1,1] = -0.25*(ngrid - 1)*ngrid + #D[ngrid,ngrid] = 0.25*(ngrid - 1)*ngrid + #for ix in 1:ngrid-1 + # D[ix,ix] = 0.0 + #end + # get diagonal values from sum of nonzero off diagonal values + for ix in 1:ngrid + D[ix,ix] = -sum(D[ix,:]) + end + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + return nothing +end +""" +From +https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamentals-differentiationch2.html + +D -- differentiation matrix +x -- Gauss-Legendre-Radau points in [-1,1) +ngrid -- number of points per element (incl. boundary points) +L -- total length of full domain +nelement -- total number of elements +""" +function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) + D[:,:] .= 0.0 + for ix in 1:ngrid + for ixp in 1:ngrid + if !(ix == ixp) + D[ix,ixp] = (Pl(x[ix],ngrid-1)/Pl(x[ixp],ngrid-1))*((1.0 - x[ixp])/(1.0 - x[ix]))/(x[ix]-x[ixp]) + end + end + end + # uncomment for analytical diagonal values + #D[1,1] = -0.25*(ngrid - 1)*(ngrid + 1) + #for ix in 2:ngrid + # D[ix,ix] = 0.5/(1.0 - x[ix]) + #end + # get diagonal values from sum of nonzero off diagonal values + for ix in 1:ngrid + D[ix,ix] = -sum(D[ix,:]) + end + #multiply by scale factor for element length + D .= (2.0*float(nelement)/L).*D + return nothing +end + +""" +result of the inner product of Legendre polys of order k +""" +function Legendre_h_n(k) + h_n = 2.0/(2.0*k + 1) + return h_n +end +""" +difference prefac between Gauss-Legendre +and Gauss-Legendre-Lobatto points for the mass matrix +""" +function alpha_n(N) + gamma_n = 2.0/N + h_n = Legendre_h_n(N) + alpha = (h_n - gamma_n)/(gamma_n^2) + return alpha +end + +function beta_n(N) + gamma_n = 2.0/N + h_n = Legendre_h_n(N) + beta = (gamma_n - h_n)/(gamma_n*h_n) + return beta +end + +""" +assign Gauss-Legendre-Lobatto mass matrix on a 1D line with Jacobian = 1 +""" +function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L) + N = ngrid - 1 + alpha = alpha_n(N) + MM .= 0.0 + ## off diagonal components + for i in 1:ngrid + for j in 1:ngrid + MM[i,j] = alpha*wgts[i]*wgts[j]*Pl(x[i],N)*Pl(x[j],N) + end + end + ## diagonal components + for i in 1:ngrid + MM[i,i] += wgts[i] + end + @. MM *= (L/2.0) + return nothing +end +""" +exact inverse of Gauss-Legendre-Lobatto mass matrix for testing +""" +function GaussLegendreLobatto_inverse_mass_matrix!(MM,ngrid,x,wgts,L) + N = ngrid - 1 + beta = beta_n(N) + MM .= 0.0 + ## off diagonal components + for i in 1:ngrid + for j in 1:ngrid + MM[i,j] = beta*Pl(x[i],N)*Pl(x[j],N) + end + end + ## diagonal components + for i in 1:ngrid + MM[i,i] += 1.0/wgts[i] + end + @. MM *= 1.0/(L/2.0) + return nothing +end +""" +Gauss-Legendre-Lobatto S matrix Sjk = < lj | l'k > +Use that Djk = l'k(xj) +""" +function GaussLegendreLobatto_S_matrix!(SS,ngrid,DD,wgts,L) + N = ngrid - 1 + SS .= 0.0 + for j in 1:ngrid + for i in 1:ngrid + SS[i,j] += (L/2.0)*wgts[i]*DD[i,j] + end + end + return nothing +end +""" +Gauss-Legendre-Lobatto K matrix Kjk = -< l'j | l'k > +Use that Djk = l'k(xj) +""" +function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L) + N = ngrid - 1 + KK .= 0.0 + for j in 1:ngrid + for i in 1:ngrid + for m in 1:ngrid + KK[i,j] -= (L/2.0)*wgts[m]*DD[m,i]*DD[m,j] + end + end + end + return nothing +end + +end From cb7316ab08b82829b5e702aea136193ad9374b15 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 11 Jul 2023 15:46:15 +0100 Subject: [PATCH 051/331] addition of gausslegendre_pseudospectral to the coordinate grids --- GaussLobattoLegendre_test.jl | 56 ++++++++++++++++++++++ src/coordinates.jl | 10 ++++ src/gausslegendre.jl | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 9375bdf3d..49e7e080a 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -9,6 +9,8 @@ using Measures import moment_kinetics using moment_kinetics.gausslegendre +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate if abspath(PROGRAM_FILE) == @__FILE__ using Pkg @@ -168,4 +170,58 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println(df_exact) max_df_err = maximum(df_err) println("max df_err: ",max_df_err) + + # elemental grid tests + ngrid = 33 + nelement = 3 + y_ngrid = ngrid #number of points per element + y_nelement_local = nelement # number of elements per rank + y_nelement_global = y_nelement_local # total number of elements + y_L = 12.0 #physical box size in reference units + bc = "zero" + discretization = "gausslegendre_pseudospectral" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + y_input = grid_input("y", y_ngrid, y_nelement_global, y_nelement_local, + nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + + # create the coordinate structs + y = define_coordinate(y_input) + println("y.grid: ",y.grid) + println("y.wgts: ",y.wgts) + x, w = gausslobatto(y.ngrid) + println("Gauss Lobatto Legendre") + println("x: ",x) + println("w: ",w) + + f_exact = Array{Float64,1}(undef,y.n) + df_exact = Array{Float64,1}(undef,y.n) + df_num = Array{Float64,1}(undef,y.n) + df_err = Array{Float64,1}(undef,y.n) + + for iy in 1:y.n + f_exact[iy] = exp(-y.grid[iy]^2) + df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + + #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + #df_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) + end + F_exact = sqrt(pi) + # do a test integration + println(f_exact) + F_num = sum(y.wgts.*f_exact) + F_err = abs(F_num - F_exact) + #for ix in 1:ngrid + # F_num += w[ix]*df_exact[ix] + #end + println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) + + end diff --git a/src/coordinates.jl b/src/coordinates.jl index 4b9d2c177..4d73e527f 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -8,6 +8,7 @@ export equally_spaced_grid using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_int using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid +using ..gausslegendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid using ..quadrature: composite_simpson_weights using ..input_structs: advection_input @@ -188,6 +189,15 @@ function init_grid(ngrid, nelement_global, nelement_local, n_global, n_local, ir # that are those associated with Clenshaw-Curtis quadrature grid, wgts = scaled_chebyshev_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) end + elseif discretization == "gausslegendre_pseudospectral" + if name == "vperp" + grid, wgts = scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) + grid .= grid .+ L/2.0 # shift to [0,L] appropriate to vperp variable + wgts = 2.0 .* wgts .* grid # to include 2 vperp in jacobian of integral + # see note above on normalisation + else + grid, wgts = scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) + end elseif discretization == "finite_difference" if name == "vperp" # initialize equally spaced grid defined on [0,L] diff --git a/src/gausslegendre.jl b/src/gausslegendre.jl index a73213bda..0cdfcbb99 100644 --- a/src/gausslegendre.jl +++ b/src/gausslegendre.jl @@ -9,10 +9,14 @@ export GaussLegendreLobatto_mass_matrix! export GaussLegendreLobatto_inverse_mass_matrix! export GaussLegendreLobatto_K_matrix! export GaussLegendreLobatto_S_matrix! +export scaled_gauss_legendre_lobatto_grid +export scaled_gauss_legendre_radau_grid using FastGaussQuadrature using LegendrePolynomials: Pl using LinearAlgebra: mul! +using ..type_definitions: mk_float, mk_int +using ..array_allocation: allocate_float """ Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of @@ -177,4 +181,90 @@ function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L) return nothing end +""" +function for setting up the full Gauss-Legendre-Lobatto +grid and collocation point weights +""" +function scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_local, + n, irank, box_length, imin, imax) + # get Gauss-Legendre-Lobatto points and weights on [-1,1] + x, w = gausslobatto(ngrid) + # factor with maps [-1,1] -> [-L/2, L/2] + scale_factor = 0.5*box_length/float(nelement_global) + # grid and weights arrays + grid = allocate_float(n) + wgts = allocate_float(n) + wgts .= 0.0 + #integer to deal with the overlap of element boundaries + k = 1 + @inbounds for j in 1:nelement_local + # calculate the grid avoiding overlap + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + @. grid[imin[j]:imax[j]] = scale_factor*x[k:ngrid] + shift + + # calculate the weights + # remembering on boundary points to include weights + # from both left and right elements + #println(imin[j]," ",imax[j]) + @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w[1:ngrid] + + k = 2 + end + return grid, wgts +end + +""" +function for setting up the full Gauss-Legendre-Radau +grid and collocation point weights +see comments of Gauss-Legendre-Lobatto routine above +""" +function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local, + n, irank, box_length, imin, imax) + # get Gauss-Legendre-Lobatto points and weights on [-1,1] + x_lob, w_lob = gausslobatto(ngrid) + # get Gauss-Legendre-Radau points and weights on [-1,1) + x_rad, w_rad = gaussradau(ngrid) + # transform to a Gauss-Legendre-Radau grid on (-1,1] + x_rad, w_rad = -reverse(x_rad), reverse(w_rad)# + + # factor with maps [-1,1] -> [-L/2, L/2] + scale_factor = 0.5*box_length/float(nelement_global) + # grid and weights arrays + grid = allocate_float(n) + wgts = allocate_float(n) + wgts .= 0.0 + if irank == 0 + # for 1st element, fill in with Gauss-Legendre-Radau points + j = 1 + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + @. grid[imin[j]:imax[j]] = scale_factor*x_rad[1:ngrid] + shift + @. wgts[imin[j]:imax[j]] += scale_factor*w_rad[1:ngrid] + + #integer to deal with the overlap of element boundaries + k = 2 + @inbounds for j in 2:nelement_local + # calculate the grid avoiding overlap + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift + @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] + end + else # all elements are Gauss-Legendre-Lobatto + #integer to deal with the overlap of element boundaries + k = 1 + @inbounds for j in 1:nelement_local + # calculate the grid avoiding overlap + iel_global = j + irank*nelement_local + shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift + @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] + + k = 2 + end + end + return grid, wgts +end + end From 8947f67438e9e3a4a11f3624617125fe16b18dd7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 11 Jul 2023 18:02:09 +0100 Subject: [PATCH 052/331] Addition of test of fully expanded collision operator and collisional fluxes. --- fkpl_test.jl | 302 +++++++++++++++++++++++++++++-------------- src/fokker_planck.jl | 76 ++++++++++- 2 files changed, 282 insertions(+), 96 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index bac1ff30c..15217a8f4 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -19,7 +19,7 @@ using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Max using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck: dHdvpa, dHdvperp +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs, F_Maxwellian using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -421,21 +421,29 @@ if abspath(PROGRAM_FILE) == @__FILE__ end fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + + fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Gs = allocate_shared_float(nvpa,nvperp) - d2Gsdvpa2 = allocate_shared_float(nvpa,nvperp) - dGsdvperp = allocate_shared_float(nvpa,nvperp) - d2Gsdvperpdvpa = allocate_shared_float(nvpa,nvperp) - d2Gsdvperp2 = allocate_shared_float(nvpa,nvperp) - #Gs = Array{mk_float,2}(undef,nvpa,nvperp) + Gsp = allocate_shared_float(nvpa,nvperp) + d2Gspdvpa2 = allocate_shared_float(nvpa,nvperp) + dGspdvperp = allocate_shared_float(nvpa,nvperp) + d2Gspdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gspdvperp2 = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) G_err = allocate_shared_float(nvpa,nvperp) d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) @@ -448,11 +456,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Hs = allocate_shared_float(nvpa,nvperp) - Hs_from_Gs = allocate_shared_float(nvpa,nvperp) - dHsdvpa = allocate_shared_float(nvpa,nvperp) - dHsdvperp = allocate_shared_float(nvpa,nvperp) - #Gs = Array{mk_float,2}(undef,nvpa,nvperp) + Hsp = allocate_shared_float(nvpa,nvperp) + Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa = allocate_shared_float(nvpa,nvperp) + dHspdvperp = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) H_err = allocate_shared_float(nvpa,nvperp) dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) @@ -460,47 +468,92 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) dHdvperp_err = allocate_shared_float(nvpa,nvperp) + Cssp_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vpa = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) + Cflux_vperp = allocate_shared_float(nvpa,nvperp) + Cflux_vperp_err = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) @serial_region begin println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) end # set up test Maxwellian - dens = 1.0 #3.0/4.0 - upar = 0.5 #2.0/3.0 - ppar = 1.0 #2.0/3.0 - pperp = 1.0 #2.0/3.0 - pres = get_pressure(ppar,pperp) - mi = 1.0 - vths = get_vth(pres,dens,mi) + # species s + denss = 1.0 #3.0/4.0 + upars = 0.5 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + # species sp + denssp = 1.0 #3.0/4.0 + uparsp = 0.5 #2.0/3.0 + pparsp = 1.0 #2.0/3.0 + pperpsp = 1.0 #2.0/3.0 + pressp = get_pressure(pparsp,pperpsp) + msp = 1.0 + vthsp = get_vth(pressp,denssp,msp) + nussp = 1.0 for ivperp in 1:nvperp for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(dens,upar,vths,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(dens,upar,vths,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(dens,upar,vths,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(dens,upar,vths,vpa,vperp,ivpa,ivperp) + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + denssp,uparsp,vthsp,msp, + nussp,vpa,vperp,ivpa,ivperp) + Cflux_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) + Cflux_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) end end for ivperp in 1:nvperp + # s @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @. dfsdvpa[:,ivperp] = vpa.scratch @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + # sp + @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 end for ivpa in 1:vpa.n + # s @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) @. dfsdvperp[ivpa,:] = vperp.scratch @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fsdvperp2[ivpa,:] = vperp.scratch2 + @. d2fsdvperp2[ivpa,:] = vperp.scratch2 + # sp + @views derivative!(vperp.scratch, fsp_in[ivpa,:], vperp, vperp_spectral) + @. dfspdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fspdvperp2[ivpa,:] = vperp.scratch2 end for ivperp in 1:nvperp + # s @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch + # sp + @views derivative!(vpa.scratch, dfspdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch end function get_imin_imax(coord,iel) @@ -660,44 +713,64 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_vperp_vpa_region() - # use precalculated weights to calculate Gs using nodal values of fs + # use precalculated weights to calculate Gsp using nodal values of fs @loop_vperp_vpa ivperp ivpa begin #for ivperp in 1:nvperp #for ivpa in 1:nvpa - d2Gsdvpa2[ivpa,ivperp] = 0.0 - dGsdvperp[ivpa,ivperp] = 0.0 - d2Gsdvperpdvpa[ivpa,ivperp] = 0.0 - d2Gsdvperp2[ivpa,ivperp] = 0.0 - Gs[ivpa,ivperp] = 0.0 - Hs[ivpa,ivperp] = 0.0 + d2Gspdvpa2[ivpa,ivperp] = 0.0 + dGspdvperp[ivpa,ivperp] = 0.0 + d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gspdvperp2[ivpa,ivperp] = 0.0 + Gsp[ivpa,ivperp] = 0.0 + Hsp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa - d2Gsdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvpa2[ivpap,ivperpp] - dGsdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfsdvperp[ivpap,ivperpp] - d2Gsdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvperpdvpa[ivpap,ivperpp] - d2Gsdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fsdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfsdvperp[ivpap,ivperpp] - Gs[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] - Hs[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fs_in[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] end end #end - (Hs_from_Gs[ivpa,ivperp] = 0.5*( d2Gsdvpa2[ivpa,ivperp] + - d2Gsdvperp2[ivpa,ivperp] + - (1.0/vperp.grid[ivperp])*dGsdvperp[ivpa,ivperp])) + (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + + d2Gspdvperp2[ivpa,ivperp] + + (1.0/vperp.grid[ivperp])*dGspdvperp[ivpa,ivperp])) end begin_vperp_region() @loop_vperp ivperp begin - @views derivative!(vpa.scratch, Hs_from_Gs[:,ivperp], vpa, vpa_spectral) - @. dHsdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) + @. dHspdvpa[:,ivperp] = vpa.scratch end begin_vpa_region() @loop_vpa ivpa begin - @views derivative!(vperp.scratch, Hs_from_Gs[ivpa,:], vperp, vperp_spectral) - @. dHsdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) + @. dHspdvperp[ivpa,:] = vperp.scratch end + # evaluate collsion operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + # fully expanded form + (Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + + d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + + 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + + (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + + 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + + (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) + # collisional fluxes + ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = + calculate_collisional_fluxes(fs_in[ivpa,ivperp], + dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp], + d2Gspdvpa2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp], + d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], + ms,msp) ) + end + + plot_H = false #true plot_dHdvpa = false #true plot_dHdvperp = false #true @@ -706,28 +779,56 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_dGdvperp = false #true plot_d2Gdvpa2 = false #true plot_G = false #true + plot_C = false #true begin_serial_region() @serial_region begin println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. H_err = abs(Hs - H_Maxwell) + @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) + max_C_err = maximum(Cssp_err) + max_C_Maxwell_val = maximum(Cssp_Maxwell) + max_C_numerical_val = maximum(Cssp_numerical) + println("max_C_err: ",max_C_err) + println("max_C_Maxwell_val: ",max_C_Maxwell_val) + println("max_C_numerical_val: ",max_C_numerical_val) + @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) + max_Cflux_vpa_err = maximum(Cflux_vpa_err) + println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) + @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) + max_Cflux_vperp_err = maximum(Cflux_vperp_err) + println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) + @. H_err = abs(Hsp - H_Maxwell) max_H_err = maximum(H_err) println("max_H_err: ",max_H_err) - @. H_err = abs(Hs_from_Gs - H_Maxwell) + @. H_err = abs(Hsp_from_Gsp - H_Maxwell) max_H_err = maximum(H_err) println("max_H_from_G_err: ",max_H_err) - @. dHdvperp_err = abs(dHsdvperp - dHdvperp_Maxwell) + @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) max_dHdvperp_err = maximum(dHdvperp_err) println("max_dHdvperp_err: ",max_dHdvperp_err) - @. dHdvpa_err = abs(dHsdvpa - dHdvpa_Maxwell) + @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) max_dHdvpa_err = maximum(dHdvpa_err) println("max_dHdvpa_err: ",max_dHdvpa_err) + if plot_C + @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_err.pdf") + savefig(outfile) + end if plot_H - @views heatmap(vperp.grid, vpa.grid, Hs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_H_lagrange.pdf") savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Hs_from_Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, Hsp_from_Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_H_from_G_lagrange.pdf") savefig(outfile) @@ -741,7 +842,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end if plot_dHdvpa - @views heatmap(vperp.grid, vpa.grid, dHsdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_dHdvpa_lagrange.pdf") savefig(outfile) @@ -755,7 +856,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end if plot_dHdvperp - @views heatmap(vperp.grid, vpa.grid, dHsdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_dHdvperp_lagrange.pdf") savefig(outfile) @@ -768,11 +869,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_dHdvperp_err.pdf") savefig(outfile) end - @. d2Gdvperp2_err = abs(d2Gsdvperp2 - d2Gdvperp2_Maxwell) + @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) if plot_d2Gdvperp2 - @views heatmap(vperp.grid, vpa.grid, d2Gsdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") savefig(outfile) @@ -785,11 +886,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_d2Gdvperp2_err.pdf") savefig(outfile) end - @. d2Gdvperpdvpa_err = abs(d2Gsdvperpdvpa - d2Gdvperpdvpa_Maxwell) + @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) if plot_d2Gdvperpdvpa - @views heatmap(vperp.grid, vpa.grid, d2Gsdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") savefig(outfile) @@ -802,11 +903,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") savefig(outfile) end - @. dGdvperp_err = abs(dGsdvperp - dGdvperp_Maxwell) + @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) max_dGdvperp_err = maximum(dGdvperp_err) println("max_dGdvperp_err: ",max_dGdvperp_err) if plot_dGdvperp - @views heatmap(vperp.grid, vpa.grid, dGsdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_dGdvperp_lagrange.pdf") savefig(outfile) @@ -819,11 +920,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_dGdvperp_err.pdf") savefig(outfile) end - @. d2Gdvpa2_err = abs(d2Gsdvpa2 - d2Gdvpa2_Maxwell) + @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) if plot_d2Gdvpa2 - @views heatmap(vperp.grid, vpa.grid, d2Gsdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") savefig(outfile) @@ -836,11 +937,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_d2Gdvpa2_err.pdf") savefig(outfile) end - @. G_err = abs(Gs - G_Maxwell) + @. G_err = abs(Gsp - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) if plot_G - @views heatmap(vperp.grid, vpa.grid, Gs[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_G_lagrange.pdf") savefig(outfile) @@ -858,9 +959,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ if standalone finalize_comms!() end - println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - - return maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err) + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + results = maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err) + return results end function test_collision_operator(nelement,ngrid) @@ -1188,10 +1289,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 - nscan = 3 - #nelement_list = Int[2, 4, 8, 16, 32] - nelement_list = Int[2, 4, 8] + ngrid = 5 + nscan = 5 + nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + #nelement_list = Int[2, 4] + #nelement_list = Int[2] + max_C_err = Array{mk_float,1}(undef,nscan) + max_Gvpa_err = Array{mk_float,1}(undef,nscan) + max_Gvperp_err = Array{mk_float,1}(undef,nscan) max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) @@ -1206,33 +1312,39 @@ if abspath(PROGRAM_FILE) == @__FILE__ for iscan in 1:nscan local nelement = nelement_list[iscan] - ((max_G_err[iscan], max_H_err[iscan], + ((max_C_err[iscan], max_Gvpa_err[iscan], max_Gvperp_err[iscan], + max_G_err[iscan], max_H_err[iscan], max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], max_dGdvperp_err[iscan]) = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) end - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - Glabel = L"\epsilon(G)" - Hlabel = L"\epsilon(H)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabel = L"\epsilon(dG/d v_{\perp})" - println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], - xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_numerical_lagrange_integration_test.pdf" - savefig(outfile) - println(outfile) + if global_rank[]==0 + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Clabel = L"\epsilon(C)" + Gvpalabel = L"\epsilon(\Gamma_{\|\|})" + Gvperplabel = L"\epsilon(\Gamma_{\perp})" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + savefig(outfile) + println(outfile) + end finalize_comms!() end ## evaluate the collision operator with numerically computed G & H diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index c6e4284e0..14351d09e 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -12,7 +12,8 @@ export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs export calculate_Rosenbluth_H_from_G! export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -export dHdvpa, dHdvperp +export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +export F_Maxwellian using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int @@ -509,6 +510,79 @@ function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, return fac end +function F_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = (dens/(vth^3))*exp(-eta^2) + return fac +end + +function dFdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = -2.0*(dens/(vth^4))*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) + return fac +end + +function dFdvperp_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = -2.0*(dens/(vth^4))*(vperp.grid[ivperp]/vth)*exp(-eta^2) + return fac +end + +function d2Fdvperpdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*(vperp.grid[ivperp]/vth)*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) + return fac +end + +function d2Fdvpa2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*( ((vpa.grid[ivpa] - upar)/vth)^2 - 0.5 )*exp(-eta^2) + return fac +end + +function d2Fdvperp2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*((vperp.grid[ivperp]/vth)^2 - 0.5)*exp(-eta^2) + return fac +end + +function Cssp_Maxwellian_inputs(denss::mk_float,upars::mk_float,vths::mk_float,ms::mk_float, + denssp::mk_float,uparsp::mk_float,vthsp::mk_float,msp::mk_float, + nussp::mk_float,vpa,vperp,ivpa,ivperp) + + d2Fsdvpa2 = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Fsdvperp2 = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Fsdvperpdvpa = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dFsdvperp = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dFsdvpa = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + Fs = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + d2Gspdvpa2 = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperp2 = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperpdvpa = d2Gdvperpdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGspdvperp = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvperp = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvpa = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + Fsp = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + ( Cssp_Maxwellian = + d2Fsdvpa2*d2Gspdvpa2 + + d2Fsdvperp2*d2Gspdvperp2 + + 2.0*d2Fsdvperpdvpa*d2Gspdvperpdvpa + + (1.0/(vperp.grid[ivperp]^2))*dFsdvperp*dGspdvperp + + 2.0*(1.0 - (ms/msp))*(dFsdvpa*dHspdvpa + dFsdvperp*dHspdvperp) + + (8.0/sqrt(pi))*(ms/msp)*Fs*Fsp ) + + Cssp_Maxwellian *= nussp + return Cssp_Maxwellian +end + function Cflux_vpa_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, vpa,vperp,ivpa,ivperp) From 06a8dda4807dc319c12844b7e21d064dc0caf6d8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 11 Jul 2023 18:03:02 +0100 Subject: [PATCH 053/331] add gausslegendre.jl to the main moment_kinetics file --- src/moment_kinetics.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index 43c89328d..417df1657 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -18,6 +18,7 @@ include("looping.jl") include("array_allocation.jl") include("interpolation.jl") include("clenshaw_curtis.jl") +include("gausslegendre.jl") include("chebyshev.jl") include("finite_differences.jl") include("quadrature.jl") From 48ca2add70616908d8557b1cb117d2281a9e393b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 12 Jul 2023 16:00:25 +0100 Subject: [PATCH 054/331] Incorporation of matrix (first) derivative using Gauss Legendre points into the moment_kinetics derivative! framework. Note that second derivatives via the second derivative function are not yet supported. --- GaussLobattoLegendre_test.jl | 21 ++++++++-- src/calculus.jl | 20 ++++++++++ src/gausslegendre.jl | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 49e7e080a..5837c2bf1 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -11,6 +11,7 @@ import moment_kinetics using moment_kinetics.gausslegendre using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.calculus: derivative! if abspath(PROGRAM_FILE) == @__FILE__ using Pkg @@ -134,8 +135,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # gauss radau test ngrid = 9 - x, w = gaussradau(ngrid) + xradau, wradau = gaussradau(ngrid) println("Gauss Radau Legendre") + println("xradau: ",xradau) + println("wradau: ",wradau) + x = -reverse(xradau) + w = reverse(wradau) println("x: ",x) println("w: ",w) @@ -161,7 +166,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) Dmat = Array{Float64,2}(undef,ngrid,ngrid) - gaussradaulegendre_differentiation_matrix!(Dmat,x,ngrid,2.0,1) + gaussradaulegendre_differentiation_matrix!(Dmat,xradau,ngrid,2.0,1) print_matrix(Dmat,"Dmat",ngrid,ngrid) mul!(df_num,Dmat,f_exact) @@ -189,11 +194,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate - y_input = grid_input("y", y_ngrid, y_nelement_global, y_nelement_local, + y_input = grid_input("vperp", y_ngrid, y_nelement_global, y_nelement_local, nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate structs y = define_coordinate(y_input) + y_spectral = setup_gausslegendre_pseudospectral(y) println("y.grid: ",y.grid) println("y.wgts: ",y.wgts) x, w = gausslobatto(y.ngrid) @@ -213,7 +219,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) #df_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) end - F_exact = sqrt(pi) + if y.name == "y" + F_exact = sqrt(pi) + elseif y.name == "vperp" + F_exact = 1.0 + end # do a test integration println(f_exact) F_num = sum(y.wgts.*f_exact) @@ -223,5 +233,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ #end println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) + derivative!(df_num, f_exact, y, y_spectral) + @. df_err = df_num - df_exact + println("max(df_err): ",maximum(df_err)) end diff --git a/src/calculus.jl b/src/calculus.jl index c70a1b2cd..e9d730509 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -8,12 +8,32 @@ export integral using ..chebyshev: chebyshev_info, chebyshev_derivative! +using ..gausslegendre: gausslegendre_info, gausslegendre_derivative! using ..finite_differences: derivative_finite_difference! using ..type_definitions: mk_float, mk_int using MPI using ..communication: block_rank using ..communication: _block_synchronize using ..looping + +""" +Use Gauss-Legendre differentiation matrices to take the first derivative +""" +function derivative!(df, f, coord, adv_fac, spectral::gausslegendre_info) + # get the derivative at each grid point within each element and store in df + gausslegendre_derivative!(coord.scratch_2d, f, spectral, coord) + # map the derivative from the elemental grid to the full grid; + # at element boundaries, use the derivative from the upwind element. + derivative_elements_to_full_grid!(df, coord.scratch_2d, coord, adv_fac) +end + +function derivative!(df, f, coord, spectral::gausslegendre_info) + # get the derivative at each grid point within each element and store in df + gausslegendre_derivative!(coord.scratch_2d, f, spectral, coord) + # map the derivative from the elemental grid to the full grid; + # at element boundaries, use the average of the derivatives from neighboring elements. + derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) +end """ Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' """ diff --git a/src/gausslegendre.jl b/src/gausslegendre.jl index 0cdfcbb99..53c7b0d48 100644 --- a/src/gausslegendre.jl +++ b/src/gausslegendre.jl @@ -11,6 +11,8 @@ export GaussLegendreLobatto_K_matrix! export GaussLegendreLobatto_S_matrix! export scaled_gauss_legendre_lobatto_grid export scaled_gauss_legendre_radau_grid +export gausslegendre_derivative! +export setup_gausslegendre_pseudospectral using FastGaussQuadrature using LegendrePolynomials: Pl @@ -18,6 +20,72 @@ using LinearAlgebra: mul! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float + +""" +structs for passing around matrices for taking +the derivatives on Gauss-Legendre points in 1D +""" +struct gausslegendre_base_info{} + # elementwise differentiation matrix (ngrid*ngrid) + Dmat::Array{mk_float,2} +end + +struct gausslegendre_info{} + lobatto::gausslegendre_base_info + radau::gausslegendre_base_info +end + +function setup_gausslegendre_pseudospectral(coord) + lobatto = setup_gausslegendre_pseudospectral_lobatto(coord) + radau = setup_gausslegendre_pseudospectral_radau(coord) + return gausslegendre_info(lobatto,radau) +end + +function setup_gausslegendre_pseudospectral_lobatto(coord) + x, w = gausslobatto(coord.ngrid) + Dmat = allocate_float(coord.ngrid, coord.ngrid) + gausslobattolegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) + return gausslegendre_base_info(Dmat) +end + +function setup_gausslegendre_pseudospectral_radau(coord) + x, w = gaussradau(coord.ngrid) + Dmat = allocate_float(coord.ngrid, coord.ngrid) + gaussradaulegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) + return gausslegendre_base_info(Dmat) +end +""" +function for taking the first derivative on Gauss-Legendre points +""" +function gausslegendre_derivative!(df, ff, gausslegendre, coord) + # define local variable nelement for convenience + nelement = coord.nelement_local + # check array bounds + @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) + + # variable k will be used to avoid double counting of overlapping point + k = 0 + j = 1 # the first element + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + @views mul!(df[:,j],gausslegendre.radau.Dmat[:,:],ff[imin:imax]) + else #differentiate using the Lobatto scheme + @views mul!(df[:,j],gausslegendre.lobatto.Dmat[:,:],ff[imin:imax]) + end + # calculate the derivative on each element + @inbounds for j ∈ 2:nelement + k = 1 + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views mul!(df[:,j],gausslegendre.lobatto.Dmat[:,:],ff[imin:imax]) + end + + return nothing +end + """ Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of `Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. @@ -82,6 +150,15 @@ function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array end #multiply by scale factor for element length D .= (2.0*float(nelement)/L).*D + + # get into correct order for a grid on (-1,1] + Dreverse = copy(D) + for ix in 1:ngrid + for ixp in 1:ngrid + Dreverse[ngrid-ix+1,ngrid-ixp+1] = -D[ix,ixp] + end + end + D .= Dreverse return nothing end From 0f36f3a0207d2c7e2d0bc821b654dfd61069b340 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 13 Jul 2023 12:56:09 +0100 Subject: [PATCH 055/331] Addition of a weak-form second derivative function for Gauss-Legendre-Lobatto grids. Performance is comparable to that of a direct double first derivative for large ngrid, but for smaller ngrid the weak form derivative typically has an order of magnitude lower errors. Boundary conditions may not be implemented correctly on the K matrix -- in our framework where we include the boundary point and set it to zero manually each time step, this seems not to be an issue. --- GaussLobattoLegendre_test.jl | 42 +++++++---- src/calculus.jl | 11 +++ src/gausslegendre.jl | 140 +++++++++++++++++++++++++++++++++-- 3 files changed, 171 insertions(+), 22 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 5837c2bf1..22ca43a42 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -11,7 +11,7 @@ import moment_kinetics using moment_kinetics.gausslegendre using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.calculus: derivative! +using moment_kinetics.calculus: derivative!, second_derivative! if abspath(PROGRAM_FILE) == @__FILE__ using Pkg @@ -39,6 +39,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # gauss lobatto test ngrid = 5 + nelement = 1 x, w = gausslobatto(ngrid) println("Gauss Lobatto Legendre") println("x: ",x) @@ -85,7 +86,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #print_matrix(Dmat,"Dmat",ngrid,ngrid) Mmat = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_mass_matrix!(Mmat,ngrid,x,w,Lx) + GaussLegendreLobatto_mass_matrix!(Mmat,ngrid,x,w,Lx,nelement) #print_matrix(Mmat,"Mmat",ngrid,ngrid) IMmat = Array{Float64,2}(undef,ngrid,ngrid) @@ -105,7 +106,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #print_matrix(Dmat_test,"Dmat_test",ngrid,ngrid) Kmat = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_K_matrix!(Kmat,ngrid,Dmat,w,Lx) + GaussLegendreLobatto_K_matrix!(Kmat,ngrid,Dmat,w,Lx,nelement) #print_matrix(Kmat,"Kmat",ngrid,ngrid) mul!(D2mat,IMmat,Kmat) #print_matrix(D2mat,"D2mat",ngrid,ngrid) @@ -177,8 +178,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max df_err: ",max_df_err) # elemental grid tests - ngrid = 33 - nelement = 3 + ngrid = 2 + nelement = 1000 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -194,30 +195,35 @@ if abspath(PROGRAM_FILE) == @__FILE__ comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate - y_input = grid_input("vperp", y_ngrid, y_nelement_global, y_nelement_local, + y_input = grid_input("y", y_ngrid, y_nelement_global, y_nelement_local, nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate structs y = define_coordinate(y_input) y_spectral = setup_gausslegendre_pseudospectral(y) - println("y.grid: ",y.grid) - println("y.wgts: ",y.wgts) + #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) + #print_matrix(y_spectral.lobatto.Mmat,"local lobatto mass matrix",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.Mmat,"local radau mass matrix",y.ngrid,y.ngrid) + #println("y.grid: ",y.grid) + #println("y.wgts: ",y.wgts) x, w = gausslobatto(y.ngrid) println("Gauss Lobatto Legendre") - println("x: ",x) - println("w: ",w) + #println("x: ",x) + #println("w: ",w) f_exact = Array{Float64,1}(undef,y.n) df_exact = Array{Float64,1}(undef,y.n) df_num = Array{Float64,1}(undef,y.n) df_err = Array{Float64,1}(undef,y.n) + d2f_exact = Array{Float64,1}(undef,y.n) + d2f_num = Array{Float64,1}(undef,y.n) + d2f_err = Array{Float64,1}(undef,y.n) for iy in 1:y.n f_exact[iy] = exp(-y.grid[iy]^2) df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) - #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) - #df_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) + d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) end if y.name == "y" F_exact = sqrt(pi) @@ -225,7 +231,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ F_exact = 1.0 end # do a test integration - println(f_exact) + #println(f_exact) F_num = sum(y.wgts.*f_exact) F_err = abs(F_num - F_exact) #for ix in 1:ngrid @@ -236,5 +242,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ derivative!(df_num, f_exact, y, y_spectral) @. df_err = df_num - df_exact println("max(df_err): ",maximum(df_err)) - + second_derivative!(d2f_num, f_exact, y, y_spectral) + @. d2f_err = d2f_num - d2f_exact + #println(d2f_num) + #println(d2f_exact) + println("max(d2f_err) (weak form): ",maximum(d2f_err)) + derivative!(d2f_num, df_num, y, y_spectral) + @. d2f_err = d2f_num - d2f_exact + println("max(d2f_err) (double first derivative): ",maximum(d2f_err)) + end diff --git a/src/calculus.jl b/src/calculus.jl index e9d730509..1818c5424 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -9,6 +9,7 @@ export integral using ..chebyshev: chebyshev_info, chebyshev_derivative! using ..gausslegendre: gausslegendre_info, gausslegendre_derivative! +using ..gausslegendre: gausslegendre_apply_Kmat!, gausslegendre_mass_matrix_solve! using ..finite_differences: derivative_finite_difference! using ..type_definitions: mk_float, mk_int using MPI @@ -34,6 +35,16 @@ function derivative!(df, f, coord, spectral::gausslegendre_info) # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) end + +function second_derivative!(d2f, f, coord, spectral::gausslegendre_info) + # get the derivative at each grid point within each element and store in df + gausslegendre_apply_Kmat!(coord.scratch_2d, f, spectral, coord) + # map the derivative from the elemental grid to the full grid; + # at element boundaries, use the average of the derivatives from neighboring elements. + derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) + # solve weak form problem M * d2f = K * f + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) +end """ Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' """ diff --git a/src/gausslegendre.jl b/src/gausslegendre.jl index 53c7b0d48..c83db2e92 100644 --- a/src/gausslegendre.jl +++ b/src/gausslegendre.jl @@ -12,11 +12,14 @@ export GaussLegendreLobatto_S_matrix! export scaled_gauss_legendre_lobatto_grid export scaled_gauss_legendre_radau_grid export gausslegendre_derivative! +export gausslegendre_apply_Kmat! +export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral using FastGaussQuadrature using LegendrePolynomials: Pl -using LinearAlgebra: mul! +using LinearAlgebra: mul!, lu, LU +using SparseArrays: sparse using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float @@ -28,31 +31,51 @@ the derivatives on Gauss-Legendre points in 1D struct gausslegendre_base_info{} # elementwise differentiation matrix (ngrid*ngrid) Dmat::Array{mk_float,2} + # local mass matrix + Mmat::Array{mk_float,2} + # local K matrix (for second derivatives) + Kmat::Array{mk_float,2} end struct gausslegendre_info{} lobatto::gausslegendre_base_info radau::gausslegendre_base_info + # global (1D) mass matrix + mass_matrix::Array{mk_float,2} + # global (1D) LU object + mass_matrix_lu::T where T end function setup_gausslegendre_pseudospectral(coord) lobatto = setup_gausslegendre_pseudospectral_lobatto(coord) radau = setup_gausslegendre_pseudospectral_radau(coord) - return gausslegendre_info(lobatto,radau) + mass_matrix = allocate_float(coord.n,coord.n) + setup_global_mass_matrix!(mass_matrix, lobatto, radau, coord) + mass_matrix_lu = lu(sparse(mass_matrix)) + return gausslegendre_info(lobatto,radau,mass_matrix,mass_matrix_lu) end function setup_gausslegendre_pseudospectral_lobatto(coord) x, w = gausslobatto(coord.ngrid) Dmat = allocate_float(coord.ngrid, coord.ngrid) gausslobattolegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) - return gausslegendre_base_info(Dmat) + Mmat = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) + Kmat = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendreLobatto_K_matrix!(Kmat,coord.ngrid,Dmat,w,coord.L,coord.nelement_global) + return gausslegendre_base_info(Dmat,Mmat,Kmat) end function setup_gausslegendre_pseudospectral_radau(coord) + # elemental differentiation matrix x, w = gaussradau(coord.ngrid) Dmat = allocate_float(coord.ngrid, coord.ngrid) gaussradaulegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) - return gausslegendre_base_info(Dmat) + # elemental mass matrix + Mmat = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) + Kmat = allocate_float(coord.ngrid, coord.ngrid) + return gausslegendre_base_info(Dmat,Mmat,Kmat) end """ function for taking the first derivative on Gauss-Legendre points @@ -86,6 +109,44 @@ function gausslegendre_derivative!(df, ff, gausslegendre, coord) return nothing end +""" +function for taking the weak-form second derivative on Gauss-Legendre points +""" +function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) + # define local variable nelement for convenience + nelement = coord.nelement_local + # check array bounds + @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) + + # variable k will be used to avoid double counting of overlapping point + k = 0 + j = 1 # the first element + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + @views mul!(df[:,j],gausslegendre.radau.Kmat[:,:],ff[imin:imax]) + else #differentiate using the Lobatto scheme + @views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + end + # calculate the derivative on each element + @inbounds for j ∈ 2:nelement + k = 1 + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + @views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + end + + return nothing +end + +function gausslegendre_mass_matrix_solve!(d2f,b,spectral) + y = spectral.mass_matrix_lu \ b + @. d2f = y + return nothing +end + """ Formula for differentiation matrix taken from p196 of Chpt `The Spectral Elemtent Method' of `Computational Seismology'. Heiner Igel First Edition. Published in 2017 by Oxford University Press. @@ -190,7 +251,7 @@ end """ assign Gauss-Legendre-Lobatto mass matrix on a 1D line with Jacobian = 1 """ -function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L) +function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L,nelement_global) N = ngrid - 1 alpha = alpha_n(N) MM .= 0.0 @@ -204,7 +265,7 @@ function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L) for i in 1:ngrid MM[i,i] += wgts[i] end - @. MM *= (L/2.0) + @. MM *= (0.5*L/nelement_global) return nothing end """ @@ -245,13 +306,13 @@ end Gauss-Legendre-Lobatto K matrix Kjk = -< l'j | l'k > Use that Djk = l'k(xj) """ -function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L) +function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L,nelement_global) N = ngrid - 1 KK .= 0.0 for j in 1:ngrid for i in 1:ngrid for m in 1:ngrid - KK[i,j] -= (L/2.0)*wgts[m]*DD[m,i]*DD[m,j] + KK[i,j] -= (0.5*L/nelement_global)*wgts[m]*DD[m,i]*DD[m,j] end end end @@ -344,4 +405,67 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local return grid, wgts end +""" +function that assigns the local mass matrices to +a global array for later solving weak form of required +1D equation +""" +function setup_global_mass_matrix!(mass_matrix::Array{mk_float,2}, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + ngrid = coord.ngrid + imin = coord.imin + imax = coord.imax + @. mass_matrix = 0.0 + if coord.name == "vperp" + # use the Radau mass matrix for the 1st element + Mmat_fel = radau.Mmat + else + # use the Lobatto mass matrix + Mmat_fel = lobatto.Mmat + end + zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" + zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" + + # fill in first element + j = 1 + if zero_bc_lower_boundary #x.bc == "zero" + mass_matrix[imin[j],imin[j]:imax[j]] .+= Mmat_fel[1,:]./2.0 #contributions from this element/2 + mass_matrix[imin[j],imin[j]] += Mmat_fel[ngrid,ngrid]/2.0 #contribution from missing `zero' element/2 + else + mass_matrix[imin[j],imin[j]:imax[j]] .+= Mmat_fel[1,:] + end + for k in 2:imax[j]-imin[j] + mass_matrix[k,imin[j]:imax[j]] .+= Mmat_fel[k,:] + end + if zero_bc_upper_boundary && coord.nelement_local == 1 + mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:]./2.0 #contributions from this element/2 + mass_matrix[imax[j],imax[j]] += lobatto.Mmat[1,1]/2.0 #contribution from missing `zero' element/2 + elseif coord.nelement_local > 1 #x.bc == "zero" + mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:]./2.0 + else + mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:] + end + # remaining elements recalling definitions of imax and imin + for j in 2:coord.nelement_local + #lower boundary condition on element + mass_matrix[imin[j]-1,imin[j]-1:imax[j]] .+= lobatto.Mmat[1,:]./2.0 + for k in 2:imax[j]-imin[j]+1 + mass_matrix[k+imin[j]-2,imin[j]-1:imax[j]] .+= lobatto.Mmat[k,:] + end + # upper boundary condition on element + if j == coord.nelement_local && !(zero_bc_upper_boundary) + mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:] + elseif j == coord.nelement_local && zero_bc_upper_boundary + mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:]./2.0 #contributions from this element/2 + mass_matrix[imax[j],imax[j]] += lobatto.Mmat[1,1]/2.0 #contribution from missing `zero' element/2 + else + mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:]./2.0 + end + end + + return nothing +end + end From 912ebfcd338909c891b3c156a89ea1b94181f067 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 16 Jul 2023 12:08:16 +0100 Subject: [PATCH 056/331] Changed name of gausslegendre module file to avoid conflict with function name in FastGaussQuadrature. Implemented vpa and vperp coordinate (infinite and half infinite domain) mass and S (RHS differentiation matrices, integrating by parts) for the divergence operator, in weak form. The use of the operators are tested in the test script -- for use with the collision operator the script should be integrated into a base-level function. Sparse matrix operations are used to permit the whole assembled mass and S matrices to be stored and used with mul! & \. A separate element-wise multiplication routine could be introduced for the S matrix similar to the elementwise differentiation already in existance. The elemental mass and S matrices M0, M1, S0 and S1 are computed using the relationship between the Lagrange polynomials and the Legendre polynomials. --- GaussLobattoLegendre_test.jl | 129 +++++-- src/calculus.jl | 4 +- src/coordinates.jl | 2 +- src/{gausslegendre.jl => gauss_legendre.jl} | 379 +++++++++++++++++++- src/moment_kinetics.jl | 2 +- 5 files changed, 473 insertions(+), 43 deletions(-) rename src/{gausslegendre.jl => gauss_legendre.jl} (51%) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 22ca43a42..a7e697c89 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -1,6 +1,6 @@ using FastGaussQuadrature using LegendrePolynomials: Pl -using LinearAlgebra: mul! +using LinearAlgebra: mul!, lu, inv, cond using Printf using Plots using LaTeXStrings @@ -8,7 +8,7 @@ using MPI using Measures import moment_kinetics -using moment_kinetics.gausslegendre +using moment_kinetics.gauss_legendre using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.calculus: derivative!, second_derivative! @@ -38,7 +38,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # gauss lobatto test - ngrid = 5 + ngrid = 4 nelement = 1 x, w = gausslobatto(ngrid) println("Gauss Lobatto Legendre") @@ -87,20 +87,45 @@ if abspath(PROGRAM_FILE) == @__FILE__ Mmat = Array{Float64,2}(undef,ngrid,ngrid) GaussLegendreLobatto_mass_matrix!(Mmat,ngrid,x,w,Lx,nelement) - #print_matrix(Mmat,"Mmat",ngrid,ngrid) + print_matrix(Mmat,"Mmat",ngrid,ngrid) + + Mmat_1 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_mass_matrix_1!(Mmat_1,ngrid,x,w,Lx,nelement) + print_matrix(Mmat_1,"Mmat_1",ngrid,ngrid) IMmat = Array{Float64,2}(undef,ngrid,ngrid) II_test = Array{Float64,2}(undef,ngrid,ngrid) GaussLegendreLobatto_inverse_mass_matrix!(IMmat,ngrid,x,w,Lx) - #print_matrix(IMmat,"IMmat",ngrid,ngrid) + print_matrix(IMmat,"IMmat",ngrid,ngrid) mul!(II_test,Mmat,IMmat) - #print_matrix(II_test,"II_test",ngrid,ngrid) + print_matrix(II_test,"II_test",ngrid,ngrid) + print_matrix(inv(Mmat),"inv(Mmat)",ngrid,ngrid) Smat = Array{Float64,2}(undef,ngrid,ngrid) GaussLegendreLobatto_S_matrix!(Smat,ngrid,Dmat,w,Lx) - #print_matrix(Smat,"Smat",ngrid,ngrid) - mul!(Dmat_test,IMmat,Smat) + print_matrix(Smat,"Smat",ngrid,ngrid) + Smat_1 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_S_matrix_1!(Smat_1,ngrid,x,w,Lx,nelement) + print_matrix(Smat_1,"Smat_1",ngrid,ngrid) + + M0 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_weak_product_matrix!(M0,ngrid,x,w,Lx,nelement,"M0") + print_matrix(M0,"M0",ngrid,ngrid) + M1 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_weak_product_matrix!(M1,ngrid,x,w,Lx,nelement,"M1") + print_matrix(M1,"M1",ngrid,ngrid) + S0 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_weak_product_matrix!(S0,ngrid,x,w,Lx,nelement,"S0") + print_matrix(S0,"S0",ngrid,ngrid) + S1 = Array{Float64,2}(undef,ngrid,ngrid) + GaussLegendre_weak_product_matrix!(S1,ngrid,x,w,Lx,nelement,"S1") + print_matrix(S1,"S1",ngrid,ngrid) + + + #mul!(Dmat_test,IMmat,Smat) + mul!(Dmat_test,inv(Mmat),Smat) + #@. Dmat_test = Mmat\Smat @. Dmat_err = abs(Dmat_test - Dmat ) println("max_Dmat_err: ",maximum(Dmat_err)) #print_matrix(Dmat_test,"Dmat_test",ngrid,ngrid) @@ -135,7 +160,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # gauss radau test - ngrid = 9 + ngrid = 3 xradau, wradau = gaussradau(ngrid) println("Gauss Radau Legendre") println("xradau: ",xradau) @@ -168,7 +193,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Dmat = Array{Float64,2}(undef,ngrid,ngrid) gaussradaulegendre_differentiation_matrix!(Dmat,xradau,ngrid,2.0,1) - print_matrix(Dmat,"Dmat",ngrid,ngrid) + #print_matrix(Dmat,"Dmat",ngrid,ngrid) mul!(df_num,Dmat,f_exact) @. df_err = abs(df_num - df_exact) @@ -178,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max df_err: ",max_df_err) # elemental grid tests - ngrid = 2 - nelement = 1000 + ngrid = 17 + nelement = 100 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -195,13 +220,44 @@ if abspath(PROGRAM_FILE) == @__FILE__ comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate - y_input = grid_input("y", y_ngrid, y_nelement_global, y_nelement_local, + #y_name = "y" + y_name = "vperp" + y_input = grid_input(y_name, y_ngrid, y_nelement_global, y_nelement_local, nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate structs y = define_coordinate(y_input) y_spectral = setup_gausslegendre_pseudospectral(y) + Mmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) + x, w = gausslobatto(y.ngrid) + GaussLegendreLobatto_mass_matrix!(Mmat,y.ngrid,x,w,y.L,y.nelement_global) + #print_matrix(Mmat,"Mmat",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) + #print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) + #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) + Dmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) + Dmat_test = Array{Float64,2}(undef,y.ngrid,y.ngrid) + Dmat_err = Array{Float64,2}(undef,y.ngrid,y.ngrid) + lu_M0 = lu(y_spectral.lobatto.M0) + mul!(Dmat_test,inv(y_spectral.lobatto.M0), y_spectral.lobatto.S0) + #Dmat_test = y_spectral.lobatto.M0 \ y_spectral.lobatto.S0 + #print_matrix(lu_M0 \ y_spectral.lobatto.S0, "local D matrix",y.ngrid,y.ngrid) + #print_matrix(Dmat_test, "local D matrix",y.ngrid,y.ngrid) + mul!(Dmat_err,y_spectral.lobatto.M0,Dmat_test) + #print_matrix(Dmat_err, "local S matrix?",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.Dmat, "local D matrix (Dmat)",y.ngrid,y.ngrid) + + @. Dmat = y_spectral.lobatto.Dmat + + @. Dmat_err = Dmat_test - Dmat + #println("max_Dmat_err: ",maximum(Dmat_err)) + + #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) #print_matrix(y_spectral.lobatto.Mmat,"local lobatto mass matrix",y.ngrid,y.ngrid) #print_matrix(y_spectral.radau.Mmat,"local radau mass matrix",y.ngrid,y.ngrid) #println("y.grid: ",y.grid) @@ -215,6 +271,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ df_exact = Array{Float64,1}(undef,y.n) df_num = Array{Float64,1}(undef,y.n) df_err = Array{Float64,1}(undef,y.n) + g_exact = Array{Float64,1}(undef,y.n) + divg_exact = Array{Float64,1}(undef,y.n) + divg_num = Array{Float64,1}(undef,y.n) + divg_err = Array{Float64,1}(undef,y.n) d2f_exact = Array{Float64,1}(undef,y.n) d2f_num = Array{Float64,1}(undef,y.n) d2f_err = Array{Float64,1}(undef,y.n) @@ -222,6 +282,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for iy in 1:y.n f_exact[iy] = exp(-y.grid[iy]^2) df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + g_exact[iy] = y.grid[iy]*exp(-y.grid[iy]^2) + divg_exact[iy] = 2.0*(1.0-y.grid[iy]^2)*exp(-y.grid[iy]^2) #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) end @@ -241,14 +303,41 @@ if abspath(PROGRAM_FILE) == @__FILE__ derivative!(df_num, f_exact, y, y_spectral) @. df_err = df_num - df_exact - println("max(df_err): ",maximum(df_err)) - second_derivative!(d2f_num, f_exact, y, y_spectral) - @. d2f_err = d2f_num - d2f_exact - #println(d2f_num) - #println(d2f_exact) - println("max(d2f_err) (weak form): ",maximum(d2f_err)) + println("max(df_err) (interpolation): ",maximum(df_err)) derivative!(d2f_num, df_num, y, y_spectral) @. d2f_err = d2f_num - d2f_exact - println("max(d2f_err) (double first derivative): ",maximum(d2f_err)) + println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) + if y.name == "y" + b = Array{Float64,1}(undef,y.n) + mul!(b,y_spectral.S_matrix,f_exact) + gausslegendre_mass_matrix_solve!(df_num,b,y_spectral) + @. df_err = df_num - df_exact + #println("df_num (weak form): ",df_num) + #println("df_exact (weak form): ",df_exact) + println("max(df_err) (weak form): ",maximum(df_err)) + + second_derivative!(d2f_num, f_exact, y, y_spectral) + @. d2f_err = d2f_num - d2f_exact + #println(d2f_num) + #println(d2f_exact) + println("max(d2f_err) (weak form): ",maximum(d2f_err)) + elseif y.name == "vperp" + #println("condition: ",cond(y_spectral.mass_matrix)) + b = Array{Float64,1}(undef,y.n) + mul!(b,y_spectral.S_matrix,g_exact) + gausslegendre_mass_matrix_solve!(divg_num,b,y_spectral) + @. divg_err = abs(divg_num - divg_exact) + #println("divg_num (weak form): ",divg_num) + #println("divg_exact (weak form): ",divg_exact) + println("max(divg_err) (weak form): ",maximum(divg_err)) + + @. y.scratch = y.grid*g_exact + derivative!(y.scratch2, y.scratch, y, y_spectral) + @. divg_num = y.scratch2/y.grid + @. divg_err = abs(divg_num - divg_exact) + println("max(divg_err) (interpolation): ",maximum(divg_err)) + + end + end diff --git a/src/calculus.jl b/src/calculus.jl index 1818c5424..fc546f582 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -8,8 +8,8 @@ export integral using ..chebyshev: chebyshev_info, chebyshev_derivative! -using ..gausslegendre: gausslegendre_info, gausslegendre_derivative! -using ..gausslegendre: gausslegendre_apply_Kmat!, gausslegendre_mass_matrix_solve! +using ..gauss_legendre: gausslegendre_info, gausslegendre_derivative! +using ..gauss_legendre: gausslegendre_apply_Kmat!, gausslegendre_mass_matrix_solve! using ..finite_differences: derivative_finite_difference! using ..type_definitions: mk_float, mk_int using MPI diff --git a/src/coordinates.jl b/src/coordinates.jl index 4d73e527f..737f3b776 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -8,7 +8,7 @@ export equally_spaced_grid using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_int using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid -using ..gausslegendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid +using ..gauss_legendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid using ..quadrature: composite_simpson_weights using ..input_structs: advection_input diff --git a/src/gausslegendre.jl b/src/gauss_legendre.jl similarity index 51% rename from src/gausslegendre.jl rename to src/gauss_legendre.jl index c83db2e92..ccef87038 100644 --- a/src/gausslegendre.jl +++ b/src/gauss_legendre.jl @@ -1,25 +1,28 @@ """ module for Gauss-Legendre-Lobatto and Gauss-Legendre-Radau spectral element grids """ -module gausslegendre +module gauss_legendre export gausslobattolegendre_differentiation_matrix! export gaussradaulegendre_differentiation_matrix! export GaussLegendreLobatto_mass_matrix! +export GaussLegendre_mass_matrix_1! export GaussLegendreLobatto_inverse_mass_matrix! export GaussLegendreLobatto_K_matrix! export GaussLegendreLobatto_S_matrix! +export GaussLegendre_S_matrix_1! export scaled_gauss_legendre_lobatto_grid export scaled_gauss_legendre_radau_grid export gausslegendre_derivative! export gausslegendre_apply_Kmat! export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral +export GaussLegendre_weak_product_matrix! using FastGaussQuadrature -using LegendrePolynomials: Pl +using LegendrePolynomials: Pl, dnPl using LinearAlgebra: mul!, lu, LU -using SparseArrays: sparse +using SparseArrays: sparse, AbstractSparseArray using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float @@ -35,6 +38,14 @@ struct gausslegendre_base_info{} Mmat::Array{mk_float,2} # local K matrix (for second derivatives) Kmat::Array{mk_float,2} + # local mass matrix type 0 + M0::Array{mk_float,2} + # local mass matrix type 1 + M1::Array{mk_float,2} + # local S (weak derivative) matrix type 0 + S0::Array{mk_float,2} + # local S (weak derivative) matrix type 1 + S1::Array{mk_float,2} end struct gausslegendre_info{} @@ -42,6 +53,9 @@ struct gausslegendre_info{} radau::gausslegendre_base_info # global (1D) mass matrix mass_matrix::Array{mk_float,2} + # global (1D) weak derivative matrix + #S_matrix::Array{mk_float,2} + S_matrix::AbstractSparseArray{mk_float,Ti,2} where Ti # global (1D) LU object mass_matrix_lu::T where T end @@ -50,9 +64,14 @@ function setup_gausslegendre_pseudospectral(coord) lobatto = setup_gausslegendre_pseudospectral_lobatto(coord) radau = setup_gausslegendre_pseudospectral_radau(coord) mass_matrix = allocate_float(coord.n,coord.n) + S_matrix = allocate_float(coord.n,coord.n) setup_global_mass_matrix!(mass_matrix, lobatto, radau, coord) + + setup_global_weak_form_matrix!(mass_matrix, lobatto, radau, coord, "M") + setup_global_weak_form_matrix!(S_matrix, lobatto, radau, coord, "S") mass_matrix_lu = lu(sparse(mass_matrix)) - return gausslegendre_info(lobatto,radau,mass_matrix,mass_matrix_lu) + + return gausslegendre_info(lobatto,radau,mass_matrix,sparse(S_matrix),mass_matrix_lu) end function setup_gausslegendre_pseudospectral_lobatto(coord) @@ -63,19 +82,40 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) Kmat = allocate_float(coord.ngrid, coord.ngrid) GaussLegendreLobatto_K_matrix!(Kmat,coord.ngrid,Dmat,w,coord.L,coord.nelement_global) - return gausslegendre_base_info(Dmat,Mmat,Kmat) + + M0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M0,coord.ngrid,x,w,coord.L,coord.nelement_global,"M0") + M1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M1,coord.ngrid,x,w,coord.L,coord.nelement_global,"M1") + S0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(S0,coord.ngrid,x,w,coord.L,coord.nelement_global,"S0") + S1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(S1,coord.ngrid,x,w,coord.L,coord.nelement_global,"S1") + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1) end function setup_gausslegendre_pseudospectral_radau(coord) - # elemental differentiation matrix + # Gauss-Radau points on [-1,1) x, w = gaussradau(coord.ngrid) + # Gauss-Radau points on (-1,1] + xreverse, wreverse = -reverse(x), reverse(w) + # elemental differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) gaussradaulegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) # elemental mass matrix Mmat = allocate_float(coord.ngrid, coord.ngrid) GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) Kmat = allocate_float(coord.ngrid, coord.ngrid) - return gausslegendre_base_info(Dmat,Mmat,Kmat) + + M0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M0",radau=true) + M1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M1",radau=true) + S0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(S0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S0",radau=true) + S1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(S1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S1",radau=true) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1) end """ function for taking the first derivative on Gauss-Legendre points @@ -141,9 +181,9 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) return nothing end -function gausslegendre_mass_matrix_solve!(d2f,b,spectral) +function gausslegendre_mass_matrix_solve!(f,b,spectral) y = spectral.mass_matrix_lu \ b - @. d2f = y + @. f = y return nothing end @@ -319,6 +359,173 @@ function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L,nelement_global) return nothing end +""" +assign abitrary weak inner product matrix Q on a 1D line with Jacobian = 1 +""" +function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,option;radau=false) + # coefficient in expansion of + # lagrange polys in terms of Legendre polys + gamma = allocate_float(ngrid) + for i in 1:ngrid-1 + gamma[i] = Legendre_h_n(i-1) + end + if radau + gamma[ngrid] = Legendre_h_n(ngrid-1) + else + gamma[ngrid] = 2.0/(ngrid - 1) + end + # appropriate inner product of Legendre polys + # definition depends on required matrix + # for M0: AA = < P_i P_j > + # for M1: AA = < P_i P_j x > + # for S0: AA = -< P'_i P_j > + # for S1: AA = -< P'_i P_j x > + AA = allocate_float(ngrid,ngrid) + nquad = 2*ngrid + zz, wz = gausslegendre(nquad) + @. AA = 0.0 + if option == "M0" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += wz[k]*Pl(zz[k],i-1)*Pl(zz[k],j-1) + end + end + end + elseif option == "M1" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += zz[k]*wz[k]*Pl(zz[k],i-1)*Pl(zz[k],j-1) + end + end + end + elseif option == "S0" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] -= wz[k]*dnPl(zz[k],i-1,1)*Pl(zz[k],j-1) + end + end + end + elseif option == "S1" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] -= zz[k]*wz[k]*dnPl(zz[k],i-1,1)*Pl(zz[k],j-1) + end + end + end + end + #for i in 1:ngrid + # for j in 1:ngrid + # print(" ", AA[i,j]) + # end + # println("") + #end + QQ .= 0.0 + for j in 1:ngrid + for i in 1:ngrid + for l in 1:ngrid + for k in 1:ngrid + QQ[i,j] += wgts[i]*wgts[j]*Pl(x[i],k-1)*Pl(x[j],l-1)*AA[k,l]/(gamma[k]*gamma[l]) + end + end + end + end + # return normalised Q (no scale factors) + #@. QQ *= (0.5*L/nelement_global) + return nothing +end + +""" +assign mass matrix M1mn = < lm|x|ln > on a 1D line with Jacobian = 1 +""" +function GaussLegendre_mass_matrix_1!(MM,ngrid,x,wgts,L,nelement_global) + # coefficient in expansion of + # lagrange polys in terms of Legendre polys + gamma = allocate_float(ngrid) + for i in 1:ngrid-1 + gamma[i] = Legendre_h_n(i-1) + end + gamma[ngrid] = 2.0/(ngrid - 1) + # appropriate inner product of Legendre polys + # < P_i P_j x > + AA = allocate_float(ngrid,ngrid) + nquad = 2*ngrid + zz, wz = gausslegendre(nquad) + @. AA = 0.0 + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += zz[k]*wz[k]*Pl(zz[k],i-1)*Pl(zz[k],j-1) + end + end + end + + MM .= 0.0 + for i in 1:ngrid + for j in 1:ngrid + for k in 1:ngrid + for l in 1:ngrid + MM[i,j] += wgts[i]*wgts[j]*Pl(x[i],k-1)*Pl(x[j],l-1)*AA[k,l]/(gamma[k]*gamma[l]) + end + end + end + end + @. MM *= (0.5*L/nelement_global) + return nothing +end + +""" +assign derivative matrix S1mn = < l'm|x|ln > on a 1D line with Jacobian = 1 +""" +function GaussLegendre_S_matrix_1!(SS,ngrid,x,wgts,L,nelement_global) + # coefficient in expansion of + # lagrange polys in terms of Legendre polys + gamma = allocate_float(ngrid) + for i in 1:ngrid-1 + gamma[i] = Legendre_h_n(i-1) + end + gamma[ngrid] = 2.0/(ngrid - 1) + # appropriate inner product of Legendre polys + # < P'_i P_j x > + AA = allocate_float(ngrid,ngrid) + nquad = 2*ngrid + zz, wz = gausslegendre(nquad) + @. AA = 0.0 + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += zz[k]*wz[k]*dnPl(zz[k],i-1,1)*Pl(zz[k],j-1) + end + end + end + + SS .= 0.0 + for i in 1:ngrid + for j in 1:ngrid + for k in 1:ngrid + for l in 1:ngrid + SS[i,j] -= wgts[i]*wgts[j]*Pl(x[i],k-1)*Pl(x[j],l-1)*AA[k,l]/(gamma[k]*gamma[l]) + end + end + end + end + @. SS *= (0.5*L/nelement_global) + return nothing +end + +function scale_factor_func(L,nelement_global) + return 0.5*L/float(nelement_global) +end + +function shift_factor_func(L,nelement_global,nelement_local,irank,ielement_local) + ielement_global = ielement_local + irank*nelement_local + shift = L*((float(ielement_global)-0.5)/float(nelement_global) - 0.5) + return shift +end + """ function for setting up the full Gauss-Legendre-Lobatto grid and collocation point weights @@ -328,7 +535,7 @@ function scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_loc # get Gauss-Legendre-Lobatto points and weights on [-1,1] x, w = gausslobatto(ngrid) # factor with maps [-1,1] -> [-L/2, L/2] - scale_factor = 0.5*box_length/float(nelement_global) + scale_factor = scale_factor_func(box_length,nelement_global) #0.5*box_length/float(nelement_global) # grid and weights arrays grid = allocate_float(n) wgts = allocate_float(n) @@ -337,8 +544,9 @@ function scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_loc k = 1 @inbounds for j in 1:nelement_local # calculate the grid avoiding overlap - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + #iel_global = j + irank*nelement_local + #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) @. grid[imin[j]:imax[j]] = scale_factor*x[k:ngrid] + shift # calculate the weights @@ -367,7 +575,8 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local x_rad, w_rad = -reverse(x_rad), reverse(w_rad)# # factor with maps [-1,1] -> [-L/2, L/2] - scale_factor = 0.5*box_length/float(nelement_global) + scale_factor = scale_factor_func(box_length,nelement_global) + #scale_factor = 0.5*box_length/float(nelement_global) # grid and weights arrays grid = allocate_float(n) wgts = allocate_float(n) @@ -375,8 +584,9 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local if irank == 0 # for 1st element, fill in with Gauss-Legendre-Radau points j = 1 - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + #iel_global = j + irank*nelement_local + #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) @. grid[imin[j]:imax[j]] = scale_factor*x_rad[1:ngrid] + shift @. wgts[imin[j]:imax[j]] += scale_factor*w_rad[1:ngrid] @@ -384,8 +594,9 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local k = 2 @inbounds for j in 2:nelement_local # calculate the grid avoiding overlap - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + #iel_global = j + irank*nelement_local + #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] end @@ -394,8 +605,9 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local k = 1 @inbounds for j in 1:nelement_local # calculate the grid avoiding overlap - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + #iel_global = j + irank*nelement_local + #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] @@ -468,4 +680,133 @@ function setup_global_mass_matrix!(mass_matrix::Array{mk_float,2}, return nothing end +""" +function that assigns the local weak-form matrices to +a global array QQ_global for later solving weak form of required +1D equation + +option choosing type of matrix to be constructed -- "M" (mass matrix), "S" (derivative matrix) +""" +function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord,option) + QQ_j = allocate_float(coord.ngrid,coord.ngrid) + QQ_jp1 = allocate_float(coord.ngrid,coord.ngrid) + + ngrid = coord.ngrid + imin = coord.imin + imax = coord.imax + @. QQ_global = 0.0 + + if coord.name == "vperp" + zero_bc_upper_boundary = true + zero_bc_lower_boundary = false + else + zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" + zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" + end + # fill in first element + j = 1 + # N.B. QQ varies with ielement for vperp, but not vpa + get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) + get_QQ_local!(QQ_jp1,j+1,lobatto,radau,coord,option) + + if zero_bc_lower_boundary #x.bc == "zero" + QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:]./2.0 #contributions from this element/2 + QQ_global[imin[j],imin[j]] += QQ_j[ngrid,ngrid]/2.0 #contribution from missing `zero' element/2 + else + QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:] + end + for k in 2:imax[j]-imin[j] + QQ_global[k,imin[j]:imax[j]] .+= QQ_j[k,:] + end + if zero_bc_upper_boundary && coord.nelement_local == 1 + QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:]./2.0 #contributions from this element/2 + QQ_global[imax[j],imax[j]] += QQ_jp1[1,1]/2.0 #contribution from missing `zero' element/2 + elseif coord.nelement_local > 1 #x.bc == "zero" + QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:]./2.0 + else + QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:] + end + # remaining elements recalling definitions of imax and imin + for j in 2:coord.nelement_local + get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) + get_QQ_local!(QQ_jp1,j+1,lobatto,radau,coord,option) + + #lower boundary condition on element + QQ_global[imin[j]-1,imin[j]-1:imax[j]] .+= QQ_j[1,:]./2.0 + for k in 2:imax[j]-imin[j]+1 + QQ_global[k+imin[j]-2,imin[j]-1:imax[j]] .+= QQ_j[k,:] + end + # upper boundary condition on element + if j == coord.nelement_local && !(zero_bc_upper_boundary) + QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:] + elseif j == coord.nelement_local && zero_bc_upper_boundary + QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:]./2.0 #contributions from this element/2 + QQ_global[imax[j],imax[j]] += QQ_jp1[1,1]/2.0 #contribution from missing `zero' element/2 + else + QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:]./2.0 + end + end + + return nothing +end + +function get_QQ_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord,option) + + if option == "M" + get_MM_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "S" + get_SS_local!(QQ,ielement,lobatto,radau,coord) + end + return nothing +end + +function get_MM_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 # lobatto points + @. QQ = shift_factor*lobatto.M0 + scale_factor*lobatto.M1 + else # radau points + @. QQ = shift_factor*radau.M0 + scale_factor*radau.M1 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.M0 + end + @. QQ *= scale_factor + return nothing +end + +function get_SS_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 # lobatto points + @. QQ = shift_factor*lobatto.S0 + scale_factor*lobatto.S1 + else # radau points + @. QQ = shift_factor*radau.S0 + scale_factor*radau.S1 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.S0 + end + # no scaling factor here because d /d x * dx is invariant under length scale transforms + #@. QQ *= scale_factor + return nothing +end + end diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index 417df1657..93131e1f9 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -18,7 +18,7 @@ include("looping.jl") include("array_allocation.jl") include("interpolation.jl") include("clenshaw_curtis.jl") -include("gausslegendre.jl") +include("gauss_legendre.jl") include("chebyshev.jl") include("finite_differences.jl") include("quadrature.jl") From ae190a44651589b357b004cee32c7f6605860aa0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 16 Jul 2023 19:30:58 +0100 Subject: [PATCH 057/331] Included Gauss-Legendre grids as a (hardcoded) option in the collision operator test script. --- fkpl_test.jl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 15217a8f4..1f643c804 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -11,6 +11,7 @@ import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! @@ -181,7 +182,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function init_grids(nelement,ngrid) - discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" #discretization = "finite_difference" # define inputs needed for the test @@ -216,9 +218,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp = define_coordinate(vperp_input) #println(vperp.grid) #println(vperp.wgts) - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end return vpa, vperp, vpa_spectral, vperp_spectral end @@ -1341,7 +1349,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) end From 10acc139b0e625306ef1ab1c7215302c93b76f17 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 16 Jul 2023 19:56:02 +0100 Subject: [PATCH 058/331] Added divergence form of collision operator to test, using the weak form of the divergence provided by the gauss_legendre.jl module. The performance is almost identical to the existing form of the collision operator in the test. --- fkpl_test.jl | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 1f643c804..12edaa411 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -6,12 +6,13 @@ using MPI using SpecialFunctions: erf, ellipe, ellipk using FastGaussQuadrature using Dates +using LinearAlgebra: mul! import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral -using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! #using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! @@ -478,6 +479,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ Cssp_numerical = allocate_shared_float(nvpa,nvperp) Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_div_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_div_err = allocate_shared_float(nvpa,nvperp) Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) Cflux_vpa = allocate_shared_float(nvpa,nvperp) Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) @@ -777,7 +780,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], ms,msp) ) end - + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_numerical = 0.0 + begin_vperp_region() + @loop_vperp ivperp begin + @views mul!(vpa.scratch,vpa_spectral.S_matrix,Cflux_vpa[:,ivperp]) + gausslegendre_mass_matrix_solve!(vpa.scratch2,vpa.scratch,vpa_spectral) + Cssp_div_numerical[:,ivperp] += vpa.scratch2 + end + begin_vpa_region() + @loop_vpa ivpa begin + @views mul!(vperp.scratch,vperp_spectral.S_matrix,Cflux_vperp[ivpa,:]) + gausslegendre_mass_matrix_solve!(vperp.scratch2,vperp.scratch,vperp_spectral) + Cssp_div_numerical[ivpa,:] += vperp.scratch2 + end + @. Cssp_div_numerical *= nussp + end plot_H = false #true plot_dHdvpa = false #true @@ -799,6 +817,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max_C_err: ",max_C_err) println("max_C_Maxwell_val: ",max_C_Maxwell_val) println("max_C_numerical_val: ",max_C_numerical_val) + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_err = abs(Cssp_div_numerical - Cssp_Maxwell) + max_C_div_err = maximum(Cssp_div_err) + println("max_C_div_err: ",max_C_div_err) + end @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) max_Cflux_vpa_err = maximum(Cflux_vpa_err) println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) @@ -1298,10 +1321,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 5 - nelement_list = Int[2, 4, 8, 16, 32] + nscan = 2 + #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4] + nelement_list = Int[2, 4] #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) From e379f29be8e2524d5d75ccc2a590acaf1e4c214f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 22 Jul 2023 20:15:01 +0100 Subject: [PATCH 059/331] Add weak form calculation of vpa second derivative, with moderately improved results. --- fkpl_test.jl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 12edaa411..aa21de8f9 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -23,7 +23,7 @@ using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs, F_Maxwellian using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.calculus: derivative! +using moment_kinetics.calculus: derivative!, second_derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.communication using moment_kinetics.looping @@ -546,6 +546,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) @. d2fspdvpa2[:,ivperp] = vpa.scratch2 end + if vpa.discretization == "gausslegendre_pseudospectral" + println("use weak-form second derivative for vpa") + for ivperp in 1:nvperp + @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + @views second_derivative!(vpa.scratch2, fsp_in[:,ivperp], vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 + end + end for ivpa in 1:vpa.n # s @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) @@ -1321,10 +1330,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 2 - #nelement_list = Int[2, 4, 8, 16, 32] + nscan = 5 + nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - nelement_list = Int[2, 4] + #nelement_list = Int[2, 4] #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) From 6ab8fbdb2ded43878c5c03e4713484501bd53520 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 22 Jul 2023 20:18:34 +0100 Subject: [PATCH 060/331] Addition of weak form Laplacian and second derivative operators for the vperp coordinate, with the aim of achieving improved performance compared to interpolation derivatives. The maximum errors in the corresponding tests are not as promising as the enhanced accuracy seen for the vpa coordinate (infinite domain). This appears to be due to a Gibbs Phenomena at the orgin of the half-infinite domain. --- GaussLobattoLegendre_test.jl | 71 +++++++++++++--- src/gauss_legendre.jl | 156 +++++++++++++++++++++++++++++------ 2 files changed, 188 insertions(+), 39 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index a7e697c89..a8ea98059 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max df_err: ",max_df_err) # elemental grid tests - ngrid = 17 - nelement = 100 + ngrid = 5 + nelement = 32 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -216,7 +216,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ cheb_option = "matrix" adv_input = advection_input("default", 1.0, 0.0, 0.0) nrank = 1 - irank = 0 + irank = 0#1 comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate @@ -230,16 +230,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ y_spectral = setup_gausslegendre_pseudospectral(y) Mmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) x, w = gausslobatto(y.ngrid) + #print_vector(y.grid,"y.grid",y.n) + #print_vector(y.wgts,"y.wgts",y.n) GaussLegendreLobatto_mass_matrix!(Mmat,y.ngrid,x,w,y.L,y.nelement_global) #print_matrix(Mmat,"Mmat",y.ngrid,y.ngrid) - #print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) - #print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) - #print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) - #print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) + print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) + print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) #print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) #print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) + print_matrix(y_spectral.radau.K0,"local radau K matrix K0",y.ngrid,y.ngrid) + print_matrix(y_spectral.radau.K1,"local radau K matrix K1",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.K0,"local K matrix K0",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.K1,"local K matrix K1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.K_matrix,"global K matrix",y.n,y.n) + #@views y_spectral.K_matrix[1,:] *= (4.0/3.0) + #print_matrix(y_spectral.K_matrix,"global K matrix (hacked) ",y.n,y.n) + Dmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) Dmat_test = Array{Float64,2}(undef,y.ngrid,y.ngrid) Dmat_err = Array{Float64,2}(undef,y.ngrid,y.ngrid) @@ -272,20 +282,31 @@ if abspath(PROGRAM_FILE) == @__FILE__ df_num = Array{Float64,1}(undef,y.n) df_err = Array{Float64,1}(undef,y.n) g_exact = Array{Float64,1}(undef,y.n) + h_exact = Array{Float64,1}(undef,y.n) divg_exact = Array{Float64,1}(undef,y.n) divg_num = Array{Float64,1}(undef,y.n) divg_err = Array{Float64,1}(undef,y.n) + laph_exact = Array{Float64,1}(undef,y.n) + laph_num = Array{Float64,1}(undef,y.n) + laph_err = Array{Float64,1}(undef,y.n) d2f_exact = Array{Float64,1}(undef,y.n) d2f_num = Array{Float64,1}(undef,y.n) d2f_err = Array{Float64,1}(undef,y.n) - + b = Array{Float64,1}(undef,y.n) for iy in 1:y.n f_exact[iy] = exp(-y.grid[iy]^2) df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) g_exact[iy] = y.grid[iy]*exp(-y.grid[iy]^2) divg_exact[iy] = 2.0*(1.0-y.grid[iy]^2)*exp(-y.grid[iy]^2) + h_exact[iy] = exp(-y.grid[iy]^2) + laph_exact[iy] = 4.0*(y.grid[iy]^2 - 1.0)*exp(-y.grid[iy]^2) + #h_exact[iy] = exp(-2.0*y.grid[iy]^2) + #laph_exact[iy] = 8.0*(2.0*y.grid[iy]^2 - 1.0)*exp(-2.0*y.grid[iy]^2) + #h_exact[iy] = exp(-y.grid[iy]^3) + #laph_exact[iy] = 9.0*y.grid[iy]*(y.grid[iy]^3 - 1.0)*exp(-y.grid[iy]^3) #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) - d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) + end if y.name == "y" F_exact = sqrt(pi) @@ -308,19 +329,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2f_err = d2f_num - d2f_exact println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) if y.name == "y" - b = Array{Float64,1}(undef,y.n) mul!(b,y_spectral.S_matrix,f_exact) gausslegendre_mass_matrix_solve!(df_num,b,y_spectral) @. df_err = df_num - df_exact #println("df_num (weak form): ",df_num) #println("df_exact (weak form): ",df_exact) println("max(df_err) (weak form): ",maximum(df_err)) - second_derivative!(d2f_num, f_exact, y, y_spectral) - @. d2f_err = d2f_num - d2f_exact + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) + @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) println("max(d2f_err) (weak form): ",maximum(d2f_err)) + plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") + outfile = "vpa_test.pdf" + savefig(outfile) + elseif y.name == "vperp" #println("condition: ",cond(y_spectral.mass_matrix)) b = Array{Float64,1}(undef,y.n) @@ -331,6 +356,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("divg_exact (weak form): ",divg_exact) println("max(divg_err) (weak form): ",maximum(divg_err)) + second_derivative!(d2f_num, f_exact, y, y_spectral) + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) + @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* + #println(d2f_num[1:10]) + #println(d2f_exact[1:10]) + println("max(d2f_err) (weak form): ",maximum(d2f_err)) + plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") + outfile = "vperp_second_derivative_test.pdf" + savefig(outfile) + + mul!(b,y_spectral.L_matrix,h_exact) + gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) + @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* + #println(b[1:10]) + #println(laph_num[1:10]) + #println(laph_exact[1:10]) + println("max(laph_err) (weak form): ",maximum(laph_err)) + plot([y.grid, y.grid], [laph_num, laph_exact], xlabel="vperp", label=["num" "exact"], ylabel="") + outfile = "vperp_laplacian_test.pdf" + savefig(outfile) + @. y.scratch = y.grid*g_exact derivative!(y.scratch2, y.scratch, y, y_spectral) @. divg_num = y.scratch2/y.grid diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index ccef87038..3164e4211 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -46,6 +46,12 @@ struct gausslegendre_base_info{} S0::Array{mk_float,2} # local S (weak derivative) matrix type 1 S1::Array{mk_float,2} + # local K (weak second derivative) matrix type 0 + K0::Array{mk_float,2} + # local K (weak second derivative) matrix type 1 + K1::Array{mk_float,2} + # local P (weak derivative) matrix type 0 + P0::Array{mk_float,2} end struct gausslegendre_info{} @@ -56,8 +62,14 @@ struct gausslegendre_info{} # global (1D) weak derivative matrix #S_matrix::Array{mk_float,2} S_matrix::AbstractSparseArray{mk_float,Ti,2} where Ti + # global (1D) weak second derivative matrix + K_matrix::Array{mk_float,2} + # global (1D) weak Laplacian derivative matrix + L_matrix::Array{mk_float,2} # global (1D) LU object mass_matrix_lu::T where T + # dummy matrix for local operators + Qmat::Array{mk_float,2} end function setup_gausslegendre_pseudospectral(coord) @@ -65,13 +77,17 @@ function setup_gausslegendre_pseudospectral(coord) radau = setup_gausslegendre_pseudospectral_radau(coord) mass_matrix = allocate_float(coord.n,coord.n) S_matrix = allocate_float(coord.n,coord.n) + K_matrix = allocate_float(coord.n,coord.n) + L_matrix = allocate_float(coord.n,coord.n) setup_global_mass_matrix!(mass_matrix, lobatto, radau, coord) setup_global_weak_form_matrix!(mass_matrix, lobatto, radau, coord, "M") setup_global_weak_form_matrix!(S_matrix, lobatto, radau, coord, "S") + setup_global_weak_form_matrix!(K_matrix, lobatto, radau, coord, "K") + setup_global_weak_form_matrix!(L_matrix, lobatto, radau, coord, "L") mass_matrix_lu = lu(sparse(mass_matrix)) - - return gausslegendre_info(lobatto,radau,mass_matrix,sparse(S_matrix),mass_matrix_lu) + Qmat = allocate_float(coord.ngrid,coord.ngrid) + return gausslegendre_info(lobatto,radau,mass_matrix,sparse(S_matrix),K_matrix,L_matrix,mass_matrix_lu,Qmat) end function setup_gausslegendre_pseudospectral_lobatto(coord) @@ -91,7 +107,13 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendre_weak_product_matrix!(S0,coord.ngrid,x,w,coord.L,coord.nelement_global,"S0") S1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(S1,coord.ngrid,x,w,coord.L,coord.nelement_global,"S1") - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1) + K0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K0,coord.ngrid,x,w,coord.L,coord.nelement_global,"K0") + K1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K1,coord.ngrid,x,w,coord.L,coord.nelement_global,"K1") + P0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,coord.L,coord.nelement_global,"P0") + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0) end function setup_gausslegendre_pseudospectral_radau(coord) @@ -115,7 +137,13 @@ function setup_gausslegendre_pseudospectral_radau(coord) GaussLegendre_weak_product_matrix!(S0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S0",radau=true) S1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(S1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S1",radau=true) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1) + K0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K0",radau=true) + K1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K1",radau=true) + P0 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P0",radau=true) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0) end """ function for taking the first derivative on Gauss-Legendre points @@ -164,20 +192,28 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] - if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme - @views mul!(df[:,j],gausslegendre.radau.Kmat[:,:],ff[imin:imax]) - else #differentiate using the Lobatto scheme - @views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) - end + get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + #println(gausslegendre.Qmat) + @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) + #if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme + # @views mul!(df[:,j],gausslegendre.radau.Kmat[:,:],ff[imin:imax]) + #else #differentiate using the Lobatto scheme + #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + #end # calculate the derivative on each element @inbounds for j ∈ 2:nelement k = 1 imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] - @views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + #println(gausslegendre.Qmat) + @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) end - + #for j in 1:nelement + # println(df[:,j]) + #end return nothing end @@ -380,6 +416,9 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op # for M1: AA = < P_i P_j x > # for S0: AA = -< P'_i P_j > # for S1: AA = -< P'_i P_j x > + # for K0: AA = -< P'_i P'_j > + # for K1: AA = -< P'_i P'_j x > + # for P0: AA = < P_i P'_j > AA = allocate_float(ngrid,ngrid) nquad = 2*ngrid zz, wz = gausslegendre(nquad) @@ -416,13 +455,32 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op end end end + elseif option == "K0" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] -= wz[k]*dnPl(zz[k],i-1,1)*dnPl(zz[k],j-1,1) + end + end + end + elseif option == "K1" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] -= zz[k]*wz[k]*dnPl(zz[k],i-1,1)*dnPl(zz[k],j-1,1) + end + end + end + elseif option == "P0" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += wz[k]*Pl(zz[k],i-1)*dnPl(zz[k],j-1,1) + end + end + end end - #for i in 1:ngrid - # for j in 1:ngrid - # print(" ", AA[i,j]) - # end - # println("") - #end + QQ .= 0.0 for j in 1:ngrid for i in 1:ngrid @@ -521,7 +579,8 @@ function scale_factor_func(L,nelement_global) end function shift_factor_func(L,nelement_global,nelement_local,irank,ielement_local) - ielement_global = ielement_local + irank*nelement_local + #ielement_global = ielement_local # for testing + irank*nelement_local + ielement_global = ielement_local + irank*nelement_local # proper line for future distributed memory MPI use shift = L*((float(ielement_global)-0.5)/float(nelement_global) - 0.5) return shift end @@ -762,6 +821,10 @@ function get_QQ_local!(QQ,ielement, get_MM_local!(QQ,ielement,lobatto,radau,coord) elseif option == "S" get_SS_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "K" + get_KK_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "L" + get_LL_local!(QQ,ielement,lobatto,radau,coord) end return nothing end @@ -775,15 +838,14 @@ function get_MM_local!(QQ,ielement, shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral - if ielement > 1 # lobatto points - @. QQ = shift_factor*lobatto.M0 + scale_factor*lobatto.M1 + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (shift_factor*lobatto.M0 + scale_factor*lobatto.M1)*scale_factor else # radau points - @. QQ = shift_factor*radau.M0 + scale_factor*radau.M1 + @. QQ = (shift_factor*radau.M0 + scale_factor*radau.M1)*scale_factor end else # assume integrals of form int^infty_-infty (.) d vpa - @. QQ = lobatto.M0 - end - @. QQ *= scale_factor + @. QQ = lobatto.M0*scale_factor + end return nothing end @@ -796,7 +858,7 @@ function get_SS_local!(QQ,ielement, shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral - if ielement > 1 # lobatto points + if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = shift_factor*lobatto.S0 + scale_factor*lobatto.S1 else # radau points @. QQ = shift_factor*radau.S0 + scale_factor*radau.S1 @@ -804,8 +866,48 @@ function get_SS_local!(QQ,ielement, else # assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.S0 end - # no scaling factor here because d /d x * dx is invariant under length scale transforms - #@. QQ *= scale_factor + return nothing +end + +function get_KK_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 - lobatto.P0 + else # radau points + @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 - radau.P0 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.K0/scale_factor + end + return nothing +end + +function get_LL_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + # (1/vperp) d ( vperp d (.) / d vperp) + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 + else # radau points + @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 + end + else # d^2 (.) d vpa^2 -- assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.K0/scale_factor + end return nothing end From 7d749ef3e8e4a39fbf30d4833dbabaf367dfdace Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 22 Jul 2023 20:28:42 +0100 Subject: [PATCH 061/331] Laplacian derivative test for the vperp coordinate, using direct interpolation derivatives. --- GaussLobattoLegendre_test.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index a8ea98059..78999249f 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -208,7 +208,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements - y_L = 12.0 #physical box size in reference units + y_L = 6.0 #physical box size in reference units bc = "zero" discretization = "gausslegendre_pseudospectral" # fd_option and adv_input not actually used so given values unimportant @@ -384,6 +384,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. divg_err = abs(divg_num - divg_exact) println("max(divg_err) (interpolation): ",maximum(divg_err)) + derivative!(y.scratch, h_exact, y, y_spectral) + @. y.scratch2 = y.grid*y.scratch + derivative!(y.scratch, y.scratch2, y, y_spectral) + @. laph_num = y.scratch/y.grid + @. laph_err = abs(laph_num - laph_exact) + println("max(laph_err) (interpolation): ",maximum(laph_err)) + end From 97ad43acf4425266221e22f41f84dafbbe38759e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 23 Jul 2023 18:28:31 +0100 Subject: [PATCH 062/331] Added a plausible regularity condition at vperp = 0 for taking derivatives using the weak method. We assume that near vperp = 0, the function is a function of vperp2, so that we can impose d g / d vperp = 0 whenever we calculate g = d2 F / d vperp2 (or other second derivatives). The mass matrix has the information encoded in the 1st row. Using this method requires the 1st element in the RHS vector to be zero. --- GaussLobattoLegendre_test.jl | 21 +++++++----- src/gauss_legendre.jl | 63 ++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 78999249f..59ac05ddf 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max df_err: ",max_df_err) # elemental grid tests - ngrid = 5 - nelement = 32 + ngrid = 3 + nelement = 100 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -239,14 +239,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) - #print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) - #print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) print_matrix(y_spectral.radau.K0,"local radau K matrix K0",y.ngrid,y.ngrid) print_matrix(y_spectral.radau.K1,"local radau K matrix K1",y.ngrid,y.ngrid) print_matrix(y_spectral.lobatto.K0,"local K matrix K0",y.ngrid,y.ngrid) print_matrix(y_spectral.lobatto.K1,"local K matrix K1",y.ngrid,y.ngrid) + print_matrix(y_spectral.radau.P0,"local radau P matrix P0",y.ngrid,y.ngrid) + print_matrix(y_spectral.lobatto.P0,"local P matrix P0",y.ngrid,y.ngrid) #print_matrix(y_spectral.K_matrix,"global K matrix",y.n,y.n) + #print_matrix(y_spectral.L_matrix,"global L matrix",y.n,y.n) #@views y_spectral.K_matrix[1,:] *= (4.0/3.0) #print_matrix(y_spectral.K_matrix,"global K matrix (hacked) ",y.n,y.n) @@ -360,8 +363,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ #mul!(b,y_spectral.K_matrix,f_exact) #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* - #println(d2f_num[1:10]) - #println(d2f_exact[1:10]) + println(d2f_num[1:10]) + println(d2f_exact[1:10]) + println(d2f_err[1:10]) println("max(d2f_err) (weak form): ",maximum(d2f_err)) plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") outfile = "vperp_second_derivative_test.pdf" @@ -371,8 +375,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* #println(b[1:10]) - #println(laph_num[1:10]) - #println(laph_exact[1:10]) + println(laph_num[1:10]) + println(laph_exact[1:10]) + println(laph_err[1:10]) println("max(laph_err) (weak form): ",maximum(laph_err)) plot([y.grid, y.grid], [laph_num, laph_exact], xlabel="vperp", label=["num" "exact"], ylabel="") outfile = "vperp_laplacian_test.pdf" diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 3164e4211..48a05f00c 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -52,6 +52,8 @@ struct gausslegendre_base_info{} K1::Array{mk_float,2} # local P (weak derivative) matrix type 0 P0::Array{mk_float,2} + # boundary condition differentiation matrix (for vperp grid using radau points) + D0::Array{mk_float,1} end struct gausslegendre_info{} @@ -113,7 +115,10 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendre_weak_product_matrix!(K1,coord.ngrid,x,w,coord.L,coord.nelement_global,"K1") P0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,coord.L,coord.nelement_global,"P0") - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0) + D0 = allocate_float(coord.ngrid) + #@. D0 = Dmat[1,:] # values at lower extreme of element + GaussLegendre_derivative_vector!(D0,1,coord.ngrid,x,w,coord.L,coord.nelement_global) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) end function setup_gausslegendre_pseudospectral_radau(coord) @@ -143,7 +148,9 @@ function setup_gausslegendre_pseudospectral_radau(coord) GaussLegendre_weak_product_matrix!(K1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K1",radau=true) P0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P0",radau=true) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0) + D0 = allocate_float(coord.ngrid) + GaussLegendre_derivative_vector!(D0,1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) end """ function for taking the first derivative on Gauss-Legendre points @@ -195,11 +202,11 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) - #if coord.name == "vperp" && coord.irank == 0 # differentiate this element with the Radau scheme - # @views mul!(df[:,j],gausslegendre.radau.Kmat[:,:],ff[imin:imax]) - #else #differentiate using the Lobatto scheme - #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) - #end + if coord.name == "vperp" + # set the 1st point of the RHS vector to zero + # consistent with use with the mass matrix with D f = 0 boundary conditions + df[1,j] = 0.0 + end # calculate the derivative on each element @inbounds for j ∈ 2:nelement k = 1 @@ -299,6 +306,39 @@ function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array return nothing end +""" +Gauss-Legendre derivative at arbitrary x values, for boundary condition on radau points +D0 -- the vector +j -- the index of x where the derivative is evaluated +ngrid -- number of points in x +x -- the grid from -1, 1 +L -- size of physical domain +""" +function GaussLegendre_derivative_vector!(D0,j,ngrid,x,wgts,L,nelement_global;radau=false) + # coefficient in expansion of + # lagrange polys in terms of Legendre polys + gamma = allocate_float(ngrid) + for i in 1:ngrid-1 + gamma[i] = Legendre_h_n(i-1) + end + if radau + gamma[ngrid] = Legendre_h_n(ngrid-1) + else + gamma[ngrid] = 2.0/(ngrid - 1) + end + + @. D0 = 0.0 + for i in 1:ngrid + for k in 1:ngrid + D0[i] += wgts[i]*Pl(x[i],k-1)*dnPl(x[j],k-1,1)/gamma[k] + end + end + # set `diagonal' value + D0[j] = 0.0 + D0[j] = -sum(D0[:]) + @. D0 *= 2.0*float(nelement_global)/L +end + """ result of the inner product of Legendre polys of order k """ @@ -761,9 +801,11 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, if coord.name == "vperp" zero_bc_upper_boundary = true zero_bc_lower_boundary = false + zero_gradient_bc_lower_boundary = true else zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" + zero_gradient_bc_lower_boundary = false end # fill in first element j = 1 @@ -774,6 +816,13 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, if zero_bc_lower_boundary #x.bc == "zero" QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:]./2.0 #contributions from this element/2 QQ_global[imin[j],imin[j]] += QQ_j[ngrid,ngrid]/2.0 #contribution from missing `zero' element/2 + elseif zero_gradient_bc_lower_boundary + if option == "M" && coord.name == "vperp" + #QQ_global[imin[j],imin[j]:imax[j]] .= lobatto.D0[:] + QQ_global[imin[j],imin[j]:imax[j]] .= radau.D0[:] + else + QQ_global[imin[j],imin[j]:imax[j]] .= 0.0 + end else QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:] end From 07fc47fe6cc4ec864d3127b524aecff33c577adb Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 25 Jul 2023 17:31:18 +0100 Subject: [PATCH 063/331] Experimenting with options for weak method vperp differentiation. --- src/gauss_legendre.jl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 48a05f00c..4b5f4052e 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -202,7 +202,8 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) - if coord.name == "vperp" + zero_gradient_bc_lower_boundary = false#true + if coord.name == "vperp" && zero_gradient_bc_lower_boundary # set the 1st point of the RHS vector to zero # consistent with use with the mass matrix with D f = 0 boundary conditions df[1,j] = 0.0 @@ -531,6 +532,14 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op end end end + #if option == "K0" || option == "K1" || option == "S0" || option == "P0" + # compute diagonal from off-diagonal values + # to ensure numerical stability + # for i in 1:ngrid + # QQ[i,i] = 0.0 + # QQ[i,i] = -sum(QQ[i,:]) + # end + #end # return normalised Q (no scale factors) #@. QQ *= (0.5*L/nelement_global) return nothing @@ -625,6 +634,7 @@ function shift_factor_func(L,nelement_global,nelement_local,irank,ielement_local return shift end + """ function for setting up the full Gauss-Legendre-Lobatto grid and collocation point weights @@ -801,7 +811,7 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, if coord.name == "vperp" zero_bc_upper_boundary = true zero_bc_lower_boundary = false - zero_gradient_bc_lower_boundary = true + zero_gradient_bc_lower_boundary = false#true else zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" From d04ad38f4112e9a9caa9726f3c75659b26ee7ff7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 25 Jul 2023 17:35:19 +0100 Subject: [PATCH 064/331] Test script for exploring distributed memory MPI calculation of the G integration weights, with the aim of reducing the N^4 scaling to a N^2 scaling for the weights calculation. --- fkpl_mpi_test.jl | 432 ++++++++++++++++++++++++++++++++++++++++++ src/gauss_legendre.jl | 4 + 2 files changed, 436 insertions(+) create mode 100644 fkpl_mpi_test.jl diff --git a/fkpl_mpi_test.jl b/fkpl_mpi_test.jl new file mode 100644 index 000000000..24f30d42f --- /dev/null +++ b/fkpl_mpi_test.jl @@ -0,0 +1,432 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using SpecialFunctions: erf, ellipe, ellipk +using FastGaussQuadrature +using Dates +using LinearAlgebra: mul! + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.gauss_legendre: ielement_global_func +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs, F_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float + +function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + bc = "zero" + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input, comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input, comm) + + # create the coordinate structs + #println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end + + # define distribution memory MPI versions of these coordinates + vpa_MPI_ngrid = ngrid #number of points per element + vpa_MPI_nelement_local = 2# nelement # number of elements per rank + vpa_MPI_nelement_global = nelement # total number of elements + #vpa_MPI_L = 12.0 #physical box size in reference units + vperp_MPI_ngrid = ngrid #number of points per element + vperp_MPI_nelement_local = 2# nelement # number of elements per rank + vperp_MPI_nelement_global = nelement # total number of elements + #vperp_MPI_L = 6.0 #physical box size in reference units + irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp = setup_distributed_memory_MPI(vpa_MPI_nelement_global,vpa_MPI_nelement_local, + vperp_MPI_nelement_global,vperp_MPI_nelement_local) + vpa_MPI_input = grid_input("vpa", vpa_ngrid, vpa_MPI_nelement_global, vpa_MPI_nelement_local, + nrank_vpa, irank_vpa, vpa_L, discretization, fd_option, cheb_option, bc, adv_input, comm_sub_vpa) + vperp_MPI_input = grid_input("vperp", vperp_MPI_ngrid, vperp_MPI_nelement_global, vperp_MPI_nelement_local, + nrank_vperp, irank_vperp, vperp_L, discretization, fd_option, cheb_option, bc, adv_input, comm_sub_vperp) + vpa_MPI = define_coordinate(vpa_MPI_input) + #println("vpa ",vpa_MPI.grid) + vperp_MPI = define_coordinate(vperp_MPI_input) + #println("vperp ",vperp_MPI.grid) + + return vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI +end + +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end + +function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth + return eta +end + +""" +functions for doing the Lagrange polynomial integration +""" + +function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax +end + +function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes +end +""" +Lagrange polynomial +args: +j - index of l_j from list of nodes +x_nodes - array of x node values +x - point where interpolated value is returned +""" +function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly +end + +function get_scaled_x_w!(x_scaled, w_scaled, x, w, node_min, node_max) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled = scale*x + shift + @. w_scaled = scale*w + return nothing +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + # Set up MPI + initialize_comms!() + + nelement = 4 + ngrid = 5 + + vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI = init_grids(nelement,ngrid) + + nvpa_local = vpa_MPI.n + nvpa_global = vpa_MPI.n_global + nvperp_local = vperp_MPI.n + nvperp_global = vperp_MPI.n_global + + # coord.n is the local number of points in each shared memory block + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=nvperp_local, vpa=nvpa_local, + vzeta=1, vr=1, vz=1) + if global_rank[] == 0 + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + fsp_in = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_Maxwell = Array{mk_float,2}(undef,nvpa_local,nvperp_local) + G_weights = allocate_shared_float(nvpa_local,nvperp_local,nvpa_global,nvperp_global) + Gsp = allocate_shared_float(nvpa_local,nvperp_local) + G_err = allocate_shared_float(nvpa_local,nvperp_local) + + Gsp_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_Maxwell_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_err_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + + if global_rank[] == 0 + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + # set up test Maxwellian + # species sp + denssp = 1.0 #3.0/4.0 + uparsp = 0.5 #2.0/3.0 + pparsp = 1.0 #2.0/3.0 + pperpsp = 1.0 #2.0/3.0 + pressp = get_pressure(pparsp,pperpsp) + msp = 1.0 + vthsp = get_vth(pressp,denssp,msp) + + for ivperp in 1:nvperp_global + for ivpa in 1:nvpa_global + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + G_Maxwell_global[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + end + end + for ivperp in 1:nvperp_local + for ivpa in 1:nvpa_local + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa_MPI,vperp_MPI,ivpa,ivperp) + end + end + + if global_rank[] == 0 + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x, w = gausslegendre(nquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + + if global_rank[] == 0 + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + vperp_val = vperp_MPI.grid[ivperp] + vpa_val = vpa_MPI.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + + # loop over elements and grid points within elements on primed coordinate + for ielement_vperp in 1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperp) + vperp_max = vperp_nodes[end] + if ielement_vperp > 1 # Gauss-Lobatto + vperp_min = vperp_nodes[1] + else # adjust for the Gauss-Radau element + vperp_min = 0.0 + end + get_scaled_x_w!(x_vperp, w_vperp, x, w, vperp_min, vperp_max) + + for ielement_vpa in 1:vpa.nelement_local + + vpa_nodes = get_nodes(vpa,ielement_vpa) + # assumme Gauss-Lobatto elements + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + get_scaled_x_w!(x_vpa, w_vpa, x, w, vpa_min, vpa_max) + + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad + for kvpa in 1:nquad + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + end + end + end + end + end + end + end + + #_block_synchronize() + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + begin_vperp_vpa_region() + + # use precalculated weights to calculate Gsp using nodal values of fs + @loop_vperp_vpa ivperp ivpa begin + Gsp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp_global + for ivpap in 1:nvpa_global + Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end + end + end + + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + + # local plotting + plot_G = false#true + + begin_serial_region() + @serial_region begin + @. G_err = abs(Gsp - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + if plot_G + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + end + end + + @. Gsp_global = 0.0 + # allreduce operations + # get local data into global array before allreduce + begin_serial_region() + @serial_region begin + # set up indexes for doing the assignment to the global array + iel_vpa_min = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,1) + iel_vpa_max = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,vpa_MPI.nelement_local) + ivpa_global_min = vpa.imin[iel_vpa_min] + ivpa_global_max = vpa.imax[iel_vpa_max] + if vpa_MPI.irank > 0 + j = 1 #exclude lowest point + else + j = 0 #include lowest point + end + ivpa_local_min = 1 + j + ivpa_local_max = vpa_MPI.n + + iel_vperp_min = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,1) + iel_vperp_max = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,vperp_MPI.nelement_local) + ivperp_global_min = vperp.imin[iel_vperp_min] + ivperp_global_max = vperp.imax[iel_vperp_max] + if vperp_MPI.irank > 0 + k = 1 #exclude lowest point + else + k = 0 #include lowest point + end + ivperp_local_min = 1 + k + ivperp_local_max = vperp_MPI.n + + @. Gsp_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max] += Gsp[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max] + + # first reduce along vperp dimension, then along vpa + MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) + MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) + end + + # global plotting + plot_G = true + + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + @. G_err_global = abs(Gsp_global - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global): ",max_G_err) + if plot_G + @views heatmap(vperp.grid, vpa.grid, Gsp_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end + end + end + # clean up MPI objects + finalize_comms!() + + +end \ No newline at end of file diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 4b5f4052e..1313160b3 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -18,6 +18,7 @@ export gausslegendre_apply_Kmat! export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral export GaussLegendre_weak_product_matrix! +export ielement_global_func using FastGaussQuadrature using LegendrePolynomials: Pl, dnPl @@ -634,6 +635,9 @@ function shift_factor_func(L,nelement_global,nelement_local,irank,ielement_local return shift end +function ielement_global_func(nelement_local,irank,ielement_local) + return ielement_global = ielement_local + irank*nelement_local +end """ function for setting up the full Gauss-Legendre-Lobatto From f4545e1ea3994b3d0c7490b9b2d57244732e4997 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 26 Jul 2023 11:06:21 +0100 Subject: [PATCH 065/331] correct typo in comments --- src/communication.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/communication.jl b/src/communication.jl index 198c4f38a..8dafc210f 100644 --- a/src/communication.jl +++ b/src/communication.jl @@ -103,11 +103,11 @@ function setup_distributed_memory_MPI(z_nelement_global,z_nelement_local,r_nelem nrank_global = global_size[] # number of processes # get information about how the grid is divided up - # number of sections `chunks' of the x grid + # number of sections `chunks' of the r grid r_nchunks = floor(mk_int,r_nelement_global/r_nelement_local) # number of sections `chunks' of the z grid z_nchunks = floor(mk_int,z_nelement_global/z_nelement_local) # number of sections of the z grid - # get the number of shared-memorz blocks in the z r decomposition + # get the number of shared-memory blocks in the z r decomposition nblocks = r_nchunks*z_nchunks # get the number of ranks per block nrank_per_zr_block = floor(mk_int,nrank_global/nblocks) From 4f12e7924bb14b6340cd51b6c28b7c1cc9a68035 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Jul 2023 09:05:15 +0100 Subject: [PATCH 066/331] Added distributed memory MPI which can handle more cores than can do useful work in the calculation of the Rosenbluth potential G. The user must specify sensible inputs to maximise the number of used cores. --- fkpl_mpi_test.jl | 39 ++++++++---- src/communication.jl | 141 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 13 deletions(-) diff --git a/fkpl_mpi_test.jl b/fkpl_mpi_test.jl index 24f30d42f..d0fcc1122 100644 --- a/fkpl_mpi_test.jl +++ b/fkpl_mpi_test.jl @@ -30,7 +30,7 @@ using moment_kinetics.communication using moment_kinetics.looping using moment_kinetics.array_allocation: allocate_shared_float -function init_grids(nelement,ngrid) +function init_grids(nelement,nelement_local,ngrid,max_cores_per_shm_block) discretization = "gausslegendre_pseudospectral" #discretization = "chebyshev_pseudospectral" #discretization = "finite_difference" @@ -78,15 +78,23 @@ function init_grids(nelement,ngrid) # define distribution memory MPI versions of these coordinates vpa_MPI_ngrid = ngrid #number of points per element - vpa_MPI_nelement_local = 2# nelement # number of elements per rank + vpa_MPI_nelement_local = nelement_local # nelement # number of elements per rank vpa_MPI_nelement_global = nelement # total number of elements #vpa_MPI_L = 12.0 #physical box size in reference units vperp_MPI_ngrid = ngrid #number of points per element - vperp_MPI_nelement_local = 2# nelement # number of elements per rank + vperp_MPI_nelement_local = nelement_local# nelement # number of elements per rank vperp_MPI_nelement_global = nelement # total number of elements #vperp_MPI_L = 6.0 #physical box size in reference units - irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp = setup_distributed_memory_MPI(vpa_MPI_nelement_global,vpa_MPI_nelement_local, - vperp_MPI_nelement_global,vperp_MPI_nelement_local) + + # using standard MPI routine with assumption of exactly the right number of cores + #irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp = setup_distributed_memory_MPI(vpa_MPI_nelement_global,vpa_MPI_nelement_local, + # vperp_MPI_nelement_global,vperp_MPI_nelement_local) + # novel MPI routine that tries to use only a subset of the cores available + # need to specify max number of cores in a shared-memory region (depends on hardware) + + ( (irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp) = + setup_distributed_memory_MPI_for_weights_precomputation(vpa_MPI_nelement_global,vpa_MPI_nelement_local, + vperp_MPI_nelement_global,vperp_MPI_nelement_local,max_cores_per_shm_block,printout=false)) vpa_MPI_input = grid_input("vpa", vpa_ngrid, vpa_MPI_nelement_global, vpa_MPI_nelement_local, nrank_vpa, irank_vpa, vpa_L, discretization, fd_option, cheb_option, bc, adv_input, comm_sub_vpa) vperp_MPI_input = grid_input("vperp", vperp_MPI_ngrid, vperp_MPI_nelement_global, vperp_MPI_nelement_local, @@ -181,10 +189,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # Set up MPI initialize_comms!() - nelement = 4 - ngrid = 5 + nelement_global = 4 # global number of elements in vpa (vperp) + nelement_local = 1 # local number of elements in each distributed memory `chunk' of the vpa (vperp) grid + ngrid = 5 # number of points per element + max_cores_per_shm_block = 4 # maximum number of cores in a shared-memory block - vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI = init_grids(nelement,ngrid) + vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI = init_grids(nelement_global,nelement_local,ngrid,max_cores_per_shm_block) nvpa_local = vpa_MPI.n nvpa_global = vpa_MPI.n_global @@ -340,14 +350,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # local plotting - plot_G = false#true + plot_local_G = false#true begin_serial_region() @serial_region begin - @. G_err = abs(Gsp - G_Maxwell) - max_G_err = maximum(G_err) - println("max_G_err: ",max_G_err) - if plot_G + if plot_local_G + @. G_err = abs(Gsp - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) @views heatmap(vperp_MPI.grid, vpa_MPI.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_G_lagrange"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") @@ -398,6 +408,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # first reduce along vperp dimension, then along vpa MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) + # then broadcast to all cores, noting that some may + # not be included in the communicators used in the calculation + MPI.Bcast!(Gsp_global,comm_world,root=0) end # global plotting diff --git a/src/communication.jl b/src/communication.jl index 8dafc210f..8d41123c9 100644 --- a/src/communication.jl +++ b/src/communication.jl @@ -16,6 +16,7 @@ export allocate_shared, block_rank, block_size, comm_block, comm_inter_block, iblock_index, comm_world, finalize_comms!, initialize_comms!, global_rank, MPISharedArray, global_size export setup_distributed_memory_MPI +export setup_distributed_memory_MPI_for_weights_precomputation export _block_synchronize using MPI @@ -96,6 +97,13 @@ notation definitions: - block: group of processes that share data with shared memory - z group: group of processes that need to communicate data for z derivatives - r group: group of processes that need to communicate data for r derivatives +This routine assumes that the number of processes is selected by the user +to match exactly the number the ratio + + nblocks = (r_nelement_global/r_nelement_local)*(z_nelement_global/z_nelement_local) + +This guarantees perfect load balancing. Shared memory is used to parallelise the other +dimensions within each distributed-memory parallelised rz block. """ function setup_distributed_memory_MPI(z_nelement_global,z_nelement_local,r_nelement_global,r_nelement_local; printout=false) # setup some local constants and dummy variables @@ -204,6 +212,139 @@ function setup_distributed_memory_MPI(z_nelement_global,z_nelement_local,r_nelem return z_irank, z_nrank_per_group, z_comm, r_irank, r_nrank_per_group, r_comm end +""" +Function to take information from user about vpa vperp grids and +number of processes allocated to set up communicators for +precomputation of the Rosenbluth potential integration weights +notation definitions: + - block: group of processes that share data with shared memory + - vpa group: group of processes that need to communicate data for vpa derivatives/integrals + - vperp group: group of processes that need to communicate data for vperp derivatives/integrals +This routine assumes that the number of processes is selected by the user +to match or be larger than the ratio + + nblocks = (vpa_nelement_global/vpa_nelement_local)*(vperp_nelement_global/vperp_nelement_local) + +We also need to know (from user input) the maximum number of cores per shared memory region. +A fraction of the cores will not contribute to the calculation, as we cannot guarantee that +the same number of cores is required for the rz parallelisation as the vpa vperp parallelisation +""" +function setup_distributed_memory_MPI_for_weights_precomputation(vpa_nelement_global,vpa_nelement_local, + vperp_nelement_global,vperp_nelement_local, max_cores_per_block; printout=false) + # setup some local constants and dummy variables + irank_global = global_rank[] # rank index within global processes + nrank_global = global_size[] # number of processes + + # get information about how the grid is divided up + # number of sections `chunks' of the vperp grid + vperp_nchunks = floor(mk_int,vperp_nelement_global/vperp_nelement_local) + # number of sections `chunks' of the vpa grid + vpa_nchunks = floor(mk_int,vpa_nelement_global/vpa_nelement_local) + # get the number of shared-memory blocks in the vpa vperp decomposition + nblocks = vperp_nchunks*vpa_nchunks + # get the number of ranks per block + nrank_per_vpavperp_block = min(floor(mk_int,nrank_global/nblocks), max_cores_per_block) + # get the total number of useful cores + nrank_vpavperp = nrank_per_vpavperp_block*nblocks + # N.B. the user should pick the largest possible value for nblocks that is consistent + # with the total number of cores available and complete shared-memory regions. This + # should be done by choosing + # (vperp_nelement_global/vperp_nelement_local)*(vpa_nelement_global/vpa_nelement_local) + # in the input file. For example, if there are 26 cores available, and 8 global elements in + # each dimension, we should choose 4 local elements, making nblocks = 16 and nrank_per_vpavperp_block = 1. + if printout + println("debug info:") + println("nrank_global: ",nrank_global) + println("vperp_nchunks: ",vperp_nchunks) + println("vpa_nchunks: ",vpa_nchunks) + println("nblocks: ",nblocks) + println("nrank_per_vpavperp_block: ",nrank_per_vpavperp_block) + println("max_cores_per_block: ",max_cores_per_block) + end + + # Create a communicator which includes enough cores for the calculation + # and includes irank_global = 0. Excess cores have a copy of the communicator + # with a different color. After the calculation is completed a MPI broadcast + # on the world communicator should be carried out to get the data to the + # excess cores. + irank_vpavperp = mod(irank_global,nrank_vpavperp) + igroup_vpavperp = floor(mk_int,irank_global/nrank_vpavperp) + comm_vpavperp = MPI.Comm_split(comm_world,igroup_vpavperp,irank_vpavperp) + # MPI.Comm_split(comm,color,key) + # comm -> communicator to be split + # color -> label of group of processes + # key -> label of process in group + # if color == nothing then this process is excluded from the communicator + + # assign information regarding shared-memory blocks + # block index -- which block is this process in + iblock = floor(mk_int,irank_vpavperp/nrank_per_vpavperp_block) + # rank index within a block + irank_block = mod(irank_vpavperp,nrank_per_vpavperp_block) + + if printout + println("iblock: ",iblock) + println("irank_block: ",irank_block) + end + # assign the block rank to the global variables + iblock_index[] = iblock + block_rank[] = irank_block + block_size[] = nrank_per_vpavperp_block + # construct a communicator for intra-block communication + comm_block[] = MPI.Comm_split(comm_vpavperp,iblock,irank_block) + + vpa_ngroup = vpa_nchunks + vpa_nrank_per_group = vpa_nchunks + vpa_igroup = floor(mk_int,iblock/vpa_nchunks) # iblock(irank) - > vpa_igroup + vpa_irank = mod(iblock,vpa_nchunks) # iblock(irank) -> vpa_irank + # iblock = vpa_igroup * vpa_nchunks + vpa_irank_sub + + if printout + # useful information for debugging + println("vpa_ngroup: ",vpa_ngroup) + println("vpa_nrank_per_group: ",vpa_nrank_per_group) + println("vpa_igroup: ",vpa_igroup) + println("vpa_irank_sub: ",vpa_irank) + println("iblock: ",iblock, " ", vpa_igroup * vpa_nchunks + vpa_irank) + println("") + end + + vperp_ngroup = vpa_nchunks + vperp_nrank_per_group = vperp_nchunks + vperp_igroup = vpa_irank # block(irank) - > vperp_igroup + vperp_irank = vpa_igroup # block(irank) -> vperp_irank + # irank = vperp_igroup + vpa_nrank_per_group * vperp_irank + + if printout + # useful information for debugging + println("vperp_ngroup: ",vperp_ngroup) + println("vperp_nrank_per_group: ",vperp_nrank_per_group) + println("vperp_igroup: ",vperp_igroup) + println("vperp_irank: ",vperp_irank) + println("iblock: ",iblock, " ", vperp_irank * vperp_ngroup + vperp_igroup) + println("") + end + + # construct communicators for inter-block communication + # only communicate between lead processes on a block + if block_rank[] == 0 #&& utilised_core + comm_inter_block[] = MPI.Comm_split(comm_vpavperp, 0, iblock) + vperp_comm = MPI.Comm_split(comm_vpavperp,vperp_igroup,vperp_irank) + vpa_comm = MPI.Comm_split(comm_vpavperp,vpa_igroup,vpa_irank) + else # assign a dummy value + comm_inter_block[] = MPI.Comm_split(comm_vpavperp, nothing, iblock) + vperp_comm = MPI.Comm_split(comm_vpavperp,nothing,vperp_irank) + vpa_comm = MPI.Comm_split(comm_vpavperp,nothing,vpa_irank) + end + # MPI.Comm_split(comm,color,key) + # comm -> communicator to be split + # color -> label of group of processes + # key -> label of process in group + # if color == nothing then this process is excluded from the communicator + + return vpa_irank, vpa_nrank_per_group, vpa_comm, vperp_irank, vperp_nrank_per_group, vperp_comm +end + @debug_shared_array begin """ Special type for debugging race conditions in accesses to shared-memory arrays. From 042f122db459cd8ee483fcf1bc2db5daacaf27ff Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Jul 2023 09:31:29 +0100 Subject: [PATCH 067/331] Extension of the test to store the global integration weights, and use them with the usual shared-memory arrangement where all vpa and vperp are local on the shared memory block. --- fkpl_mpi_test.jl | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/fkpl_mpi_test.jl b/fkpl_mpi_test.jl index d0fcc1122..f935adac8 100644 --- a/fkpl_mpi_test.jl +++ b/fkpl_mpi_test.jl @@ -220,6 +220,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Gsp_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) G_Maxwell_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) G_err_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_weights_global = Array{mk_float,4}(undef,nvpa_global,nvperp_global,nvpa_global,nvperp_global) if global_rank[] == 0 @serial_region begin @@ -374,6 +375,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. Gsp_global = 0.0 + @. G_weights_global = 0.0 # allreduce operations # get local data into global array before allreduce begin_serial_region() @@ -404,17 +406,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_local_max = vperp_MPI.n @. Gsp_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max] += Gsp[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max] + @. G_weights_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max,:,:] += G_weights[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max,:,:] # first reduce along vperp dimension, then along vpa MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vperp_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vpa_MPI.comm) # then broadcast to all cores, noting that some may # not be included in the communicators used in the calculation MPI.Bcast!(Gsp_global,comm_world,root=0) + MPI.Bcast!(G_weights_global,comm_world,root=0) end # global plotting - plot_G = true + plot_G = false #true begin_serial_region() if global_rank[] == 0 @@ -436,8 +442,34 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_G_err.pdf") savefig(outfile) end + end + end + + # check that the weights have been calculated properly by recalculating G + # using the looping and MPI arrangement of the main code + z_irank, z_nrank_per_group, z_comm, r_irank, r_nrank_per_group, r_comm = setup_distributed_memory_MPI(1,1,1,1) + # coord.n is the local number of points in each shared memory block + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=nvperp_global, vpa=nvpa_global, + vzeta=1, vr=1, vz=1) + Gsp_global_shared = allocate_shared_float(nvpa_global,nvperp_global) + # use precalculated weights to calculate Gsp using nodal values of fs + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Gsp_global_shared[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp_global + for ivpap in 1:nvpa_global + Gsp_global_shared[ivpa,ivperp] += G_weights_global[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end end end + begin_serial_region() + @serial_region begin + @. G_err_global = abs(Gsp_global_shared - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global from redistributed weights): ",max_G_err) + end # clean up MPI objects finalize_comms!() From 84e0ceabe307472b3681740f193a382b3f6dcd74 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Jul 2023 11:04:41 +0100 Subject: [PATCH 068/331] Added plots to test the convergence of the calculation of the derivatives of Fs and Fsprime. --- fkpl_test.jl | 115 +++++++++++++++++++++++++++++++++++++++++-- src/fokker_planck.jl | 3 +- 2 files changed, 113 insertions(+), 5 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index aa21de8f9..721503f3b 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -21,7 +21,9 @@ using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Max using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs, F_Maxwellian +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative!, second_derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -435,12 +437,30 @@ if abspath(PROGRAM_FILE) == @__FILE__ dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) @@ -515,7 +535,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivperp in 1:nvperp for ivpa in 1:nvpa fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) @@ -524,6 +555,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, denssp,uparsp,vthsp,msp, nussp,vpa,vperp,ivpa,ivperp) @@ -547,7 +579,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2fspdvpa2[:,ivperp] = vpa.scratch2 end if vpa.discretization == "gausslegendre_pseudospectral" - println("use weak-form second derivative for vpa") + @serial_region begin + println("use weak-form second derivative for vpa") + end for ivperp in 1:nvperp @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) @. d2fsdvpa2[:,ivperp] = vpa.scratch2 @@ -576,6 +610,37 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch end + # error analysis of distribution function + @serial_region begin + @. dfsdvpa_err = abs(dfsdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. d2fsdvpa2_err = abs(d2fsdvpa2 - d2fsdvpa2_Maxwell) + max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) + println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) + @. dfsdvperp_err = abs(dfsdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(d2fsdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + @. d2fsdvperp2_err = abs(d2fsdvperp2 - d2fsdvperp2_Maxwell) + max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) + println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) + + @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) + max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) + println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) + @. dfspdvperp_err = abs(dfspdvperp - dfspdvperp_Maxwell) + max_dfspdvperp_err = maximum(dfspdvperp_err) + println("max_dfspdvperp_err: ",max_dfspdvperp_err) + @. d2fspdvperpdvpa_err = abs(d2fspdvperpdvpa - d2fspdvperpdvpa_Maxwell) + max_d2fspdvperpdvpa_err = maximum(d2fspdvperpdvpa_err) + println("max_d2fspdvperpdvpa_err: ",max_d2fspdvperpdvpa_err) + @. d2fspdvperp2_err = abs(d2fspdvperp2 - d2fspdvperp2_Maxwell) + max_d2fspdvperp2_err = maximum(d2fspdvperp2_err) + println("max_d2fspdvperp2_err: ",max_d2fspdvperp2_err) + end function get_imin_imax(coord,iel) j = iel if j > 1 @@ -1000,7 +1065,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ finalize_comms!() end #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - results = maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err) + results = maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err) return results end @@ -1346,6 +1411,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) + max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + max_dfspdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) expected_label = L"(1/N_{el})^{n_g - 1}" @@ -1357,7 +1431,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan]) + max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], + max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], + max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], + max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan]) = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) end if global_rank[]==0 @@ -1385,6 +1463,35 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + + dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" + dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" + d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" + d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" + d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" + plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], + xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfspdvperp_label = L"\epsilon(d F_{s^\prime} /d v_{\perp})" + d2fspdvpa2_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\|\|}^2)" + d2fspdvperpdvpa_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\perp}d v_{\|\|})" + d2fspdvperp2_label = L"\epsilon(d^2 F_{s^\prime}/ d v_{\perp}^2)" + plot(nelement_list, [max_dfspdvperp_err,max_d2fspdvpa2_err,max_d2fspdvperpdvpa_err,max_d2fspdvperp2_err,expected], + xlabel=xlabel, label=[dfspdvperp_label d2fspdvpa2_label d2fspdvperpdvpa_label d2fspdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fsp_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) end finalize_comms!() end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 14351d09e..6ffc3fc06 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -13,7 +13,8 @@ export calculate_Rosenbluth_H_from_G! export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs -export F_Maxwellian +export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian using SpecialFunctions: ellipk, ellipe, erf using ..type_definitions: mk_float, mk_int From 57bac49a1de7af6ba96b3b51fccb561687b2e0e2 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 27 Jul 2023 18:24:50 +0100 Subject: [PATCH 069/331] Attempt to include boundary flux terms in the weak formulation of the Laplacian. Errors are larger when using the flux terms than they otherwise were. --- GaussLobattoLegendre_test.jl | 12 ++++--- src/calculus.jl | 13 +++++++- src/coordinates.jl | 11 +++++-- src/gauss_legendre.jl | 62 ++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 8 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 59ac05ddf..9c0903666 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -11,7 +11,7 @@ import moment_kinetics using moment_kinetics.gauss_legendre using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_derivative! if abspath(PROGRAM_FILE) == @__FILE__ using Pkg @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("max df_err: ",max_df_err) # elemental grid tests - ngrid = 3 - nelement = 100 + ngrid = 17 + nelement = 10 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -355,6 +355,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ mul!(b,y_spectral.S_matrix,g_exact) gausslegendre_mass_matrix_solve!(divg_num,b,y_spectral) @. divg_err = abs(divg_num - divg_exact) + #println("divg_b (weak form): ",b) #println("divg_num (weak form): ",divg_num) #println("divg_exact (weak form): ",divg_exact) println("max(divg_err) (weak form): ",maximum(divg_err)) @@ -371,8 +372,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "vperp_second_derivative_test.pdf" savefig(outfile) - mul!(b,y_spectral.L_matrix,h_exact) - gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) + #mul!(b,y_spectral.L_matrix,h_exact) + laplacian_derivative!(laph_num, h_exact, y, y_spectral) + #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* #println(b[1:10]) println(laph_num[1:10]) diff --git a/src/calculus.jl b/src/calculus.jl index fc546f582..31ce17442 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -2,7 +2,7 @@ """ module calculus -export derivative!, second_derivative! +export derivative!, second_derivative!, laplacian_derivative! export reconcile_element_boundaries_MPI! export integral @@ -10,6 +10,7 @@ export integral using ..chebyshev: chebyshev_info, chebyshev_derivative! using ..gauss_legendre: gausslegendre_info, gausslegendre_derivative! using ..gauss_legendre: gausslegendre_apply_Kmat!, gausslegendre_mass_matrix_solve! +using ..gauss_legendre: gausslegendre_apply_Lmat! using ..finite_differences: derivative_finite_difference! using ..type_definitions: mk_float, mk_int using MPI @@ -45,6 +46,16 @@ function second_derivative!(d2f, f, coord, spectral::gausslegendre_info) # solve weak form problem M * d2f = K * f gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) end + +function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) + # get the derivative at each grid point within each element and store in df + gausslegendre_apply_Lmat!(coord.scratch_2d, f, spectral, coord) + # map the derivative from the elemental grid to the full grid; + # at element boundaries, use the average of the derivatives from neighboring elements. + derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) + # solve weak form problem M * d2f = K * f + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) +end """ Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' """ diff --git a/src/coordinates.jl b/src/coordinates.jl index 737f3b776..38f941d97 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -88,6 +88,8 @@ struct coordinate local_io_range::UnitRange{Int64} # global range to write into in output file global_io_range::UnitRange{Int64} + # jacobian function (grid points) + jacobian::Array{mk_float,1} end """ @@ -130,7 +132,12 @@ function define_coordinate(input, parallel_io::Bool=false) # endpoints, so only two pieces of information must be shared send_buffer = allocate_float(1) receive_buffer = allocate_float(1) - + jacobian = allocate_float(n_local) + if input.name == "vperp" + @. jacobian = grid + else + @. jacobian = 1.0 + end # Add some ranges to support parallel file io if !parallel_io # No parallel io, just write everything @@ -153,7 +160,7 @@ function define_coordinate(input, parallel_io::Bool=false) 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), scratch_2d, copy(scratch_2d), advection, send_buffer, receive_buffer, input.comm, - local_io_range, global_io_range) + local_io_range, global_io_range,jacobian) end """ diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 1313160b3..ae64edaea 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -15,6 +15,7 @@ export scaled_gauss_legendre_lobatto_grid export scaled_gauss_legendre_radau_grid export gausslegendre_derivative! export gausslegendre_apply_Kmat! +export gausslegendre_apply_Lmat! export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral export GaussLegendre_weak_product_matrix! @@ -226,6 +227,67 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) return nothing end +""" +function for taking the weak-form Laplacian derivative on Gauss-Legendre points +""" +function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) + # define local variable nelement for convenience + nelement = coord.nelement_local + # check array bounds + @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) + + # variable k will be used to avoid double counting of overlapping point + k = 0 + j = 1 # the first element + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + #println(gausslegendre.Qmat) + @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) + zero_gradient_bc_lower_boundary = false#true + boundary_flux_terms = true#false + if coord.name == "vperp" && zero_gradient_bc_lower_boundary + # set the 1st point of the RHS vector to zero + # consistent with use with the mass matrix with D f = 0 boundary conditions + df[1,j] = 0.0 + end + # boundary flux terms + if boundary_flux_terms + if coord.name=="vperp" # only include a flux term from the upper boundary + @. coord.scratch[imin:imax] = gausslegendre.radau.Dmat[coord.ngrid,:]*ff[imin:imax] + df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) + else + @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[1,:]*ff[imin:imax] + df[1,j] -= coord.jacobian[imin]*sum(coord.scratch[imin:imax]) + @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[coord.ngrid,:]*ff[imin:imax] + df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) + end + end + # calculate the derivative on each element + @inbounds for j ∈ 2:nelement + k = 1 + imin = coord.imin[j]-k + # imax is the maximum index on the full grid for this (jth) element + imax = coord.imax[j] + #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) + get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + #println(gausslegendre.Qmat) + @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) + # boundary flux terms + if boundary_flux_terms + @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[1,:]*ff[imin:imax] + df[1,j] -= coord.jacobian[imin]*sum(coord.scratch[imin:imax]) + @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[coord.ngrid,:]*ff[imin:imax] + df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) + end + end + #for j in 1:nelement + # println(df[:,j]) + #end + return nothing +end + function gausslegendre_mass_matrix_solve!(f,b,spectral) y = spectral.mass_matrix_lu \ b @. f = y From d1c1089039458f6aac63e6cbd320ae80b68403d6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 28 Jul 2023 16:25:17 +0100 Subject: [PATCH 070/331] Example for how to integrate a 1/(square root y) divergent integrand on the domain y = 0 to 1 --- DivergingIntegrands_test.jl | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 DivergingIntegrands_test.jl diff --git a/DivergingIntegrands_test.jl b/DivergingIntegrands_test.jl new file mode 100644 index 000000000..269d59e73 --- /dev/null +++ b/DivergingIntegrands_test.jl @@ -0,0 +1,54 @@ +using FastGaussQuadrature +using Printf + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.3f ", matrix[i,j]) + end + println("") + end + println("\n") + end + + function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") + end + + # gauss lobatto test + ngrid = 100 + nelement = 1 + x, w = gausslaguerre(ngrid) + print_vector(x,"Gauss Laguerre x",ngrid) + print_vector(w,"Gauss Laguerre w",ngrid) + + # integrate 1/sqrt(y) from y = 0 to 1 + # use the Gauss-Laguerre quadrature to + # convert the diverging to a converging integrand + integrand = Array{Float64,1}(undef,ngrid) + for i in 1:ngrid + # change of variables + y = exp(-x[i]) + # function to integrate in terms of y + integrand[i] = sqrt(1.0/y) + end + @. integrand *= w + + print_vector(integrand,"Gauss Laguerre integrand",ngrid) + + primitive = sum(integrand) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + +end \ No newline at end of file From c4b2a64af5af56d729fa78746a79749bdb27f9de Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 29 Jul 2023 18:58:27 +0100 Subject: [PATCH 071/331] Test that sqrt(y) integrands are as well integrated as 1/sqrt(y) integrands with the Gauss Laguerre scheme. --- DivergingIntegrands_test.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/DivergingIntegrands_test.jl b/DivergingIntegrands_test.jl index 269d59e73..4a67030c4 100644 --- a/DivergingIntegrands_test.jl +++ b/DivergingIntegrands_test.jl @@ -26,7 +26,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # gauss lobatto test - ngrid = 100 + ngrid = 32 nelement = 1 x, w = gausslaguerre(ngrid) print_vector(x,"Gauss Laguerre x",ngrid) @@ -36,13 +36,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ # use the Gauss-Laguerre quadrature to # convert the diverging to a converging integrand integrand = Array{Float64,1}(undef,ngrid) + integrand_sqrty = Array{Float64,1}(undef,ngrid) for i in 1:ngrid # change of variables y = exp(-x[i]) # function to integrate in terms of y - integrand[i] = sqrt(1.0/y) + integrand[i] = sqrt(1.0/y)*w[i] + integrand_sqrty[i] = sqrt(y)*w[i] end - @. integrand *= w + #@. integrand *= w print_vector(integrand,"Gauss Laguerre integrand",ngrid) @@ -51,4 +53,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ primitive_err = abs(primitive - primitive_exact) println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + primitive = sum(integrand_sqrty) + primitive_exact = 2.0/3.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + end \ No newline at end of file From e4c6482ee56c551385551529f9635efe025b85dc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 30 Jul 2023 09:33:55 +0100 Subject: [PATCH 072/331] First attempt to use a different quadrature in elements where the integrand diverges because mm = 1.0. Gauss-Laguerre points are used with a change of variables, and a min(x,y) function is used to prevent mm >= 1.0 for points in the Gauss-Laguerre quadrature which generate mm very close to 1.0 --- fkpl_test.jl | 53 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 721503f3b..fd2f9fcf2 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -682,11 +682,40 @@ if abspath(PROGRAM_FILE) == @__FILE__ return poly end - function get_scaled_x_w!(x_scaled, w_scaled, x, w, node_min, node_max) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled = scale*x + shift - @. w_scaled = scale*w + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, xhalf_laguerre, whalf_laguerre, node_min, node_max, coord_val) + zero = 1.0e-6 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero # divergence at upper endpoint + @. x_scaled = node_max + (node_min - node_max)*exp(-x_laguerre) + @. w_scaled = (node_max - node_min)*w_laguerre + #println("upper divergence") + elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + @. x_scaled = node_min + (node_max - node_min)*exp(-x_laguerre) + @. w_scaled = (node_max - node_min)*w_laguerre + #println("lower divergence") + elseif (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + n = size(x_scaled,1) + nhalf = floor(mk_int,n/2) + # lower half of domain + for j in 1:nhalf + x_scaled[j] = coord_val + (node_min - coord_val)*exp(-xhalf_laguerre[j]) + w_scaled[j] = (coord_val - node_min)*whalf_laguerre[j] + end + # upper half of domain + for j in 1:nhalf + x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-xhalf_laguerre[j]) + w_scaled[n+1-j] = (node_max - coord_val)*whalf_laguerre[j] + end + #println("intermediate divergence") + else # no divergences + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled = scale*x_legendre + shift + @. w_scaled = scale*w_legendre + #println("no divergence") + end + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) return nothing end @@ -696,8 +725,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # get Gauss-Legendre points and weights on (-1,1) - nquad = 2*ngrid - x, w = gausslegendre(nquad) + nquad = 4*ngrid + halfnquad = floor(mk_int,nquad/2) + x_legendre, w_legendre = gausslegendre(nquad) + x_laguerre, w_laguerre = gausslaguerre(nquad) + x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) @@ -728,14 +760,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ else # adjust for the Gauss-Radau element vperp_min = 0.0 end - get_scaled_x_w!(x_vperp, w_vperp, x, w, vperp_min, vperp_max) + get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, x_hlaguerre, w_hlaguerre, vperp_min, vperp_max, vperp_val) for ielement_vpa in 1:vpa.nelement_local vpa_nodes = get_nodes(vpa,ielement_vpa) # assumme Gauss-Lobatto elements vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - get_scaled_x_w!(x_vpa, w_vpa, x, w, vpa_min, vpa_max) + get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, x_hlaguerre, w_hlaguerre, vpa_min, vpa_max, vpa_val) for igrid_vperp in 1:vperp.ngrid for igrid_vpa in 1:vpa.ngrid @@ -750,10 +782,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = 4.0*vperp_val*x_kvperp/denom + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) prefac = sqrt(denom) ellipe_mm = ellipe(mm) ellipk_mm = ellipk(mm) + #println("mm: ",mm," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) From 82c0f5c54bf66e5eb6fcaf61de6c35b7f4c30673 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 31 Jul 2023 12:06:06 +0100 Subject: [PATCH 073/331] Changed the quadrature scheme to only use 4*ngrid points for elements with an interior divergence in the integrand. Added calculation of dHdvpa dHdvperp by direct integration. Start of experiment making d2Gdcoord2 with only first derivatives of F. --- fkpl_test.jl | 106 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 31 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index fd2f9fcf2..448ca05ee 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -451,13 +451,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) @@ -485,8 +488,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hsp = allocate_shared_float(nvpa,nvperp) Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvperp_from_Gsp = allocate_shared_float(nvpa,nvperp) dHspdvpa = allocate_shared_float(nvpa,nvperp) dHspdvperp = allocate_shared_float(nvpa,nvperp) #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) @@ -542,6 +549,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfspdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) @@ -575,6 +583,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2fsdvpa2[:,ivperp] = vpa.scratch2 # sp @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) + @. dfspdvpa[:,ivperp] = vpa.scratch @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) @. d2fspdvpa2[:,ivperp] = vpa.scratch2 end @@ -628,6 +637,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) + @. dfspdvpa_err = abs(dfspdvpa - dfspdvpa_Maxwell) + max_dfspdvpa_err = maximum(dfspdvpa_err) @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) @@ -682,41 +693,50 @@ if abspath(PROGRAM_FILE) == @__FILE__ return poly end - function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, xhalf_laguerre, whalf_laguerre, node_min, node_max, coord_val) + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) zero = 1.0e-6 + nquad = size(x_legendre,1) + @. x_scaled = 0.0 + @. w_scaled = 0.0 + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint - @. x_scaled = node_max + (node_min - node_max)*exp(-x_laguerre) - @. w_scaled = (node_max - node_min)*w_laguerre + @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) + @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre + nquad_coord = nquad #println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint - @. x_scaled = node_min + (node_max - node_min)*exp(-x_laguerre) - @. w_scaled = (node_max - node_min)*w_laguerre + @. x_scaled[1:nquad] = node_min + (node_max - node_min)*exp(-x_laguerre) + @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre + nquad_coord = nquad #println("lower divergence") elseif (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence n = size(x_scaled,1) - nhalf = floor(mk_int,n/2) # lower half of domain - for j in 1:nhalf - x_scaled[j] = coord_val + (node_min - coord_val)*exp(-xhalf_laguerre[j]) - w_scaled[j] = (coord_val - node_min)*whalf_laguerre[j] + for j in 1:nquad + x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) + w_scaled[j] = (coord_val - node_min)*w_laguerre[j] end # upper half of domain - for j in 1:nhalf - x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-xhalf_laguerre[j]) - w_scaled[n+1-j] = (node_max - coord_val)*whalf_laguerre[j] + for j in 1:nquad + x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) + w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] end + nquad_coord = 2*nquad #println("intermediate divergence") else # no divergences shift = 0.5*(node_min + node_max) scale = 0.5*(node_max - node_min) - @. x_scaled = scale*x_legendre + shift - @. w_scaled = scale*w_legendre + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre #println("no divergence") + nquad_coord = nquad end #println("x_scaled",x_scaled) #println("w_scaled",w_scaled) - return nothing + return nquad_coord end @@ -725,13 +745,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # get Gauss-Legendre points and weights on (-1,1) - nquad = 4*ngrid + nquad = 2*ngrid halfnquad = floor(mk_int,nquad/2) x_legendre, w_legendre = gausslegendre(nquad) x_laguerre, w_laguerre = gausslaguerre(nquad) - x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) @serial_region begin @@ -750,6 +770,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G2_weights[ivpa,ivperp,:,:] = 0.0 @. G3_weights[ivpa,ivperp,:,:] = 0.0 @. H_weights[ivpa,ivperp,:,:] = 0.0 + @. H1_weights[ivpa,ivperp,:,:] = 0.0 + @. H2_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate for ielement_vperp in 1:vperp.nelement_local @@ -760,14 +782,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ else # adjust for the Gauss-Radau element vperp_min = 0.0 end - get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, x_hlaguerre, w_hlaguerre, vperp_min, vperp_max, vperp_val) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) for ielement_vpa in 1:vpa.nelement_local vpa_nodes = get_nodes(vpa,ielement_vpa) # assumme Gauss-Lobatto elements vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, x_hlaguerre, w_hlaguerre, vpa_min, vpa_max, vpa_val) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) for igrid_vperp in 1:vperp.ngrid for igrid_vpa in 1:vpa.ngrid @@ -775,8 +797,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad - for kvpa in 1:nquad + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa x_kvpa = x_vpa[kvpa] x_kvperp = x_vperp[kvperp] w_kvperp = w_vperp[kvperp] @@ -792,6 +814,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/mm^2) - (4.0*(mm - 8.0)*ellipe_mm/(3.0*mm^2)) ) lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) @@ -814,6 +838,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ (H_weights[ivpa,ivperp,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end end end @@ -841,14 +874,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gspdvperp2[ivpa,ivperp] = 0.0 Gsp[ivpa,ivperp] = 0.0 Hsp[ivpa,ivperp] = 0.0 + dHspdvpa[ivpa,ivperp] = 0.0 + dHspdvperp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] end end #end @@ -861,12 +899,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_vperp_region() @loop_vperp ivperp begin @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) - @. dHspdvpa[:,ivperp] = vpa.scratch + @. dHspdvpa_from_Gsp[:,ivperp] = vpa.scratch end begin_vpa_region() @loop_vpa ivpa begin @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) - @. dHspdvperp[ivpa,:] = vperp.scratch + @. dHspdvperp_from_Gsp[ivpa,:] = vperp.scratch end # evaluate collsion operator @@ -935,12 +973,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) max_Cflux_vperp_err = maximum(Cflux_vperp_err) println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) - @. H_err = abs(Hsp - H_Maxwell) - max_H_err = maximum(H_err) - println("max_H_err: ",max_H_err) @. H_err = abs(Hsp_from_Gsp - H_Maxwell) max_H_err = maximum(H_err) println("max_H_from_G_err: ",max_H_err) + @. dHdvperp_err = abs(dHspdvperp_from_Gsp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err (from G): ",max_dHdvperp_err) + @. dHdvpa_err = abs(dHspdvpa_from_Gsp - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err (from G): ",max_dHdvpa_err) + @. H_err = abs(Hsp - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_err: ",max_H_err) @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) max_dHdvperp_err = maximum(dHdvperp_err) println("max_dHdvperp_err: ",max_dHdvperp_err) @@ -1428,11 +1472,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 5 - nelement_list = Int[2, 4, 8, 16, 32] + nscan = 1 + #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4] - #nelement_list = Int[2] + nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From f7484da5d9d1e2de13425ec01ced48112d4818bb Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 31 Jul 2023 12:35:51 +0100 Subject: [PATCH 074/331] Correction of calculation of d2Gdvperp2 by new -H- method with divergent integrand. --- fkpl_test.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 448ca05ee..24442254e 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -815,7 +815,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/mm^2) - (4.0*(mm - 8.0)*ellipe_mm/(3.0*mm^2)) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) @@ -881,8 +881,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] From 5828f57f5a8d4f4f2606a4c29126316c54b686a6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 31 Jul 2023 13:32:52 +0100 Subject: [PATCH 075/331] Calculation of d2Gdvpa2 by new -H- method with divergent integrand. --- fkpl_test.jl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 24442254e..0f1be0b1b 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -490,6 +490,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) Hsp = allocate_shared_float(nvpa,nvperp) Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) @@ -772,6 +773,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H_weights[ivpa,ivperp,:,:] = 0.0 @. H1_weights[ivpa,ivperp,:,:] = 0.0 @. H2_weights[ivpa,ivperp,:,:] = 0.0 + @. H3_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate for ielement_vperp in 1:vperp.nelement_local @@ -847,6 +849,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ lagrange_poly_vpa*lagrange_poly_vperp* (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end end end @@ -878,7 +884,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHspdvperp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa - d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] @@ -1472,11 +1479,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 1 + nscan = 4 #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4] - nelement_list = Int[2] + #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From 7881185c7aa20d53bb88e0372a85eac50b1dede1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 31 Jul 2023 16:59:18 +0100 Subject: [PATCH 076/331] An attempt to optimise divergent integrand calculation by removing if statement on mm -- reduce number of Laguerre points and divide mm by a number close to machine precision to ensure that it is always numerically below 1. --- fkpl_test.jl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 0f1be0b1b..6c54848af 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -696,7 +696,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) zero = 1.0e-6 - nquad = size(x_legendre,1) @. x_scaled = 0.0 @. w_scaled = 0.0 # assume x_scaled, w_scaled are arrays of length 2*nquad @@ -704,17 +703,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint + nquad = size(x_laguerre,1) @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre nquad_coord = nquad #println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + nquad = size(x_laguerre,1) @. x_scaled[1:nquad] = node_min + (node_max - node_min)*exp(-x_laguerre) @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre nquad_coord = nquad #println("lower divergence") elseif (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - n = size(x_scaled,1) + nquad = size(x_laguerre,1) + n = 2*nquad # lower half of domain for j in 1:nquad x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) @@ -725,9 +727,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] end - nquad_coord = 2*nquad + nquad_coord = n #println("intermediate divergence") else # no divergences + nquad = size(x_legendre,1) shift = 0.5*(node_min + node_max) scale = 0.5*(node_max - node_min) @. x_scaled[1:nquad] = scale*x_legendre + shift @@ -747,9 +750,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ # get Gauss-Legendre points and weights on (-1,1) nquad = 2*ngrid - halfnquad = floor(mk_int,nquad/2) x_legendre, w_legendre = gausslegendre(nquad) - x_laguerre, w_laguerre = gausslaguerre(nquad) + nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) @@ -806,11 +810,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) + #mm = 4.0*vperp_val*x_kvperp/denom prefac = sqrt(denom) ellipe_mm = ellipe(mm) ellipk_mm = ellipk(mm) - #println("mm: ",mm," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) @@ -1482,7 +1490,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ nscan = 4 #nelement_list = Int[2, 4, 8, 16, 32] nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4] + #nelement_list = Int[2, 4, 8] #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) From 1a91b7752c812f59ee6dfca21b8f708264c444b5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 10:41:56 +0100 Subject: [PATCH 077/331] Refactor integration script to use a function for the lowest level element integration --- fkpl_test.jl | 227 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 155 insertions(+), 72 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 6c54848af..61e15b142 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -743,6 +743,82 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nquad_coord end + function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpa,vpa_nodes, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + #mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) + #mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing + end @serial_region begin println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) @@ -751,7 +827,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # get Gauss-Legendre points and weights on (-1,1) nquad = 2*ngrid x_legendre, w_legendre = gausslegendre(nquad) - nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad x_laguerre, w_laguerre = gausslaguerre(nlaguerre) #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) @@ -797,74 +874,80 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) - for igrid_vperp in 1:vperp.ngrid - for igrid_vpa in 1:vpa.ngrid - # get grid index for point on full grid - ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad_vperp - for kvpa in 1:nquad_vpa - x_kvpa = x_vpa[kvpa] - x_kvperp = x_vperp[kvperp] - w_kvperp = w_vperp[kvperp] - w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - #mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) - #mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) - #if mm_test > 1.0 - # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - #end - G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - - (G_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G1_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G2_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G3_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H1_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H2_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*(vpa_val - x_kvpa)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - end - end - end - end + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpa,vpa_nodes, + nquad_vperp,ielement_vperp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + # # # # # #for igrid_vperp in 1:vperp.ngrid + # # # # # # #for igrid_vpa in 1:vpa.ngrid + # # # # # # # ## get grid index for point on full grid + # # # # # # # #ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + # # # # # # # #ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # # # # # # # ## carry out integration over Lagrange polynomial at this node, on this element + # # # # # # # #for kvperp in 1:nquad_vperp + # # # # # # # # #for kvpa in 1:nquad_vpa + # # # # # # # # # #x_kvpa = x_vpa[kvpa] + # # # # # # # # # #x_kvperp = x_vperp[kvperp] + # # # # # # # # # #w_kvperp = w_vperp[kvperp] + # # # # # # # # # #w_kvpa = w_vpa[kvpa] + # # # # # # # # # #denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + # # # # # # # # # ##mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + # # # # # # # # # #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) + # # # # # # # # # ##mm = 4.0*vperp_val*x_kvperp/denom + # # # # # # # # # #prefac = sqrt(denom) + # # # # # # # # # #ellipe_mm = ellipe(mm) + # # # # # # # # # #ellipk_mm = ellipk(mm) + # # # # # # # # # ##if mm_test > 1.0 + # # # # # # # # # ## #println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + # # # # # # # # # ##end + # # # # # # # # # #G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + # # # # # # # # # #G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + # # # # # # # # # #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + # # # # # # # # # #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + # # # # # # # # # #H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + # # # # # # # # # #H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + # # # # # # # # # #H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + # # # # # # # # # #lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + # # # # # # # # # #lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + # # # # # # # # # # + # # # # # # # # # #(G_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # + # # # # # # # # # #(G1_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # + # # # # # # # # # #(G2_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # + # # # # # # # # # #(G3_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # + # # # # # # # # # #(H_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # # + # # # # # # # # # #(H1_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # # # + # # # # # # # # # #(H2_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #(H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + # # # # # # # # # # #x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # # #(H3_weights[ivpa,ivperp,ivpap,ivperpp] += + # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* + # # # # # # # # # # #H_elliptic_integral_factor*(vpa_val - x_kvpa)* + # # # # # # # # # # #x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + # # # # # # # # #end + # # # # # # # #end + # # # # # # #end + # # # # # #end end end #end @@ -1487,10 +1570,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 4 + nscan = 3 #nelement_list = Int[2, 4, 8, 16, 32] - nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] + #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8] #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) From e86039a008ea4828c1ec5c2b3f16de5626843b7f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 10:42:25 +0100 Subject: [PATCH 078/331] Remove comments. --- fkpl_test.jl | 68 ---------------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 61e15b142..3a5d8d10f 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -880,74 +880,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_vperp,ielement_vperp,vperp_nodes, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) - # # # # # #for igrid_vperp in 1:vperp.ngrid - # # # # # # #for igrid_vpa in 1:vpa.ngrid - # # # # # # # ## get grid index for point on full grid - # # # # # # # #ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - # # # # # # # #ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - # # # # # # # ## carry out integration over Lagrange polynomial at this node, on this element - # # # # # # # #for kvperp in 1:nquad_vperp - # # # # # # # # #for kvpa in 1:nquad_vpa - # # # # # # # # # #x_kvpa = x_vpa[kvpa] - # # # # # # # # # #x_kvperp = x_vperp[kvperp] - # # # # # # # # # #w_kvperp = w_vperp[kvperp] - # # # # # # # # # #w_kvpa = w_vpa[kvpa] - # # # # # # # # # #denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - # # # # # # # # # ##mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - # # # # # # # # # #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) - # # # # # # # # # ##mm = 4.0*vperp_val*x_kvperp/denom - # # # # # # # # # #prefac = sqrt(denom) - # # # # # # # # # #ellipe_mm = ellipe(mm) - # # # # # # # # # #ellipk_mm = ellipk(mm) - # # # # # # # # # ##if mm_test > 1.0 - # # # # # # # # # ## #println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - # # # # # # # # # ##end - # # # # # # # # # #G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - # # # # # # # # # #G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - # # # # # # # # # #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - # # # # # # # # # #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - # # # # # # # # # #H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - # # # # # # # # # #H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - # # # # # # # # # #H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - # # # # # # # # # #lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - # # # # # # # # # #lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - # # # # # # # # # # - # # # # # # # # # #(G_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # - # # # # # # # # # #(G1_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # - # # # # # # # # # #(G2_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # - # # # # # # # # # #(G3_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # - # # # # # # # # # #(H_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # # - # # # # # # # # # #(H1_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # # # - # # # # # # # # # #(H2_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #(H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - # # # # # # # # # # #x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # # #(H3_weights[ivpa,ivperp,ivpap,ivperpp] += - # # # # # # # # # # #lagrange_poly_vpa*lagrange_poly_vperp* - # # # # # # # # # # #H_elliptic_integral_factor*(vpa_val - x_kvpa)* - # # # # # # # # # # #x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - # # # # # # # # #end - # # # # # # # #end - # # # # # # #end - # # # # # #end end end #end From 47edc0203f3f16c23957630bdc63dd47470260db Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:00:51 +0100 Subject: [PATCH 079/331] Refactor to reduce number of if statements in integrating over vpa domain by using knowledge of the location of the divergences -- should be faster when scaled up. --- fkpl_test.jl | 101 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 3a5d8d10f..d7094bc90 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -743,6 +743,38 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nquad_coord end + function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) + zero = 1.0e-6 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + nquad = size(x_legendre,1) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad + end + + # function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid + function ng_low(igrid,ngrid) + return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) + end + # function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid + function ng_hi(igrid,ngrid) + return floor(mk_int, igrid/ngrid) + end + # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 + function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) + end + # function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement + function nel_hi(ielement,nelement) + return 1- floor(mk_int, ielement/nelement) + end + function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpa,vpa_nodes, # info about primed vperp grids @@ -840,11 +872,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid # precalculated weights, integrating over Lagrange polynomials begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin #for ivperp in 1:nvperp # for ivpa in 1:nvpa + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + #limits where checks required to determine which divergence-safe grid is needed + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println(igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @. G_weights[ivpa,ivperp,:,:] = 0.0 @@ -856,30 +899,54 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H2_weights[ivpa,ivperp,:,:] = 0.0 @. H3_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - for ielement_vperp in 1:vperp.nelement_local + for ielement_vperpp in 1:vperp.nelement_local - vperp_nodes = get_nodes(vperp,ielement_vperp) + vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] - if ielement_vperp > 1 # Gauss-Lobatto - vperp_min = vperp_nodes[1] - else # adjust for the Gauss-Radau element - vperp_min = 0.0 - end + #if ielement_vperpp > 1 # Gauss-Lobatto + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + #else # adjust for the Gauss-Radau element + # vperp_min = 0.0 + #end nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) - for ielement_vpa in 1:vpa.nelement_local - - vpa_nodes = get_nodes(vpa,ielement_vpa) - # assumme Gauss-Lobatto elements + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpa,vpa_nodes, - nquad_vperp,ielement_vperp,vperp_nodes, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) + end end #end @@ -1502,11 +1569,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 3 + nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - nelement_list = Int[2, 4, 8] - #nelement_list = Int[2] + #nelement_list = Int[2, 4, 8] + nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From 909d494f365297f21c736da8b4e64712019bdaee Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:10:59 +0100 Subject: [PATCH 080/331] Refactor loop over vpa elements into a callable function. --- fkpl_test.jl | 122 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index d7094bc90..d6c328af1 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -852,6 +852,53 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes, + nquad_vperp,ielement_vperpp,vperp_nodes, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + @serial_region begin println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -910,44 +957,51 @@ if abspath(PROGRAM_FILE) == @__FILE__ #end nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) - for ielement_vpap in 1:ielement_vpa_low-1 - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + + #for ielement_vpap in 1:ielement_vpa_low-1 + # # do integration over part of the domain with no divergences + # vpa_nodes = get_nodes(vpa,ielement_vpap) + # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + # nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + # H_weights,H1_weights,H2_weights,H3_weights, + # nquad_vpa,ielement_vpap,vpa_nodes, + # nquad_vperp,ielement_vperpp,vperp_nodes, + # x_vpa, w_vpa, x_vperp, w_vperp, + # vpa_val, vperp_val, ivpa, ivperp) + #end + #for ielement_vpap in ielement_vpa_low:ielement_vpa_hi #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # vpa_nodes = get_nodes(vpa,ielement_vpap) + # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + # nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + # H_weights,H1_weights,H2_weights,H3_weights, + # nquad_vpa,ielement_vpap,vpa_nodes, + # nquad_vperp,ielement_vperpp,vperp_nodes, + # x_vpa, w_vpa, x_vperp, w_vperp, + # vpa_val, vperp_val, ivpa, ivperp) + #end + #for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) + # vpa_nodes = get_nodes(vpa,ielement_vpap) + # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + # nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + # H_weights,H1_weights,H2_weights,H3_weights, + # nquad_vpa,ielement_vpap,vpa_nodes, + # nquad_vperp,ielement_vperpp,vperp_nodes, + # x_vpa, w_vpa, x_vperp, w_vperp, + # vpa_val, vperp_val, ivpa, ivperp) - end + #end end #end end From 964f9921e2872477ad1716c53ef2a4150d499a60 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:11:34 +0100 Subject: [PATCH 081/331] Remove commented out code. --- fkpl_test.jl | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index d6c328af1..e72fcf22b 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -963,45 +963,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) - - #for ielement_vpap in 1:ielement_vpa_low-1 - # # do integration over part of the domain with no divergences - # vpa_nodes = get_nodes(vpa,ielement_vpap) - # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - # nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - # H_weights,H1_weights,H2_weights,H3_weights, - # nquad_vpa,ielement_vpap,vpa_nodes, - # nquad_vperp,ielement_vperpp,vperp_nodes, - # x_vpa, w_vpa, x_vperp, w_vperp, - # vpa_val, vperp_val, ivpa, ivperp) - #end - #for ielement_vpap in ielement_vpa_low:ielement_vpa_hi - #for ielement_vpap in 1:vpa.nelement_local - # use general grid function that checks divergences - # vpa_nodes = get_nodes(vpa,ielement_vpap) - # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - # nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) - # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - # H_weights,H1_weights,H2_weights,H3_weights, - # nquad_vpa,ielement_vpap,vpa_nodes, - # nquad_vperp,ielement_vperpp,vperp_nodes, - # x_vpa, w_vpa, x_vperp, w_vperp, - # vpa_val, vperp_val, ivpa, ivperp) - #end - #for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local - # do integration over part of the domain with no divergences - # vpa_nodes = get_nodes(vpa,ielement_vpap) - # vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - # nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - # local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - # H_weights,H1_weights,H2_weights,H3_weights, - # nquad_vpa,ielement_vpap,vpa_nodes, - # nquad_vperp,ielement_vperpp,vperp_nodes, - # x_vpa, w_vpa, x_vperp, w_vperp, - # vpa_val, vperp_val, ivpa, ivperp) - - #end end #end end From 64a30c94d22cc52bc0e4607d82bfa69ade7f8cc3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:12:06 +0100 Subject: [PATCH 082/331] Removed commented out code. --- fkpl_test.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index e72fcf22b..3a1734003 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -950,11 +950,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] - #if ielement_vperpp > 1 # Gauss-Lobatto - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - #else # adjust for the Gauss-Radau element - # vperp_min = 0.0 - #end + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, From 2b0ce94ce075534bf63ef8d83dc067de10745d2c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:18:24 +0100 Subject: [PATCH 083/331] Refactor to avoid if statements in vperp quadrature construction where there will not be divergences in the integrand. --- fkpl_test.jl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 3a1734003..286a414a8 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -934,6 +934,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + println(igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @@ -946,16 +947,41 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H2_weights[ivpa,ivperp,:,:] = 0.0 @. H3_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - for ielement_vperpp in 1:vperp.nelement_local + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) From 54b530d4db415b4797773b2883949d737a9df29a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:23:20 +0100 Subject: [PATCH 084/331] Refactor vperp loops into callabale function. --- fkpl_test.jl | 132 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 39 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 286a414a8..4578679ef 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -899,6 +899,54 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + @serial_region begin println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -947,45 +995,51 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H2_weights[ivpa,ivperp,:,:] = 0.0 @. H3_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - for ielement_vperpp in 1:ielement_vperp_low-1 - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end + loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + # # # #for ielement_vperpp in 1:ielement_vperp_low-1 + # # # # # + # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) + # # # # #vperp_max = vperp_nodes[end] + # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + # # # # #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, + # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + # # # # # # #vpa_val, vperp_val, ivpa, ivperp) + # # # #end + # # # #for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + # # # # # + # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) + # # # # #vperp_max = vperp_nodes[end] + # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + # # # # #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, + # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + # # # # # # #vpa_val, vperp_val, ivpa, ivperp) + # # # #end + # # # #for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + # # # # # + # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) + # # # # #vperp_max = vperp_nodes[end] + # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + # # # # #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, + # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + # # # # # # #vpa_val, vperp_val, ivpa, ivperp) + # # # #end #end end From 29c997945c7c6dd1a3cc49bf9b167e94d851a87e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 1 Aug 2023 12:26:12 +0100 Subject: [PATCH 085/331] Clean up comments. --- fkpl_test.jl | 96 +++++++++++++++------------------------------------- 1 file changed, 27 insertions(+), 69 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 4578679ef..5d9e1c6a9 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -972,75 +972,33 @@ if abspath(PROGRAM_FILE) == @__FILE__ # precalculated weights, integrating over Lagrange polynomials begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin - #for ivperp in 1:nvperp - # for ivpa in 1:nvpa - igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] - #limits where checks required to determine which divergence-safe grid is needed - ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) - ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println(igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) - igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] - ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) - ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - println(igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - @. G_weights[ivpa,ivperp,:,:] = 0.0 - @. G1_weights[ivpa,ivperp,:,:] = 0.0 - @. G2_weights[ivpa,ivperp,:,:] = 0.0 - @. G3_weights[ivpa,ivperp,:,:] = 0.0 - @. H_weights[ivpa,ivperp,:,:] = 0.0 - @. H1_weights[ivpa,ivperp,:,:] = 0.0 - @. H2_weights[ivpa,ivperp,:,:] = 0.0 - @. H3_weights[ivpa,ivperp,:,:] = 0.0 - # loop over elements and grid points within elements on primed coordinate - loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - # # # #for ielement_vperpp in 1:ielement_vperp_low-1 - # # # # # - # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) - # # # # #vperp_max = vperp_nodes[end] - # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - # # # # #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, - # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - # # # # # # #vpa_val, vperp_val, ivpa, ivperp) - # # # #end - # # # #for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - # # # # # - # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) - # # # # #vperp_max = vperp_nodes[end] - # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - # # # # #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) - # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, - # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - # # # # # # #vpa_val, vperp_val, ivpa, ivperp) - # # # #end - # # # #for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local - # # # # # - # # # # #vperp_nodes = get_nodes(vperp,ielement_vperpp) - # # # # #vperp_max = vperp_nodes[end] - # # # # #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - # # # # #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - # # # # #loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - # # # # # # #H_weights,H1_weights,H2_weights,H3_weights, - # # # # # # #vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - # # # # # # #nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids - # # # # # # #x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - # # # # # # #vpa_val, vperp_val, ivpa, ivperp) - # # # #end - #end + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println(igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println(igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + @. G1_weights[ivpa,ivperp,:,:] = 0.0 + @. G2_weights[ivpa,ivperp,:,:] = 0.0 + @. G3_weights[ivpa,ivperp,:,:] = 0.0 + @. H_weights[ivpa,ivperp,:,:] = 0.0 + @. H1_weights[ivpa,ivperp,:,:] = 0.0 + @. H2_weights[ivpa,ivperp,:,:] = 0.0 + @. H3_weights[ivpa,ivperp,:,:] = 0.0 + # loop over elements and grid points within elements on primed coordinate + loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) end #_block_synchronize() From 87d6fee926fb4bff12b87d0e0027c90d642058b8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 2 Aug 2023 10:18:35 +0100 Subject: [PATCH 086/331] Create more plots with less clutter. --- fkpl_test.jl | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 5d9e1c6a9..7a8363c41 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -1617,12 +1617,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 5 - nscan = 1 + ngrid = 9 + nscan = 4 #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - nelement_list = Int[2] + #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) @@ -1686,6 +1686,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err, expected], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" From dfcecd06c4fdac834fb7ac8dfa3dcf1ac3a0dcb3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 2 Aug 2023 11:23:19 +0100 Subject: [PATCH 087/331] Reduce size of and comment out unused code to reduce complexity of if statement block. --- fkpl_test.jl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 7a8363c41..ef80ae3dc 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -695,7 +695,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) - zero = 1.0e-6 + zero = 1.0e-10 @. x_scaled = 0.0 @. w_scaled = 0.0 # assume x_scaled, w_scaled are arrays of length 2*nquad @@ -714,7 +714,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre nquad_coord = nquad #println("lower divergence") - elseif (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence nquad = size(x_laguerre,1) n = 2*nquad # lower half of domain @@ -729,14 +729,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ end nquad_coord = n #println("intermediate divergence") - else # no divergences - nquad = size(x_legendre,1) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled[1:nquad] = scale*x_legendre + shift - @. w_scaled[1:nquad] = scale*w_legendre - #println("no divergence") - nquad_coord = nquad + #else # no divergences + # nquad = size(x_legendre,1) + # shift = 0.5*(node_min + node_max) + # scale = 0.5*(node_max - node_min) + # @. x_scaled[1:nquad] = scale*x_legendre + shift + # @. w_scaled[1:nquad] = scale*w_legendre + # #println("no divergence") + # nquad_coord = nquad end #println("x_scaled",x_scaled) #println("w_scaled",w_scaled) @@ -1618,10 +1618,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 9 - nscan = 4 - #nelement_list = Int[2, 4, 8, 16, 32] - nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] + nscan = 3 + nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8] #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) From 3575af949e9c823946db7bcaa1827be20b815cee Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 2 Aug 2023 13:43:03 +0100 Subject: [PATCH 088/331] Integration of K(z) from z = 0 to 1. --- DivergingIntegrands_test.jl | 74 ++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/DivergingIntegrands_test.jl b/DivergingIntegrands_test.jl index 4a67030c4..a3d872ec1 100644 --- a/DivergingIntegrands_test.jl +++ b/DivergingIntegrands_test.jl @@ -1,4 +1,5 @@ using FastGaussQuadrature +using SpecialFunctions: ellipk using Printf if abspath(PROGRAM_FILE) == @__FILE__ @@ -25,8 +26,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("\n") end - # gauss lobatto test - ngrid = 32 + # gauss laguerre test + ngrid = 10 nelement = 1 x, w = gausslaguerre(ngrid) print_vector(x,"Gauss Laguerre x",ngrid) @@ -37,12 +38,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ # convert the diverging to a converging integrand integrand = Array{Float64,1}(undef,ngrid) integrand_sqrty = Array{Float64,1}(undef,ngrid) + integrand_Kz = Array{Float64,1}(undef,ngrid) + value_Kz = Array{Float64,1}(undef,ngrid) + value_y = Array{Float64,1}(undef,ngrid) + value_z = Array{Float64,1}(undef,ngrid) + L = 1.0 for i in 1:ngrid # change of variables - y = exp(-x[i]) + y = exp(-x[i]/L) # function to integrate in terms of y integrand[i] = sqrt(1.0/y)*w[i] integrand_sqrty[i] = sqrt(y)*w[i] + z = (1.0 - y)/(1.0 + 10^-13) + integrand_Kz[i] = ellipk(z)*w[i] + value_Kz[i] = ellipk(z) + value_z[i] = z end #@. integrand *= w @@ -51,11 +61,65 @@ if abspath(PROGRAM_FILE) == @__FILE__ primitive = sum(integrand) primitive_exact = 2.0 primitive_err = abs(primitive - primitive_exact) - println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + println("1/sqrt(y): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) primitive = sum(integrand_sqrty) primitive_exact = 2.0/3.0 primitive_err = abs(primitive - primitive_exact) - println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + println("sqrt(y): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + print_vector(integrand_Kz,"Kz integrand",ngrid) + print_vector(value_Kz,"Kz",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("K(z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + if false + # gauss lobatto test + ngrid = 10 + nelement = 1 + x, w = gausslegendre(ngrid) + print_vector(x,"Gauss Legendre x",ngrid) + print_vector(w,"Gauss Legendre w",ngrid) + + b = 1.0 # upper limit on element + a = 0.0 # lower limit on element + U = 100.0 # proxy for infinity + L = 1.0 # scale factor + z = copy(x) + y = copy(x) + wy =copy(x) + scale = U*(b-a)/L + @. z = U*(x + 1) + @. y = exp(-z/L) + @. wy = w*exp(-z/L)*scale + # integrate 1/sqrt(y) from y = 0 to 1 + # use the Gauss-Laguerre quadrature to + # convert the diverging to a converging integrand + integrand = Array{Float64,1}(undef,ngrid) + integrand_sqrty = Array{Float64,1}(undef,ngrid) + L = 1.0 + for i in 1:ngrid + # function to integrate in terms of y + integrand[i] = sqrt(1.0/y[i])*wy[i]#*w[i]*exp(-z[i]/L)*scale + integrand_sqrty[i] = sqrt(y[i])*wy[i]#*w[i]*exp(-z[i]/L)*scale + end + #@. integrand *= w + + print_vector(w,"Gauss Legendre weights",ngrid) + print_vector(y,"Gauss Legendre coordinate",ngrid) + print_vector(integrand,"Gauss Legendre integrand",ngrid) + + primitive = sum(integrand) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + primitive = sum(integrand_sqrty) + primitive_exact = 2.0/3.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + end end \ No newline at end of file From 7b1b25cbd96d2213b3209e375e17d443541e54b7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 2 Aug 2023 15:11:20 +0100 Subject: [PATCH 089/331] Add test of integral of K(z)/sqrt(1-z) from z = 0 to 1. This test seems to show a minimum achievable error of ~ 1e-7 that may be due to the limits of available precision. --- DivergingIntegrands_test.jl | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/DivergingIntegrands_test.jl b/DivergingIntegrands_test.jl index a3d872ec1..1e8a33de3 100644 --- a/DivergingIntegrands_test.jl +++ b/DivergingIntegrands_test.jl @@ -20,14 +20,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ function print_vector(vector,name,m) println("\n ",name," \n") for j in 1:m - @printf("%.3f ", vector[j]) + @printf("%.16f ", vector[j]) end println("") println("\n") end # gauss laguerre test - ngrid = 10 + ngrid = 12 nelement = 1 x, w = gausslaguerre(ngrid) print_vector(x,"Gauss Laguerre x",ngrid) @@ -40,6 +40,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ integrand_sqrty = Array{Float64,1}(undef,ngrid) integrand_Kz = Array{Float64,1}(undef,ngrid) value_Kz = Array{Float64,1}(undef,ngrid) + integrand_Kz_sqrt = Array{Float64,1}(undef,ngrid) + value_Kz_sqrt = Array{Float64,1}(undef,ngrid) value_y = Array{Float64,1}(undef,ngrid) value_z = Array{Float64,1}(undef,ngrid) L = 1.0 @@ -49,9 +51,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ # function to integrate in terms of y integrand[i] = sqrt(1.0/y)*w[i] integrand_sqrty[i] = sqrt(y)*w[i] - z = (1.0 - y)/(1.0 + 10^-13) - integrand_Kz[i] = ellipk(z)*w[i] - value_Kz[i] = ellipk(z) + z = (1.0 - y) + #z = (1.0 - y)/(1.0 + 10^-15) + ellipk_z = ellipk(z) + #if isnan(ellipk_z) || isinf(ellipk_z) + # ellipk_z = 0.0 + #end + sqrt_1_z = sqrt(1.0 - z) + #if isinf(sqrt_1_z) || isinf(sqrt_1_z) + # sqrt_1_z = 1.0 + #end + integrand_Kz[i] = ellipk_z*w[i] + value_Kz[i] = ellipk_z + integrand_Kz_sqrt[i] = ellipk_z*w[i]/sqrt_1_z + value_Kz_sqrt[i] = ellipk_z/sqrt_1_z value_z[i] = z end #@. integrand *= w @@ -75,6 +88,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ primitive_exact = 2.0 primitive_err = abs(primitive - primitive_exact) println("K(z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + print_vector(integrand_Kz_sqrt,"Kz/sqrt(1-z) integrand",ngrid) + print_vector(value_Kz_sqrt,"Kz/sqrt(1-z)",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz_sqrt) + primitive_exact = pi^2/2.0 + primitive_err = abs(primitive - primitive_exact) + println("K(z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) if false # gauss lobatto test From 9a19bf2157f763692eace8f2d9ae23bb06ef947c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 3 Aug 2023 08:58:43 +0100 Subject: [PATCH 090/331] Test subtracting the divergence in poorly converging toy problem. --- DivergingIntegrands_test.jl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/DivergingIntegrands_test.jl b/DivergingIntegrands_test.jl index 1e8a33de3..edcf4dc72 100644 --- a/DivergingIntegrands_test.jl +++ b/DivergingIntegrands_test.jl @@ -27,7 +27,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # gauss laguerre test - ngrid = 12 + ngrid = 24 nelement = 1 x, w = gausslaguerre(ngrid) print_vector(x,"Gauss Laguerre x",ngrid) @@ -42,6 +42,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ value_Kz = Array{Float64,1}(undef,ngrid) integrand_Kz_sqrt = Array{Float64,1}(undef,ngrid) value_Kz_sqrt = Array{Float64,1}(undef,ngrid) + integrand_Kz_sqrt_diff = Array{Float64,1}(undef,ngrid) + value_Kz_sqrt_diff = Array{Float64,1}(undef,ngrid) value_y = Array{Float64,1}(undef,ngrid) value_z = Array{Float64,1}(undef,ngrid) L = 1.0 @@ -51,8 +53,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # function to integrate in terms of y integrand[i] = sqrt(1.0/y)*w[i] integrand_sqrty[i] = sqrt(y)*w[i] - z = (1.0 - y) - #z = (1.0 - y)/(1.0 + 10^-15) + #z = (1.0 - y) + z = (1.0 - y)/(1.0 + 10^-15) ellipk_z = ellipk(z) #if isnan(ellipk_z) || isinf(ellipk_z) # ellipk_z = 0.0 @@ -65,6 +67,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ value_Kz[i] = ellipk_z integrand_Kz_sqrt[i] = ellipk_z*w[i]/sqrt_1_z value_Kz_sqrt[i] = ellipk_z/sqrt_1_z + value_Kz_sqrt_diff[i] = (ellipk_z - log(4.0) + log(sqrt_1_z))/sqrt_1_z + integrand_Kz_sqrt_diff[i] = value_Kz_sqrt_diff[i]*w[i] value_z[i] = z end #@. integrand *= w @@ -95,7 +99,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ primitive = sum(integrand_Kz_sqrt) primitive_exact = pi^2/2.0 primitive_err = abs(primitive - primitive_exact) - println("K(z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + println("K(z)/sqrt(1-z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + print_vector(integrand_Kz_sqrt_diff,"(Kz - log 4/sqrt(1-z))/sqrt(1-z) integrand",ngrid) + print_vector(value_Kz_sqrt_diff,"(Kz - log 4/sqrt(1-z))/sqrt(1-z)",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz_sqrt_diff) + primitive_exact = (pi^2/2.0) - 2.0*(2.0*log(2.0) + 1.0) + primitive_err = abs(primitive - primitive_exact) + println("(K(z) - log(4/sqrt(1-z)))/sqrt(1-z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) if false # gauss lobatto test From 625cc22f83eee28fe96d1816ab69d8bdbc5f7cb5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 4 Aug 2023 08:32:30 +0100 Subject: [PATCH 091/331] Reverse order of integration dummy coordinate for easier understanding of printout, and addition of L2 norm for diagnostic tests --- fkpl_test.jl | 82 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index ef80ae3dc..0fbb427dc 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -27,6 +27,7 @@ using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwelli using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative!, second_derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.velocity_moments: integrate_over_vspace using moment_kinetics.communication using moment_kinetics.looping using moment_kinetics.array_allocation: allocate_shared_float @@ -76,6 +77,22 @@ function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) end end +""" +L2norm assuming the input is the +absolution error ff_err = ff - ff_exact +We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) +where the volume of velocity space is finite +""" +function L2norm_vspace(ff_err,vpa,vperp) + ff_ones = copy(ff_err) + @. ff_ones = 1.0 + gg = copy(ff_err) + @. gg = (ff_err)^2 + num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + L2norm = sqrt(num/denom) + return L2norm +end #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) # #Gamma = 0.0 @@ -710,8 +727,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint nquad = size(x_laguerre,1) - @. x_scaled[1:nquad] = node_min + (node_max - node_min)*exp(-x_laguerre) - @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre + for j in 1:nquad + x_scaled[nquad+1-j] = node_min + (node_max - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad+1-j] = (node_max - node_min)*w_laguerre[j] + end nquad_coord = nquad #println("lower divergence") else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence @@ -794,8 +813,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - #mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-13) + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) #mm = 4.0*vperp_val*x_kvperp/denom prefac = sqrt(denom) ellipe_mm = ellipe(mm) @@ -1106,6 +1125,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_C_Maxwell_val = maximum(Cssp_Maxwell) max_C_numerical_val = maximum(Cssp_numerical) println("max_C_err: ",max_C_err) + L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) + println("L2_C_err: ",L2_C_err) println("max_C_Maxwell_val: ",max_C_Maxwell_val) println("max_C_numerical_val: ",max_C_numerical_val) if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" @@ -1131,12 +1152,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H_err = abs(Hsp - H_Maxwell) max_H_err = maximum(H_err) println("max_H_err: ",max_H_err) + L2_H_err = L2norm_vspace(H_err,vpa,vperp) + println("L2_H_err: ",L2_H_err) @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) max_dHdvperp_err = maximum(dHdvperp_err) println("max_dHdvperp_err: ",max_dHdvperp_err) + L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) + println("L2_dHdvperp_err: ",L2_dHdvperp_err) @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) max_dHdvpa_err = maximum(dHdvpa_err) println("max_dHdvpa_err: ",max_dHdvpa_err) + L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) + println("L2_dHdvpa_err: ",L2_dHdvpa_err) + if plot_C @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1200,6 +1228,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) + println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) if plot_d2Gdvperp2 @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1217,6 +1247,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) + println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) if plot_d2Gdvperpdvpa @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1234,6 +1266,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) max_dGdvperp_err = maximum(dGdvperp_err) println("max_dGdvperp_err: ",max_dGdvperp_err) + L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) + println("L2_dGdvperp_err: ",L2_dGdvperp_err) if plot_dGdvperp @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1251,6 +1285,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) + println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) if plot_d2Gdvpa2 @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1268,6 +1304,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G_err = abs(Gsp - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) + L2_G_err = L2norm_vspace(G_err,vpa,vperp) + println("L2_G_err: ",L2_G_err) if plot_G @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1288,7 +1326,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ finalize_comms!() end #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - results = maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err) + (results = (maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), + maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + L2norm_vspace(Cssp_err,vpa,vperp), L2norm_vspace(G_err,vpa,vperp), L2norm_vspace(H_err,vpa,vperp), L2norm_vspace(dHdvpa_err,vpa,vperp), + L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), + L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), + maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err) )) return results end @@ -1617,9 +1661,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 5 nscan = 3 - nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] nelement_list = Int[2, 4, 8] #nelement_list = Int[2] @@ -1643,6 +1687,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_C_err = Array{mk_float,1}(undef,nscan) + L2_G_err = Array{mk_float,1}(undef,nscan) + L2_H_err = Array{mk_float,1}(undef,nscan) + L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) expected_label = L"(1/N_{el})^{n_g - 1}" @@ -1654,7 +1708,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dGdvperp_err[iscan], L2_C_err[iscan], + L2_G_err[iscan], L2_H_err[iscan], L2_dHdvpa_err[iscan], + L2_dHdvperp_err[iscan], L2_d2Gdvperp2_err[iscan], L2_d2Gdvpa2_err[iscan], + L2_d2Gdvperpdvpa_err[iscan], L2_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], @@ -1686,6 +1743,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err, expected], + xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err, expected], xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel expected_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, From dac221865bb896f85f40a60496251ba407817272 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 7 Aug 2023 10:37:11 +0100 Subject: [PATCH 092/331] Added commented out lines for divergence-free integration method for testing, and switch to a flow-free Maxwellian for testing. --- fkpl_test.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 0fbb427dc..a9f660d34 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -541,7 +541,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up test Maxwellian # species s denss = 1.0 #3.0/4.0 - upars = 0.5 #2.0/3.0 + upars = 0.0 #2.0/3.0 ppars = 1.0 #2.0/3.0 pperps = 1.0 #2.0/3.0 press = get_pressure(ppars,pperps) @@ -549,7 +549,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vths = get_vth(press,denss,ms) # species sp denssp = 1.0 #3.0/4.0 - uparsp = 0.5 #2.0/3.0 + uparsp = 0.0 #2.0/3.0 pparsp = 1.0 #2.0/3.0 pperpsp = 1.0 #2.0/3.0 pressp = get_pressure(pparsp,pperpsp) @@ -894,6 +894,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, @@ -942,6 +943,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, From 3b0c9e81d6466beb00673a4bd93c4347ccbc4c4d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 7 Aug 2023 11:55:45 +0100 Subject: [PATCH 093/331] Pass vpa and vperp coordinate structs to lowest level integration function instead of using global variable. --- fkpl_test.jl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index a9f660d34..9f62e6bdc 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -796,8 +796,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpa,vpa_nodes, # info about primed vperp grids - nquad_vperp,ielement_vperp,vperp_nodes, # info about primed vperp grids + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids for igrid_vperp in 1:vperp.ngrid @@ -874,7 +874,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) for ielement_vpap in 1:ielement_vpa_low-1 @@ -884,8 +884,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end @@ -898,8 +898,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end @@ -910,8 +910,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes, - nquad_vperp,ielement_vperpp,vperp_nodes, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) @@ -934,7 +934,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) end @@ -948,7 +948,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) end @@ -961,7 +961,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) end @@ -997,11 +997,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println(igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - #println(igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @@ -1664,11 +1664,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 3 + nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - nelement_list = Int[2, 4, 8] - #nelement_list = Int[2] + #nelement_list = Int[2, 4, 8] + nelement_list = Int[8] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From 8ca4ac966bba798d764d8c4b13fc33e67759d6c6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 8 Aug 2023 15:23:25 +0100 Subject: [PATCH 094/331] Correct integration method to only put divergence-accommodating grid where there is a divergence. The previous method put the divergence accommodating grid wherever ielement_vpa or ielement_vperp were separately the indices for a diverging element, when divergences only occur when both element indices are match that of the diverging 2D element. --- fkpl_test.jl | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 9f62e6bdc..beaf52d93 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -919,6 +919,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids @@ -931,7 +953,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids @@ -958,7 +980,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids @@ -1664,11 +1686,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_Lagrange_integral_scan initialize_comms!() ngrid = 5 - nscan = 1 + nscan = 4 #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - nelement_list = Int[8] + #nelement_list = Int[8] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From f12dbdb019d90d6410bc98bbbef3fd105ce82007 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 9 Aug 2023 09:35:35 +0000 Subject: [PATCH 095/331] Bring velocity integrals test up to date. Confirms that machine precision integration should be possible for F with ngrid = 17 and nelement = 8. --- test_velocity_integrals.jl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test_velocity_integrals.jl b/test_velocity_integrals.jl index 108a665f7..4f74828ae 100644 --- a/test_velocity_integrals.jl +++ b/test_velocity_integrals.jl @@ -16,14 +16,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test ngrid = 17 #number of points per element - nelement_local = 20 # number of elements per rank + nelement_local = 8 # number of elements per rank nelement_global = nelement_local # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant - discretization = "chebyshev_pseudospectral" + #discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" fd_option = "fourth_order_centered" + cheb_option = "matrix" adv_input = advection_input("default", 1.0, 0.0, 0.0) nrank = 1 irank = 0 @@ -31,13 +33,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ # create the 'input' struct containing input info needed to create a # coordinate vr_input = grid_input("vperp", 1, 1, 1, - nrank, irank, 1.0, discretization, fd_option, bc, adv_input,comm) + nrank, irank, 1.0, discretization, fd_option, cheb_option, bc, adv_input,comm) vz_input = grid_input("vpa", ngrid, nelement_global, nelement_local, - nrank, irank, Lvpa, discretization, fd_option, bc, adv_input,comm) + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) vpa_input = grid_input("vpa", ngrid, nelement_global, nelement_local, - nrank, irank, Lvpa, discretization, fd_option, bc, adv_input,comm) + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) vperp_input = grid_input("vperp", ngrid, nelement_global, nelement_local, - nrank, irank, Lvperp, discretization, fd_option, bc, adv_input,comm) + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) # create the coordinate struct 'x' println("made inputs") println("vpa: ngrid: ",ngrid," nelement: ",nelement_local, " Lvpa: ",Lvpa) @@ -75,8 +77,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ dens_test = get_density(dfn,vpa,vperp) upar_test = get_upar(dfn,vpa,vperp,dens_test) - ppar_test = get_ppar(dfn,vpa,vperp,upar_test) - pperp_test = get_pperp(dfn,vpa,vperp) + ppar_test = get_ppar(dfn,vpa,vperp,upar_test,mass) + pperp_test = get_pperp(dfn,vpa,vperp,mass) pres_test = pressure(ppar_test,pperp_test) # output test results println("") @@ -102,7 +104,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end dens_test = get_density(dfn1D,vz,vr) upar_test = get_upar(dfn1D,vz,vr,dens_test) - ppar_test = get_ppar(dfn1D,vz,vr,upar_test) + ppar_test = get_ppar(dfn1D,vz,vr,upar_test,mass) # output test results println("") println("1D Maxwellian") @@ -134,8 +136,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ dens_test = get_density(dfn,vpa,vperp) upar_test = get_upar(dfn,vpa,vperp,dens_test) - ppar_test = get_ppar(dfn,vpa,vperp,upar_test) - pperp_test = get_pperp(dfn,vpa,vperp) + ppar_test = get_ppar(dfn,vpa,vperp,upar_test,mass) + pperp_test = get_pperp(dfn,vpa,vperp,mass) # output test results println("") From ef508ddc7ed017915ea8d1f13fc879df86df7a13 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 9 Aug 2023 11:14:13 +0000 Subject: [PATCH 096/331] Addition of density integration test and no_divergences! integration loop, using Gauss-Legendre points with 2*ngrid points in all elements on the primed grid. --- fkpl_test.jl | 103 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 24 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index beaf52d93..555346f6f 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -504,6 +504,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + n_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + nsp = allocate_shared_float(nvpa,nvperp) + n_err = allocate_shared_float(nvpa,nvperp) + H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) @@ -795,7 +799,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids @@ -864,6 +868,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*(vpa_val - x_kvpa)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (n_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end end end @@ -872,7 +880,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -883,7 +891,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -897,7 +905,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -909,7 +917,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -920,7 +928,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -931,7 +939,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -942,7 +950,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -954,7 +962,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -968,7 +976,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -981,7 +989,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + + function loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -1035,9 +1064,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H1_weights[ivpa,ivperp,:,:] = 0.0 @. H2_weights[ivpa,ivperp,:,:] = 0.0 @. H3_weights[ivpa,ivperp,:,:] = 0.0 + @. n_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights, + #loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -1064,6 +1095,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Hsp[ivpa,ivperp] = 0.0 dHspdvpa[ivpa,ivperp] = 0.0 dHspdvperp[ivpa,ivperp] = 0.0 + nsp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] @@ -1076,6 +1108,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] end end #end @@ -1140,10 +1173,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_d2Gdvpa2 = false #true plot_G = false #true plot_C = false #true + plot_n = false #true begin_serial_region() @serial_region begin println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. n_err = nsp - denssp + max_n_err = maximum(n_err) + println("max_n_err: ",max_n_err) + println("spot check n_err: ",n_err[end,end]) + L2_n_err = L2norm_vspace(n_err,vpa,vperp) + println("L2_n_err: ",L2_n_err) @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) max_C_err = maximum(Cssp_err) max_C_Maxwell_val = maximum(Cssp_Maxwell) @@ -1189,6 +1229,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) println("L2_dHdvpa_err: ",L2_dHdvpa_err) + if plot_n + @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, n_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_err.pdf") + savefig(outfile) + end if plot_C @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1356,7 +1406,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), - maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err) )) + maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), + maximum(n_err), L2norm_vspace(n_err,vpa,vperp) )) return results end @@ -1685,12 +1736,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 5 - nscan = 4 + ngrid = 17 + nscan = 3 #nelement_list = Int[2, 4, 8, 16, 32] - nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] - #nelement_list = Int[8] + #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8] + #nelement_list = Int[2] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) @@ -1720,6 +1771,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_n_err = Array{mk_float,1}(undef,nscan) + L2_n_err = Array{mk_float,1}(undef,nscan) expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) @@ -1739,13 +1792,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], - max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan]) + max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], + max_n_err[iscan], L2_n_err[iscan]) = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) end if global_rank[]==0 fontsize = 8 ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) xlabel = L"N_{element}" + nlabel = L"\epsilon(n)" Clabel = L"\epsilon(C)" Gvpalabel = L"\epsilon(\Gamma_{\|\|})" Gvperplabel = L"\epsilon(\Gamma_{\perp})" @@ -1767,8 +1822,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err, expected], - xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected], + xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) @@ -1776,8 +1831,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err, expected], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel expected_label], ylabel="", + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) From 924de245f6ffd49b03052ed81d71587bb3589a9b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 10 Aug 2023 10:58:10 +0100 Subject: [PATCH 097/331] Further test and variable scope fixes. --- fkpl_test.jl | 72 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 555346f6f..f7d2146a9 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -77,6 +77,12 @@ function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) end end + +function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end +end """ L2norm assuming the input is the absolution error ff_err = ff - ff_exact @@ -959,7 +965,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -972,7 +978,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, @@ -986,7 +992,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -1066,8 +1072,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H3_weights[ivpa,ivperp,:,:] = 0.0 @. n_weights[ivpa,ivperp,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - #loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + #loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids @@ -1164,24 +1170,24 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Cssp_div_numerical *= nussp end - plot_H = false #true - plot_dHdvpa = false #true - plot_dHdvperp = false #true - plot_d2Gdvperp2 = false #true - plot_d2Gdvperpdvpa = false #true - plot_dGdvperp = false #true - plot_d2Gdvpa2 = false #true - plot_G = false #true + plot_H = true + plot_dHdvpa = true + plot_dHdvperp = true + plot_d2Gdvperp2 = true + plot_d2Gdvperpdvpa = true + plot_dGdvperp = true + plot_d2Gdvpa2 = true + plot_G = true plot_C = false #true plot_n = false #true begin_serial_region() @serial_region begin println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. n_err = nsp - denssp + @. n_err = abs(nsp - denssp) max_n_err = maximum(n_err) println("max_n_err: ",max_n_err) - println("spot check n_err: ",n_err[end,end]) + println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) L2_n_err = L2norm_vspace(n_err,vpa,vperp) println("L2_n_err: ",L2_n_err) @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) @@ -1216,16 +1222,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H_err = abs(Hsp - H_Maxwell) max_H_err = maximum(H_err) println("max_H_err: ",max_H_err) + println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) L2_H_err = L2norm_vspace(H_err,vpa,vperp) println("L2_H_err: ",L2_H_err) @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) max_dHdvperp_err = maximum(dHdvperp_err) println("max_dHdvperp_err: ",max_dHdvperp_err) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) println("L2_dHdvperp_err: ",L2_dHdvperp_err) @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) max_dHdvpa_err = maximum(dHdvpa_err) println("max_dHdvpa_err: ",max_dHdvpa_err) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) println("L2_dHdvpa_err: ",L2_dHdvpa_err) @@ -1302,6 +1311,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) if plot_d2Gdvperp2 @@ -1321,6 +1331,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) if plot_d2Gdvperpdvpa @@ -1340,6 +1351,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) max_dGdvperp_err = maximum(dGdvperp_err) println("max_dGdvperp_err: ",max_dGdvperp_err) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) println("L2_dGdvperp_err: ",L2_dGdvperp_err) if plot_dGdvperp @@ -1359,6 +1371,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) if plot_d2Gdvpa2 @@ -1378,6 +1391,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G_err = abs(Gsp - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) + println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) L2_G_err = L2norm_vspace(G_err,vpa,vperp) println("L2_G_err: ",L2_G_err) if plot_G @@ -1736,12 +1750,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 17 - nscan = 3 + ngrid = 9 + nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - nelement_list = Int[2, 4, 8] - #nelement_list = Int[2] + #nelement_list = Int[2, 4, 8] + nelement_list = Int[8] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) @@ -1776,7 +1790,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" for iscan in 1:nscan local nelement = nelement_list[iscan] @@ -1813,8 +1831,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" dGdvperplabel = L"\epsilon(dG/d v_{\perp})" #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) @@ -1822,8 +1840,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected], - xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label], ylabel="", + plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) @@ -1831,8 +1849,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label], ylabel="", + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) @@ -1841,8 +1859,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) println(outfile) #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected], - xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label], ylabel="", + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) From 6b54ac2e1dbcc8fa4c39113f50eb2d2e839b7e10 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 11 Aug 2023 14:51:50 +0100 Subject: [PATCH 098/331] Experiments using an out-of-the-box rule to compute the elemental integration weights. Currently the out-of-the-box cubature rule package is many order of magnitude slower than the FastGaussQuadrature based-quadrature. --- fkpl_cubature_mpi_test.jl | 528 ++++++++++++++++++++++++++++++++++++++ fkpl_mpi_test.jl | 210 +++++++-------- 2 files changed, 637 insertions(+), 101 deletions(-) create mode 100644 fkpl_cubature_mpi_test.jl diff --git a/fkpl_cubature_mpi_test.jl b/fkpl_cubature_mpi_test.jl new file mode 100644 index 000000000..8a64c0ed7 --- /dev/null +++ b/fkpl_cubature_mpi_test.jl @@ -0,0 +1,528 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using SpecialFunctions: erf, ellipe, ellipk +using FastGaussQuadrature +using Dates +using LinearAlgebra: mul! +using Cubature: hcubature + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.gauss_legendre: ielement_global_func +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs, F_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float + +function init_grids(nelement,nelement_local,ngrid,max_cores_per_shm_block) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + bc = "zero" + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input, comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input, comm) + + # create the coordinate structs + #println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end + + # define distribution memory MPI versions of these coordinates + vpa_MPI_ngrid = ngrid #number of points per element + vpa_MPI_nelement_local = nelement_local # nelement # number of elements per rank + vpa_MPI_nelement_global = nelement # total number of elements + #vpa_MPI_L = 12.0 #physical box size in reference units + vperp_MPI_ngrid = ngrid #number of points per element + vperp_MPI_nelement_local = nelement_local# nelement # number of elements per rank + vperp_MPI_nelement_global = nelement # total number of elements + #vperp_MPI_L = 6.0 #physical box size in reference units + + # using standard MPI routine with assumption of exactly the right number of cores + #irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp = setup_distributed_memory_MPI(vpa_MPI_nelement_global,vpa_MPI_nelement_local, + # vperp_MPI_nelement_global,vperp_MPI_nelement_local) + # novel MPI routine that tries to use only a subset of the cores available + # need to specify max number of cores in a shared-memory region (depends on hardware) + + ( (irank_vpa, nrank_vpa, comm_sub_vpa, irank_vperp, nrank_vperp, comm_sub_vperp) = + setup_distributed_memory_MPI_for_weights_precomputation(vpa_MPI_nelement_global,vpa_MPI_nelement_local, + vperp_MPI_nelement_global,vperp_MPI_nelement_local,max_cores_per_shm_block,printout=false)) + vpa_MPI_input = grid_input("vpa", vpa_ngrid, vpa_MPI_nelement_global, vpa_MPI_nelement_local, + nrank_vpa, irank_vpa, vpa_L, discretization, fd_option, cheb_option, bc, adv_input, comm_sub_vpa) + vperp_MPI_input = grid_input("vperp", vperp_MPI_ngrid, vperp_MPI_nelement_global, vperp_MPI_nelement_local, + nrank_vperp, irank_vperp, vperp_L, discretization, fd_option, cheb_option, bc, adv_input, comm_sub_vperp) + vpa_MPI = define_coordinate(vpa_MPI_input) + #println("vpa ",vpa_MPI.grid) + vperp_MPI = define_coordinate(vperp_MPI_input) + #println("vperp ",vperp_MPI.grid) + + return vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI +end + +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end + +function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth + return eta +end + +""" +functions for doing the Lagrange polynomial integration +""" + +function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax +end + +function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes +end +""" +Lagrange polynomial +args: +j - index of l_j from list of nodes +x_nodes - array of x node values +x - point where interpolated value is returned +""" +function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly +end + +function get_scaled_x_w!(x_scaled, w_scaled, x, w, node_min, node_max) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled = scale*x + shift + @. w_scaled = scale*w + return nothing +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + # Set up MPI + initialize_comms!() + + nelement_global = 2 # global number of elements in vpa (vperp) + nelement_local = 2 # local number of elements in each distributed memory `chunk' of the vpa (vperp) grid + ngrid = 7 # number of points per element + max_cores_per_shm_block = 20 # maximum number of cores in a shared-memory block + + vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI = init_grids(nelement_global,nelement_local,ngrid,max_cores_per_shm_block) + + nvpa_local = vpa_MPI.n + nvpa_global = vpa_MPI.n_global + nvperp_local = vperp_MPI.n + nvperp_global = vperp_MPI.n_global + + # coord.n is the local number of points in each shared memory block + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=nvperp_local, vpa=nvpa_local, + vzeta=1, vr=1, vz=1) + if global_rank[] == 0 + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + fsp_in = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_Maxwell = Array{mk_float,2}(undef,nvpa_local,nvperp_local) + G_weights = allocate_shared_float(nvpa_local,nvperp_local,nvpa_global,nvperp_global) + Gsp = allocate_shared_float(nvpa_local,nvperp_local) + G_err = allocate_shared_float(nvpa_local,nvperp_local) + + Gsp_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_Maxwell_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_err_global = Array{mk_float,2}(undef,nvpa_global,nvperp_global) + G_weights_global = Array{mk_float,4}(undef,nvpa_global,nvperp_global,nvpa_global,nvperp_global) + + if global_rank[] == 0 + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + # set up test Maxwellian + # species sp + denssp = 1.0 #3.0/4.0 + uparsp = 0.5 #2.0/3.0 + pparsp = 1.0 #2.0/3.0 + pperpsp = 1.0 #2.0/3.0 + pressp = get_pressure(pparsp,pperpsp) + msp = 1.0 + vthsp = get_vth(pressp,denssp,msp) + + for ivperp in 1:nvperp_global + for ivpa in 1:nvpa_global + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + G_Maxwell_global[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + end + end + for ivperp in 1:nvperp_local + for ivpa in 1:nvpa_local + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa_MPI,vperp_MPI,ivpa,ivperp) + end + end + + if global_rank[] == 0 + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x, w = gausslegendre(nquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,nquad), Array{mk_float,1}(undef,nquad) + + if global_rank[] == 0 + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + + # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 + function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) + end + + function G_weights_integrand(v::Vector{mk_float},vpa_val,vperp_val, + igrid_vpap,ielement_vpap, + igrid_vperpp,ielement_vperpp, + vpap_struct,vperpp_struct) + #function G_weights_integrand(v::Vector{mk_float},vpa_val=0.0,vperp_val=0.0, + # igrid_vpap=1,ielement_vpap=1, + # igrid_vperpp=1,ielement_vperpp=1, + # vpap_struct=vpa,vperpp_struct=vperp) + + vpa_nodes = get_nodes(vpap_struct,ielement_vpap) + ivpap = vpap_struct.igrid_full[igrid_vpap,ielement_vpap] + + vperp_nodes = get_nodes(vperpp_struct,ielement_vperpp) + ivperpp = vperpp_struct.igrid_full[igrid_vperpp,ielement_vperpp] + + vpap_val = v[1] + vperpp_val = v[2] + denom = (vpa_val - vpap_val)^2 + (vperp_val + vperpp_val)^2 + mm = 4.0*vperp_val*vperpp_val/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + lagrange_poly_vpa = lagrange_poly(igrid_vpap,vpa_nodes,vpap_val) + lagrange_poly_vperp = lagrange_poly(igrid_vperpp,vperp_nodes,vperpp_val) + + (G_weight = lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*vperpp_val*2.0/sqrt(pi)) + return G_weight + end + + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + vperp_val = vperp_MPI.grid[ivperp] + vpa_val = vpa_MPI.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + + # loop over elements and grid points within elements on primed coordinate + for ielement_vperp in 1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperp,vperp.nelement_local) + get_scaled_x_w!(x_vperp, w_vperp, x, w, vperp_min, vperp_max) + + for ielement_vpa in 1:vpa.nelement_local + + vpa_nodes = get_nodes(vpa,ielement_vpa) + # assumme Gauss-Lobatto elements + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + get_scaled_x_w!(x_vpa, w_vpa, x, w, vpa_min, vpa_max) + + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + #for kvperp in 1:nquad + # for kvpa in 1:nquad + # x_kvpa = x_vpa[kvpa] + # x_kvperp = x_vperp[kvperp] + # w_kvperp = w_vperp[kvperp] + # w_kvpa = w_vpa[kvpa] + # denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + # mm = 4.0*vperp_val*x_kvperp/denom + # prefac = sqrt(denom) + # ellipe_mm = ellipe(mm) + # ellipk_mm = ellipk(mm) + # G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + # lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + # lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + # (G_weights[ivpa,ivperp,ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + G_weights_int(v) = G_weights_integrand(v,vpa_val,vperp_val, + igrid_vpa,ielement_vpa, + igrid_vperp,ielement_vperp, + vpa,vperp) + #G_weights_int(v) = G_weights_integrand(v,vpa_val=vpa_val,vperp_val=vperp_val, + # igrid_vpap=igrid_vpa,ielement_vpap=ielement_vpa, + # igrid_vperpp=igrid_vperp,ielement_vperpp=ielement_vperp, + # vpap_struct=vpa,vperpp_struct=vperp) + #(val,err) = hcubature(G_weights_int, [vpa_min,vperp_min], [vpa_max, vperp_max]; reltol=1e-8, abstol=0, maxevals=0) + (val,err) = hcubature(G_weights_int, [vpa_min,vperp_min], [vpa_max, vperp_max]; abstol=1e-8) + #println("integrated successfully") + G_weights[ivpa,ivperp,ivpap,ivperpp] += val + # end + #end + end + end + end + end + end + + #_block_synchronize() + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + begin_vperp_vpa_region() + + # use precalculated weights to calculate Gsp using nodal values of fs + @loop_vperp_vpa ivperp ivpa begin + Gsp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp_global + for ivpap in 1:nvpa_global + Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end + end + end + + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + + # local plotting + plot_local_G = false #true + print_local_G = true + + begin_serial_region() + @serial_region begin + if print_local_G + @. G_err = abs(Gsp - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + end + if plot_local_G + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + @views heatmap(vperp_MPI.grid, vpa_MPI.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") + savefig(outfile) + end + end + + mpi_test = false #true + if mpi_test + # allreduce operations + # get local data into global array before allreduce + begin_serial_region() + @serial_region begin + @. Gsp_global = 0.0 + @. G_weights_global = 0.0 + # set up indexes for doing the assignment to the global array + iel_vpa_min = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,1) + iel_vpa_max = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,vpa_MPI.nelement_local) + ivpa_global_min = vpa.imin[iel_vpa_min] + ivpa_global_max = vpa.imax[iel_vpa_max] + if vpa_MPI.irank > 0 + j = 1 #exclude lowest point + else + j = 0 #include lowest point + end + ivpa_local_min = 1 + j + ivpa_local_max = vpa_MPI.n + + iel_vperp_min = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,1) + iel_vperp_max = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,vperp_MPI.nelement_local) + ivperp_global_min = vperp.imin[iel_vperp_min] + ivperp_global_max = vperp.imax[iel_vperp_max] + if vperp_MPI.irank > 0 + k = 1 #exclude lowest point + else + k = 0 #include lowest point + end + ivperp_local_min = 1 + k + ivperp_local_max = vperp_MPI.n + + @. Gsp_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max] += Gsp[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max] + @. G_weights_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max,:,:] += G_weights[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max,:,:] + + # first reduce along vperp dimension, then along vpa + MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) + MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vperp_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vpa_MPI.comm) + # then broadcast to all cores, noting that some may + # not be included in the communicators used in the calculation + MPI.Bcast!(Gsp_global,comm_world,root=0) + MPI.Bcast!(G_weights_global,comm_world,root=0) + end + + # global plotting + plot_G = false #true + + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + @. G_err_global = abs(Gsp_global - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global): ",max_G_err) + if plot_G + @views heatmap(vperp.grid, vpa.grid, Gsp_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end + end + end + + # check that the weights have been calculated properly by recalculating G + # using the looping and MPI arrangement of the main code + z_irank, z_nrank_per_group, z_comm, r_irank, r_nrank_per_group, r_comm = setup_distributed_memory_MPI(1,1,1,1) + # coord.n is the local number of points in each shared memory block + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=nvperp_global, vpa=nvpa_global, + vzeta=1, vr=1, vz=1) + Gsp_global_shared = allocate_shared_float(nvpa_global,nvperp_global) + # use precalculated weights to calculate Gsp using nodal values of fs + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Gsp_global_shared[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp_global + for ivpap in 1:nvpa_global + Gsp_global_shared[ivpa,ivperp] += G_weights_global[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end + end + end + begin_serial_region() + @serial_region begin + @. G_err_global = abs(Gsp_global_shared - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global from redistributed weights): ",max_G_err) + end + end + # clean up MPI objects + finalize_comms!() + + +end \ No newline at end of file diff --git a/fkpl_mpi_test.jl b/fkpl_mpi_test.jl index f935adac8..3b4328f92 100644 --- a/fkpl_mpi_test.jl +++ b/fkpl_mpi_test.jl @@ -189,10 +189,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ # Set up MPI initialize_comms!() - nelement_global = 4 # global number of elements in vpa (vperp) - nelement_local = 1 # local number of elements in each distributed memory `chunk' of the vpa (vperp) grid - ngrid = 5 # number of points per element - max_cores_per_shm_block = 4 # maximum number of cores in a shared-memory block + nelement_global = 2 # global number of elements in vpa (vperp) + nelement_local = 2 # local number of elements in each distributed memory `chunk' of the vpa (vperp) grid + ngrid = 7 # number of points per element + max_cores_per_shm_block = 20 # maximum number of cores in a shared-memory block vpa, vperp, vpa_spectral, vperp_spectral, vpa_MPI, vperp_MPI = init_grids(nelement_global,nelement_local,ngrid,max_cores_per_shm_block) @@ -265,6 +265,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end + + # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 + function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) + end + # precalculated weights, integrating over Lagrange polynomials begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -277,11 +283,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_nodes = get_nodes(vperp,ielement_vperp) vperp_max = vperp_nodes[end] - if ielement_vperp > 1 # Gauss-Lobatto - vperp_min = vperp_nodes[1] - else # adjust for the Gauss-Radau element - vperp_min = 0.0 - end + vperp_min = vperp_nodes[1]*nel_low(ielement_vperp,vperp.nelement_local) get_scaled_x_w!(x_vperp, w_vperp, x, w, vperp_min, vperp_max) for ielement_vpa in 1:vpa.nelement_local @@ -352,13 +354,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ # local plotting plot_local_G = false#true + print_local_G = true begin_serial_region() @serial_region begin - if plot_local_G + if print_local_G @. G_err = abs(Gsp - G_Maxwell) max_G_err = maximum(G_err) println("max_G_err: ",max_G_err) + end + if plot_local_G @views heatmap(vperp_MPI.grid, vpa_MPI.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("fkpl_G_lagrange"*string(vpa_MPI.irank)*"."*string(vperp_MPI.irank)*".pdf") @@ -374,101 +379,104 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end - @. Gsp_global = 0.0 - @. G_weights_global = 0.0 - # allreduce operations - # get local data into global array before allreduce - begin_serial_region() - @serial_region begin - # set up indexes for doing the assignment to the global array - iel_vpa_min = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,1) - iel_vpa_max = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,vpa_MPI.nelement_local) - ivpa_global_min = vpa.imin[iel_vpa_min] - ivpa_global_max = vpa.imax[iel_vpa_max] - if vpa_MPI.irank > 0 - j = 1 #exclude lowest point - else - j = 0 #include lowest point - end - ivpa_local_min = 1 + j - ivpa_local_max = vpa_MPI.n - - iel_vperp_min = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,1) - iel_vperp_max = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,vperp_MPI.nelement_local) - ivperp_global_min = vperp.imin[iel_vperp_min] - ivperp_global_max = vperp.imax[iel_vperp_max] - if vperp_MPI.irank > 0 - k = 1 #exclude lowest point - else - k = 0 #include lowest point - end - ivperp_local_min = 1 + k - ivperp_local_max = vperp_MPI.n - - @. Gsp_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max] += Gsp[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max] - @. G_weights_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max,:,:] += G_weights[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max,:,:] - - # first reduce along vperp dimension, then along vpa - MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) - MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) - MPI.Allreduce!(G_weights_global,+,vperp_MPI.comm) - MPI.Allreduce!(G_weights_global,+,vpa_MPI.comm) - # then broadcast to all cores, noting that some may - # not be included in the communicators used in the calculation - MPI.Bcast!(Gsp_global,comm_world,root=0) - MPI.Bcast!(G_weights_global,comm_world,root=0) - end - - # global plotting - plot_G = false #true - - begin_serial_region() - if global_rank[] == 0 + mpi_test = false + if mpi_test + # allreduce operations + # get local data into global array before allreduce + begin_serial_region() @serial_region begin - @. G_err_global = abs(Gsp_global - G_Maxwell_global) - max_G_err = maximum(G_err_global) - println("max_G_err (global): ",max_G_err) - if plot_G - @views heatmap(vperp.grid, vpa.grid, Gsp_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_Maxwell_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_err_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_err.pdf") - savefig(outfile) - end + @. Gsp_global = 0.0 + @. G_weights_global = 0.0 + # set up indexes for doing the assignment to the global array + iel_vpa_min = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,1) + iel_vpa_max = ielement_global_func(vpa_MPI.nelement_local,vpa_MPI.irank,vpa_MPI.nelement_local) + ivpa_global_min = vpa.imin[iel_vpa_min] + ivpa_global_max = vpa.imax[iel_vpa_max] + if vpa_MPI.irank > 0 + j = 1 #exclude lowest point + else + j = 0 #include lowest point + end + ivpa_local_min = 1 + j + ivpa_local_max = vpa_MPI.n + + iel_vperp_min = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,1) + iel_vperp_max = ielement_global_func(vperp_MPI.nelement_local,vperp_MPI.irank,vperp_MPI.nelement_local) + ivperp_global_min = vperp.imin[iel_vperp_min] + ivperp_global_max = vperp.imax[iel_vperp_max] + if vperp_MPI.irank > 0 + k = 1 #exclude lowest point + else + k = 0 #include lowest point + end + ivperp_local_min = 1 + k + ivperp_local_max = vperp_MPI.n + + @. Gsp_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max] += Gsp[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max] + @. G_weights_global[ivpa_global_min:ivpa_global_max,ivperp_global_min:ivperp_global_max,:,:] += G_weights[ivpa_local_min:ivpa_local_max,ivperp_local_min:ivperp_local_max,:,:] + + # first reduce along vperp dimension, then along vpa + MPI.Allreduce!(Gsp_global,+,vperp_MPI.comm) + MPI.Allreduce!(Gsp_global,+,vpa_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vperp_MPI.comm) + MPI.Allreduce!(G_weights_global,+,vpa_MPI.comm) + # then broadcast to all cores, noting that some may + # not be included in the communicators used in the calculation + MPI.Bcast!(Gsp_global,comm_world,root=0) + MPI.Bcast!(G_weights_global,comm_world,root=0) end - end - - # check that the weights have been calculated properly by recalculating G - # using the looping and MPI arrangement of the main code - z_irank, z_nrank_per_group, z_comm, r_irank, r_nrank_per_group, r_comm = setup_distributed_memory_MPI(1,1,1,1) - # coord.n is the local number of points in each shared memory block - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=1, sn=1, - r=1, z=1, vperp=nvperp_global, vpa=nvpa_global, - vzeta=1, vr=1, vz=1) - Gsp_global_shared = allocate_shared_float(nvpa_global,nvperp_global) - # use precalculated weights to calculate Gsp using nodal values of fs - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - Gsp_global_shared[ivpa,ivperp] = 0.0 - for ivperpp in 1:nvperp_global - for ivpap in 1:nvpa_global - Gsp_global_shared[ivpa,ivperp] += G_weights_global[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + + # global plotting + plot_G = false #true + + begin_serial_region() + if global_rank[] == 0 + @serial_region begin + @. G_err_global = abs(Gsp_global - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global): ",max_G_err) + if plot_G + @views heatmap(vperp.grid, vpa.grid, Gsp_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err_global[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end + end + end + + # check that the weights have been calculated properly by recalculating G + # using the looping and MPI arrangement of the main code + z_irank, z_nrank_per_group, z_comm, r_irank, r_nrank_per_group, r_comm = setup_distributed_memory_MPI(1,1,1,1) + # coord.n is the local number of points in each shared memory block + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=nvperp_global, vpa=nvpa_global, + vzeta=1, vr=1, vz=1) + Gsp_global_shared = allocate_shared_float(nvpa_global,nvperp_global) + # use precalculated weights to calculate Gsp using nodal values of fs + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Gsp_global_shared[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp_global + for ivpap in 1:nvpa_global + Gsp_global_shared[ivpa,ivperp] += G_weights_global[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end end end - end - begin_serial_region() - @serial_region begin - @. G_err_global = abs(Gsp_global_shared - G_Maxwell_global) - max_G_err = maximum(G_err_global) - println("max_G_err (global from redistributed weights): ",max_G_err) + begin_serial_region() + @serial_region begin + @. G_err_global = abs(Gsp_global_shared - G_Maxwell_global) + max_G_err = maximum(G_err_global) + println("max_G_err (global from redistributed weights): ",max_G_err) + end end # clean up MPI objects finalize_comms!() From 065d9cf5fe2b0233712f37526984455739eb9d06 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 11 Aug 2023 15:49:09 +0100 Subject: [PATCH 099/331] Using ArbNumerics to successfully improve integration accuracy of K(z)/sqrt(1-z) integrand to 10^-15 precision. ArbNumerics is only leveraged to improve accuracy of K(z), exp, log, and sqrt functions. Extra accuracy is not used in the Gauss Laguerre quadrature, which is still computed by FastGaussQuadrature. --- DivergingIntegrands_test_highprecision.jl | 166 ++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 DivergingIntegrands_test_highprecision.jl diff --git a/DivergingIntegrands_test_highprecision.jl b/DivergingIntegrands_test_highprecision.jl new file mode 100644 index 000000000..cf4ba9312 --- /dev/null +++ b/DivergingIntegrands_test_highprecision.jl @@ -0,0 +1,166 @@ +using FastGaussQuadrature +using SpecialFunctions: ellipk +using Printf +using ArbNumerics: sqrt, elliptic_k, ArbFloat, exp, log + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.3f ", matrix[i,j]) + end + println("") + end + println("\n") + end + + function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.16f ", vector[j]) + end + println("") + println("\n") + end + + # gauss laguerre test + ngrid = 25 + nelement = 1 + x, w = gausslaguerre(ngrid) + print_vector(x,"Gauss Laguerre x",ngrid) + print_vector(w,"Gauss Laguerre w",ngrid) + + # integrate 1/sqrt(y) from y = 0 to 1 + # use the Gauss-Laguerre quadrature to + # convert the diverging to a converging integrand + integrand = Array{ArbFloat,1}(undef,ngrid) + integrand_sqrty = Array{ArbFloat,1}(undef,ngrid) + integrand_Kz = Array{ArbFloat,1}(undef,ngrid) + value_Kz = Array{ArbFloat,1}(undef,ngrid) + integrand_Kz_sqrt = Array{ArbFloat,1}(undef,ngrid) + value_Kz_sqrt = Array{ArbFloat,1}(undef,ngrid) + integrand_Kz_sqrt_diff = Array{ArbFloat,1}(undef,ngrid) + value_Kz_sqrt_diff = Array{ArbFloat,1}(undef,ngrid) + value_y = Array{ArbFloat,1}(undef,ngrid) + value_z = Array{ArbFloat,1}(undef,ngrid) + + xx = Array{ArbFloat,1}(undef,ngrid) + ww = Array{ArbFloat,1}(undef,ngrid) + @. xx = ArbFloat(x) + @. ww = ArbFloat(w) + + L = ArbFloat(1.0) + for i in 1:ngrid + # change of variables + y = exp(-xx[i]/L) + # function to integrate in terms of y + integrand[i] = sqrt(1.0/y)*ww[i] + integrand_sqrty[i] = sqrt(y)*ww[i] + z = (ArbFloat(1.0) - y) + #z = (1.0 - y)/(1.0 + 10^-15) + #ellipk_z = ellipk(z) + ellipk_z = elliptic_k(z) + #if isnan(ellipk_z) || isinf(ellipk_z) + # ellipk_z = 0.0 + #end + sqrt_1_z = sqrt(ArbFloat(1.0) - z) + #if isinf(sqrt_1_z) || isinf(sqrt_1_z) + # sqrt_1_z = 1.0 + #end + integrand_Kz[i] = ellipk_z*ww[i] + value_Kz[i] = ellipk_z + integrand_Kz_sqrt[i] = ellipk_z*ww[i]/sqrt_1_z + value_Kz_sqrt[i] = ellipk_z/sqrt_1_z + value_Kz_sqrt_diff[i] = (ellipk_z - log(4.0) + log(sqrt_1_z))/sqrt_1_z + integrand_Kz_sqrt_diff[i] = value_Kz_sqrt_diff[i]*ww[i] + value_z[i] = z + end + #@. integrand *= w + + print_vector(integrand,"Gauss Laguerre integrand",ngrid) + + primitive = sum(integrand) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("1/sqrt(y): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + primitive = sum(integrand_sqrty) + primitive_exact = 2.0/3.0 + primitive_err = abs(primitive - primitive_exact) + println("sqrt(y): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + print_vector(integrand_Kz,"Kz integrand",ngrid) + print_vector(value_Kz,"Kz",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("K(z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + print_vector(integrand_Kz_sqrt,"Kz/sqrt(1-z) integrand",ngrid) + print_vector(value_Kz_sqrt,"Kz/sqrt(1-z)",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz_sqrt) + primitive_exact = pi^2/2.0 + primitive_err = abs(primitive - primitive_exact) + println("K(z)/sqrt(1-z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + print_vector(integrand_Kz_sqrt_diff,"(Kz - log 4/sqrt(1-z))/sqrt(1-z) integrand",ngrid) + print_vector(value_Kz_sqrt_diff,"(Kz - log 4/sqrt(1-z))/sqrt(1-z)",ngrid) + print_vector(value_z,"z",ngrid) + primitive = sum(integrand_Kz_sqrt_diff) + primitive_exact = (pi^2/2.0) - 2.0*(2.0*log(2.0) + 1.0) + primitive_err = abs(primitive - primitive_exact) + println("(K(z) - log(4/sqrt(1-z)))/sqrt(1-z): Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + if false + # gauss lobatto test + ngrid = 10 + nelement = 1 + x, w = gausslegendre(ngrid) + print_vector(x,"Gauss Legendre x",ngrid) + print_vector(w,"Gauss Legendre w",ngrid) + + b = 1.0 # upper limit on element + a = 0.0 # lower limit on element + U = 100.0 # proxy for infinity + L = 1.0 # scale factor + z = copy(x) + y = copy(x) + wy =copy(x) + scale = U*(b-a)/L + @. z = U*(x + 1) + @. y = exp(-z/L) + @. wy = w*exp(-z/L)*scale + # integrate 1/sqrt(y) from y = 0 to 1 + # use the Gauss-Laguerre quadrature to + # convert the diverging to a converging integrand + integrand = Array{Float64,1}(undef,ngrid) + integrand_sqrty = Array{Float64,1}(undef,ngrid) + L = 1.0 + for i in 1:ngrid + # function to integrate in terms of y + integrand[i] = sqrt(1.0/y[i])*wy[i]#*w[i]*exp(-z[i]/L)*scale + integrand_sqrty[i] = sqrt(y[i])*wy[i]#*w[i]*exp(-z[i]/L)*scale + end + #@. integrand *= w + + print_vector(w,"Gauss Legendre weights",ngrid) + print_vector(y,"Gauss Legendre coordinate",ngrid) + print_vector(integrand,"Gauss Legendre integrand",ngrid) + + primitive = sum(integrand) + primitive_exact = 2.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + + primitive = sum(integrand_sqrty) + primitive_exact = 2.0/3.0 + primitive_err = abs(primitive - primitive_exact) + println("Primitive: ",primitive," should be: ",primitive_exact," error: ",primitive_err) + end +end From 00d163c4e5a42261f3c167e087a89a4522858b81 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 11 Aug 2023 16:13:35 +0100 Subject: [PATCH 100/331] Cleaned version of fkpl testing script for trying higher precision floats. --- fkpl_high_precision.jl | 1352 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1352 insertions(+) create mode 100644 fkpl_high_precision.jl diff --git a/fkpl_high_precision.jl b/fkpl_high_precision.jl new file mode 100644 index 000000000..132907f4e --- /dev/null +++ b/fkpl_high_precision.jl @@ -0,0 +1,1352 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using SpecialFunctions: erf, ellipe, ellipk +using FastGaussQuadrature +using Dates +using LinearAlgebra: mul! +using ArbNumerics: ArbFloat, sqrt, elliptic_k, elliptic_e, exp + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.velocity_moments: integrate_over_vspace +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float + +function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth + return eta +end + +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end +function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 + H = 2.0/sqrt(pi) + else + # H_M = erf(eta)/eta + H = erf(eta)/eta + end + return H*dens/vth +end + +function pressure(ppar,pperp) + pres = (1.0/3.0)*(ppar + 2.0*pperp) + return pres +end + +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + +function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end +end +""" +L2norm assuming the input is the +absolution error ff_err = ff - ff_exact +We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) +where the volume of velocity space is finite +""" +function L2norm_vspace(ff_err,vpa,vperp) + ff_ones = copy(ff_err) + @. ff_ones = 1.0 + gg = copy(ff_err) + @. gg = (ff_err)^2 + num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + L2norm = sqrt(num/denom) + return L2norm +end + + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2Gdvpa2[:,ivperp] = vpa.scratch2 + end + end + + function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @. buffer_vpavperp[ivpa,:] = vperp.scratch + end + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) + @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2Gdvperp2[ivpa,:] = vperp.scratch2 + end + end + + function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) + @. dHdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) + @. dHdvperp[ivpa,:] = vperp.scratch + end + end + + function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + + # create the coordinate structs + #println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end + return vpa, vperp, vpa_spectral, vperp_spectral + end + + test_Lagrange_integral = false #true + test_Lagrange_integral_scan = true + + function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) + G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Gsp = allocate_shared_float(nvpa,nvperp) + d2Gspdvpa2 = allocate_shared_float(nvpa,nvperp) + dGspdvperp = allocate_shared_float(nvpa,nvperp) + d2Gspdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gspdvperp2 = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) + G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = allocate_shared_float(nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + + n_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + nsp = allocate_shared_float(nvpa,nvperp) + n_err = allocate_shared_float(nvpa,nvperp) + + H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Hsp = allocate_shared_float(nvpa,nvperp) + Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvperp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa = allocate_shared_float(nvpa,nvperp) + dHspdvperp = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = allocate_shared_float(nvpa,nvperp) + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) + + Cssp_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_div_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_div_err = allocate_shared_float(nvpa,nvperp) + Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vpa = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) + Cflux_vperp = allocate_shared_float(nvpa,nvperp) + Cflux_vperp_err = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # set up test Maxwellian + # species s + denss = 1.0 #3.0/4.0 + upars = 0.0 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + # species sp + denssp = 1.0 #3.0/4.0 + uparsp = 0.0 #2.0/3.0 + pparsp = 1.0 #2.0/3.0 + pperpsp = 1.0 #2.0/3.0 + pressp = get_pressure(pparsp,pperpsp) + msp = 1.0 + vthsp = get_vth(pressp,denssp,msp) + + nussp = 1.0 + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfspdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + denssp,uparsp,vthsp,msp, + nussp,vpa,vperp,ivpa,ivperp) + Cflux_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) + Cflux_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) + end + end + for ivperp in 1:nvperp + # s + @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @. dfsdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + # sp + @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) + @. dfspdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 + end + if vpa.discretization == "gausslegendre_pseudospectral" + @serial_region begin + println("use weak-form second derivative for vpa") + end + for ivperp in 1:nvperp + @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + @views second_derivative!(vpa.scratch2, fsp_in[:,ivperp], vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 + end + end + for ivpa in 1:vpa.n + # s + @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) + @. dfsdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fsdvperp2[ivpa,:] = vperp.scratch2 + # sp + @views derivative!(vperp.scratch, fsp_in[ivpa,:], vperp, vperp_spectral) + @. dfspdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fspdvperp2[ivpa,:] = vperp.scratch2 + end + for ivperp in 1:nvperp + # s + @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch + # sp + @views derivative!(vpa.scratch, dfspdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch + end + + # error analysis of distribution function + @serial_region begin + @. dfsdvpa_err = abs(dfsdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. d2fsdvpa2_err = abs(d2fsdvpa2 - d2fsdvpa2_Maxwell) + max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) + println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) + @. dfsdvperp_err = abs(dfsdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(d2fsdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + @. d2fsdvperp2_err = abs(d2fsdvperp2 - d2fsdvperp2_Maxwell) + max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) + println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) + + @. dfspdvpa_err = abs(dfspdvpa - dfspdvpa_Maxwell) + max_dfspdvpa_err = maximum(dfspdvpa_err) + @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) + max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) + println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) + @. dfspdvperp_err = abs(dfspdvperp - dfspdvperp_Maxwell) + max_dfspdvperp_err = maximum(dfspdvperp_err) + println("max_dfspdvperp_err: ",max_dfspdvperp_err) + @. d2fspdvperpdvpa_err = abs(d2fspdvperpdvpa - d2fspdvperpdvpa_Maxwell) + max_d2fspdvperpdvpa_err = maximum(d2fspdvperpdvpa_err) + println("max_d2fspdvperpdvpa_err: ",max_d2fspdvperpdvpa_err) + @. d2fspdvperp2_err = abs(d2fspdvperp2 - d2fspdvperp2_Maxwell) + max_d2fspdvperp2_err = maximum(d2fspdvperp2_err) + println("max_d2fspdvperp2_err: ",max_d2fspdvperp2_err) + end + function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax + end + + function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes + end + """ + Lagrange polynomial + args: + j - index of l_j from list of nodes + x_nodes - array of x node values + x - point where interpolated value is returned + """ + function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly + end + + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) + zero = 1.0e-10 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero # divergence at upper endpoint + nquad = size(x_laguerre,1) + @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) + @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre + nquad_coord = nquad + #println("upper divergence") + elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + nquad = size(x_laguerre,1) + for j in 1:nquad + x_scaled[nquad+1-j] = node_min + (node_max - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad+1-j] = (node_max - node_min)*w_laguerre[j] + end + nquad_coord = nquad + #println("lower divergence") + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + nquad = size(x_laguerre,1) + n = 2*nquad + # lower half of domain + for j in 1:nquad + x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) + w_scaled[j] = (coord_val - node_min)*w_laguerre[j] + end + # upper half of domain + for j in 1:nquad + x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) + w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] + end + nquad_coord = n + #println("intermediate divergence") + #else # no divergences + # nquad = size(x_legendre,1) + # shift = 0.5*(node_min + node_max) + # scale = 0.5*(node_max - node_min) + # @. x_scaled[1:nquad] = scale*x_legendre + shift + # @. w_scaled[1:nquad] = scale*w_legendre + # #println("no divergence") + # nquad_coord = nquad + end + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad_coord + end + + function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) + zero = 1.0e-6 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + nquad = size(x_legendre,1) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad + end + + # function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid + function ng_low(igrid,ngrid) + return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) + end + # function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid + function ng_hi(igrid,ngrid) + return floor(mk_int, igrid/ngrid) + end + # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 + function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) + end + # function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement + function nel_hi(ielement,nelement) + return 1- floor(mk_int, ielement/nelement) + end + + function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) + #mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (n_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing + end + + function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + + function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + + function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + + function loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + @. G1_weights[ivpa,ivperp,:,:] = 0.0 + @. G2_weights[ivpa,ivperp,:,:] = 0.0 + @. G3_weights[ivpa,ivperp,:,:] = 0.0 + @. H_weights[ivpa,ivperp,:,:] = 0.0 + @. H1_weights[ivpa,ivperp,:,:] = 0.0 + @. H2_weights[ivpa,ivperp,:,:] = 0.0 + @. H3_weights[ivpa,ivperp,:,:] = 0.0 + @. n_weights[ivpa,ivperp,:,:] = 0.0 + # loop over elements and grid points within elements on primed coordinate + loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + #loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + + #_block_synchronize() + begin_serial_region() + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + + begin_vperp_vpa_region() + + # use precalculated weights to calculate Gsp using nodal values of fs + @loop_vperp_vpa ivperp ivpa begin + #for ivperp in 1:nvperp + #for ivpa in 1:nvpa + d2Gspdvpa2[ivpa,ivperp] = 0.0 + dGspdvperp[ivpa,ivperp] = 0.0 + d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gspdvperp2[ivpa,ivperp] = 0.0 + Gsp[ivpa,ivperp] = 0.0 + Hsp[ivpa,ivperp] = 0.0 + dHspdvpa[ivpa,ivperp] = 0.0 + dHspdvperp[ivpa,ivperp] = 0.0 + nsp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end + end + #end + + (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + + d2Gspdvperp2[ivpa,ivperp] + + (1.0/vperp.grid[ivperp])*dGspdvperp[ivpa,ivperp])) + end + + begin_vperp_region() + @loop_vperp ivperp begin + @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) + @. dHspdvpa_from_Gsp[:,ivperp] = vpa.scratch + end + begin_vpa_region() + @loop_vpa ivpa begin + @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) + @. dHspdvperp_from_Gsp[ivpa,:] = vperp.scratch + end + + # evaluate collsion operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + # fully expanded form + (Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + + d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + + 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + + (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + + 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + + (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) + # collisional fluxes + ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = + calculate_collisional_fluxes(fs_in[ivpa,ivperp], + dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp], + d2Gspdvpa2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp], + d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], + ms,msp) ) + end + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_numerical = 0.0 + begin_vperp_region() + @loop_vperp ivperp begin + @views mul!(vpa.scratch,vpa_spectral.S_matrix,Cflux_vpa[:,ivperp]) + gausslegendre_mass_matrix_solve!(vpa.scratch2,vpa.scratch,vpa_spectral) + Cssp_div_numerical[:,ivperp] += vpa.scratch2 + end + begin_vpa_region() + @loop_vpa ivpa begin + @views mul!(vperp.scratch,vperp_spectral.S_matrix,Cflux_vperp[ivpa,:]) + gausslegendre_mass_matrix_solve!(vperp.scratch2,vperp.scratch,vperp_spectral) + Cssp_div_numerical[ivpa,:] += vperp.scratch2 + end + @. Cssp_div_numerical *= nussp + end + + plot_H = false #true + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + plot_G = false #true + plot_C = false #true + plot_n = false #true + + begin_serial_region() + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. n_err = abs(nsp - denssp) + max_n_err = maximum(n_err) + println("max_n_err: ",max_n_err) + println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) + L2_n_err = L2norm_vspace(n_err,vpa,vperp) + println("L2_n_err: ",L2_n_err) + @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) + max_C_err = maximum(Cssp_err) + max_C_Maxwell_val = maximum(Cssp_Maxwell) + max_C_numerical_val = maximum(Cssp_numerical) + println("max_C_err: ",max_C_err) + L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) + println("L2_C_err: ",L2_C_err) + println("max_C_Maxwell_val: ",max_C_Maxwell_val) + println("max_C_numerical_val: ",max_C_numerical_val) + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_err = abs(Cssp_div_numerical - Cssp_Maxwell) + max_C_div_err = maximum(Cssp_div_err) + println("max_C_div_err: ",max_C_div_err) + end + @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) + max_Cflux_vpa_err = maximum(Cflux_vpa_err) + println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) + @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) + max_Cflux_vperp_err = maximum(Cflux_vperp_err) + println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) + @. H_err = abs(Hsp_from_Gsp - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_from_G_err: ",max_H_err) + @. dHdvperp_err = abs(dHspdvperp_from_Gsp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err (from G): ",max_dHdvperp_err) + @. dHdvpa_err = abs(dHspdvpa_from_Gsp - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err (from G): ",max_dHdvpa_err) + @. H_err = abs(Hsp - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_err: ",max_H_err) + println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) + L2_H_err = L2norm_vspace(H_err,vpa,vperp) + println("L2_H_err: ",L2_H_err) + @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) + L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) + println("L2_dHdvperp_err: ",L2_dHdvperp_err) + @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) + L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) + println("L2_dHdvpa_err: ",L2_dHdvpa_err) + + if plot_n + @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, n_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_err.pdf") + savefig(outfile) + end + if plot_C + @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_err.pdf") + savefig(outfile) + end + if plot_H + @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Hsp_from_Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_from_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_err.pdf") + savefig(outfile) + end + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) + end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) + L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) + println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) + end + @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) + L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) + println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) + end + @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) + max_dGdvperp_err = maximum(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) + L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) + println("L2_dGdvperp_err: ",L2_dGdvperp_err) + if plot_dGdvperp + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) + L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) + println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) + if plot_d2Gdvpa2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end + @. G_err = abs(Gsp - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) + L2_G_err = L2norm_vspace(G_err,vpa,vperp) + println("L2_G_err: ",L2_G_err) + if plot_G + @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end + end + _block_synchronize() + if standalone + finalize_comms!() + end + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + (results = (maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), + maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + L2norm_vspace(Cssp_err,vpa,vperp), L2norm_vspace(G_err,vpa,vperp), L2norm_vspace(H_err,vpa,vperp), L2norm_vspace(dHdvpa_err,vpa,vperp), + L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), + L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), + maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), + maximum(n_err), L2norm_vspace(n_err,vpa,vperp) )) + return results + end + + if test_Lagrange_integral + ngrid = 9 + nelement = 4 + test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) + end + if test_Lagrange_integral_scan + initialize_comms!() + ngrid = 5 + nscan = 1 + #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + #nelement_list = Int[2, 4, 8] + nelement_list = Int[2] + max_C_err = Array{mk_float,1}(undef,nscan) + max_Gvpa_err = Array{mk_float,1}(undef,nscan) + max_Gvperp_err = Array{mk_float,1}(undef,nscan) + max_G_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) + max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + max_dfspdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_C_err = Array{mk_float,1}(undef,nscan) + L2_G_err = Array{mk_float,1}(undef,nscan) + L2_H_err = Array{mk_float,1}(undef,nscan) + L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_n_err = Array{mk_float,1}(undef,nscan) + L2_n_err = Array{mk_float,1}(undef,nscan) + + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" + + for iscan in 1:nscan + local nelement = nelement_list[iscan] + ((max_C_err[iscan], max_Gvpa_err[iscan], max_Gvperp_err[iscan], + max_G_err[iscan], max_H_err[iscan], + max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], + max_dGdvperp_err[iscan], L2_C_err[iscan], + L2_G_err[iscan], L2_H_err[iscan], L2_dHdvpa_err[iscan], + L2_dHdvperp_err[iscan], L2_d2Gdvperp2_err[iscan], L2_d2Gdvpa2_err[iscan], + L2_d2Gdvperpdvpa_err[iscan], L2_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], + max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], + max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], + max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], + max_n_err[iscan], L2_n_err[iscan]) + = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) + end + if global_rank[]==0 + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + nlabel = L"\epsilon(n)" + Clabel = L"\epsilon(C)" + Gvpalabel = L"\epsilon(\Gamma_{\|\|})" + Gvperplabel = L"\epsilon(\Gamma_{\perp})" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" + dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" + d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" + d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" + d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" + plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], + xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfspdvperp_label = L"\epsilon(d F_{s^\prime} /d v_{\perp})" + d2fspdvpa2_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\|\|}^2)" + d2fspdvperpdvpa_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\perp}d v_{\|\|})" + d2fspdvperp2_label = L"\epsilon(d^2 F_{s^\prime}/ d v_{\perp}^2)" + plot(nelement_list, [max_dfspdvperp_err,max_d2fspdvpa2_err,max_d2fspdvperpdvpa_err,max_d2fspdvperp2_err,expected], + xlabel=xlabel, label=[dfspdvperp_label d2fspdvpa2_label d2fspdvperpdvpa_label d2fspdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fsp_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + end + finalize_comms!() + end + +end From 2be0344d33ff716a6937f7fead31540618fca998 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 11 Aug 2023 17:18:17 +0100 Subject: [PATCH 101/331] First attempt to use ArbNumerics to increase accuracy of computed elliptic integrals, sqrt and exp functions in the potential integrands. A significant slowdown is observed in the standard quick test with ngrid = 5 and nelement = 2, from a compute time of order seconds to a compute time of order minutes. --- fkpl_high_precision.jl | 56 ++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/fkpl_high_precision.jl b/fkpl_high_precision.jl index 132907f4e..3a7219d20 100644 --- a/fkpl_high_precision.jl +++ b/fkpl_high_precision.jl @@ -544,8 +544,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. w_scaled = 0.0 #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) nquad = size(x_legendre,1) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) + shift = ArbFloat(0.5)*(node_min + node_max) + scale = ArbFloat(0.5)*(node_max - node_min) @. x_scaled[1:nquad] = scale*x_legendre + shift @. w_scaled[1:nquad] = scale*w_legendre #println("x_scaled",x_scaled) @@ -589,12 +589,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - #mm = 4.0*vperp_val*x_kvperp/denom + mm = ArbFloat(4.0)*vperp_val*x_kvperp/denom prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) + ellipe_mm = elliptic_e(mm) + ellipk_mm = elliptic_k(mm) + #ellipe_mm = ellipe(mm) + #ellipk_mm = ellipk(mm) #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end @@ -660,7 +662,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + vpa_min, vpa_max = ArbFloat(vpa_nodes[1]), ArbFloat(vpa_nodes[end]) nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -673,7 +675,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + vpa_min, vpa_max = ArbFloat(vpa_nodes[1]), ArbFloat(vpa_nodes[end]) #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, @@ -686,7 +688,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + vpa_min, vpa_max = ArbFloat(vpa_nodes[1]), ArbFloat(vpa_nodes[end]) nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -708,7 +710,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vpap in 1:vpa.nelement_local # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + vpa_min, vpa_max = ArbFloat(vpa_nodes[1]), ArbFloat(vpa_nodes[end]) nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -730,8 +732,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vperpp in 1:ielement_vperp_low-1 vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + vperp_max = ArbFloat(vperp_nodes[end]) + vperp_min = ArbFloat(vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local)) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -743,8 +745,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + vperp_max = ArbFloat(vperp_nodes[end]) + vperp_min = ArbFloat(vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local)) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, @@ -757,8 +759,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + vperp_max = ArbFloat(vperp_nodes[end]) + vperp_min = ArbFloat(vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local)) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -778,8 +780,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:vperp.nelement_local vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + vperp_max = ArbFloat(vperp_nodes[end]) + vperp_min = ArbFloat(vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp)) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, @@ -797,14 +799,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ # get Gauss-Legendre points and weights on (-1,1) nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) + xx_legendre, ww_legendre = gausslegendre(nquad) #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + xx_laguerre, ww_laguerre = gausslaguerre(nlaguerre) + x_legendre, w_legendre = Array{ArbFloat,1}(undef,nquad), Array{ArbFloat,1}(undef,nquad) + @. x_legendre = ArbFloat(xx_legendre) + @. w_legendre = ArbFloat(ww_legendre) + x_laguerre, w_laguerre = Array{ArbFloat,1}(undef,nlaguerre), Array{ArbFloat,1}(undef,nlaguerre) + @. x_laguerre = ArbFloat(xx_laguerre) + @. w_laguerre = ArbFloat(ww_laguerre) #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vpa, w_vpa = Array{ArbFloat,1}(undef,2*nquad), Array{ArbFloat,1}(undef,2*nquad) + x_vperp, w_vperp = Array{ArbFloat,1}(undef,2*nquad), Array{ArbFloat,1}(undef,2*nquad) @serial_region begin @@ -826,8 +834,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] + vperp_val = ArbFloat(vperp.grid[ivperp]) + vpa_val = ArbFloat(vpa.grid[ivpa]) @. G_weights[ivpa,ivperp,:,:] = 0.0 @. G1_weights[ivpa,ivperp,:,:] = 0.0 @. G2_weights[ivpa,ivperp,:,:] = 0.0 From dc5844d4509e402a4accf8444e83c22147c9306f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 14 Aug 2023 13:46:59 +0100 Subject: [PATCH 102/331] test script before cutting out loops over field points --- fkpl_single_field_point_test.jl | 1350 +++++++++++++++++++++++++++++++ 1 file changed, 1350 insertions(+) create mode 100644 fkpl_single_field_point_test.jl diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl new file mode 100644 index 000000000..275492ceb --- /dev/null +++ b/fkpl_single_field_point_test.jl @@ -0,0 +1,1350 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using SpecialFunctions: erf, ellipe, ellipk +using FastGaussQuadrature +using Dates +using LinearAlgebra: mul! + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.velocity_moments: integrate_over_vspace +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float + +function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth + return eta +end + +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end +function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 + H = 2.0/sqrt(pi) + else + # H_M = erf(eta)/eta + H = erf(eta)/eta + end + return H*dens/vth +end + +function pressure(ppar,pperp) + pres = (1.0/3.0)*(ppar + 2.0*pperp) + return pres +end + +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + +function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end +end +""" +L2norm assuming the input is the +absolution error ff_err = ff - ff_exact +We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) +where the volume of velocity space is finite +""" +function L2norm_vspace(ff_err,vpa,vperp) + ff_ones = copy(ff_err) + @. ff_ones = 1.0 + gg = copy(ff_err) + @. gg = (ff_err)^2 + num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + L2norm = sqrt(num/denom) + return L2norm +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2Gdvpa2[:,ivperp] = vpa.scratch2 + end + end + + function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @. buffer_vpavperp[ivpa,:] = vperp.scratch + end + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) + @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2Gdvperp2[ivpa,:] = vperp.scratch2 + end + end + + function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) + @. dHdvpa[:,ivperp] = vpa.scratch + end + end + + function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) + @. dHdvperp[ivpa,:] = vperp.scratch + end + end + + function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + + # create the coordinate structs + #println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end + return vpa, vperp, vpa_spectral, vperp_spectral + end + + test_Lagrange_integral = false #true + test_Lagrange_integral_scan = true + + function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) + G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Gsp = allocate_shared_float(nvpa,nvperp) + d2Gspdvpa2 = allocate_shared_float(nvpa,nvperp) + dGspdvperp = allocate_shared_float(nvpa,nvperp) + d2Gspdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gspdvperp2 = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) + G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + G_err = allocate_shared_float(nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + + n_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + nsp = allocate_shared_float(nvpa,nvperp) + n_err = allocate_shared_float(nvpa,nvperp) + + H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + Hsp = allocate_shared_float(nvpa,nvperp) + Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvperp_from_Gsp = allocate_shared_float(nvpa,nvperp) + dHspdvpa = allocate_shared_float(nvpa,nvperp) + dHspdvperp = allocate_shared_float(nvpa,nvperp) + #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + H_err = allocate_shared_float(nvpa,nvperp) + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) + + Cssp_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_div_numerical = allocate_shared_float(nvpa,nvperp) + Cssp_div_err = allocate_shared_float(nvpa,nvperp) + Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vpa = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) + Cflux_vperp = allocate_shared_float(nvpa,nvperp) + Cflux_vperp_err = allocate_shared_float(nvpa,nvperp) + Cflux_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + Cflux_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # set up test Maxwellian + # species s + denss = 1.0 #3.0/4.0 + upars = 0.0 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + # species sp + denssp = 1.0 #3.0/4.0 + uparsp = 0.0 #2.0/3.0 + pparsp = 1.0 #2.0/3.0 + pperpsp = 1.0 #2.0/3.0 + pressp = get_pressure(pparsp,pperpsp) + msp = 1.0 + vthsp = get_vth(pressp,denssp,msp) + + nussp = 1.0 + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfspdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + denssp,uparsp,vthsp,msp, + nussp,vpa,vperp,ivpa,ivperp) + Cflux_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) + Cflux_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(ms,denss,upars,vths, + msp,denssp,uparsp,vthsp, + vpa,vperp,ivpa,ivperp) + end + end + for ivperp in 1:nvperp + # s + @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) + @. dfsdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + # sp + @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) + @. dfspdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 + end + if vpa.discretization == "gausslegendre_pseudospectral" + @serial_region begin + println("use weak-form second derivative for vpa") + end + for ivperp in 1:nvperp + @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) + @. d2fsdvpa2[:,ivperp] = vpa.scratch2 + @views second_derivative!(vpa.scratch2, fsp_in[:,ivperp], vpa, vpa_spectral) + @. d2fspdvpa2[:,ivperp] = vpa.scratch2 + end + end + for ivpa in 1:vpa.n + # s + @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) + @. dfsdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fsdvperp2[ivpa,:] = vperp.scratch2 + # sp + @views derivative!(vperp.scratch, fsp_in[ivpa,:], vperp, vperp_spectral) + @. dfspdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fspdvperp2[ivpa,:] = vperp.scratch2 + end + for ivperp in 1:nvperp + # s + @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch + # sp + @views derivative!(vpa.scratch, dfspdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch + end + + # error analysis of distribution function + @serial_region begin + @. dfsdvpa_err = abs(dfsdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. d2fsdvpa2_err = abs(d2fsdvpa2 - d2fsdvpa2_Maxwell) + max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) + println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) + @. dfsdvperp_err = abs(dfsdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(d2fsdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + @. d2fsdvperp2_err = abs(d2fsdvperp2 - d2fsdvperp2_Maxwell) + max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) + println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) + + @. dfspdvpa_err = abs(dfspdvpa - dfspdvpa_Maxwell) + max_dfspdvpa_err = maximum(dfspdvpa_err) + @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) + max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) + println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) + @. dfspdvperp_err = abs(dfspdvperp - dfspdvperp_Maxwell) + max_dfspdvperp_err = maximum(dfspdvperp_err) + println("max_dfspdvperp_err: ",max_dfspdvperp_err) + @. d2fspdvperpdvpa_err = abs(d2fspdvperpdvpa - d2fspdvperpdvpa_Maxwell) + max_d2fspdvperpdvpa_err = maximum(d2fspdvperpdvpa_err) + println("max_d2fspdvperpdvpa_err: ",max_d2fspdvperpdvpa_err) + @. d2fspdvperp2_err = abs(d2fspdvperp2 - d2fspdvperp2_Maxwell) + max_d2fspdvperp2_err = maximum(d2fspdvperp2_err) + println("max_d2fspdvperp2_err: ",max_d2fspdvperp2_err) + end + function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax + end + + function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes + end + """ + Lagrange polynomial + args: + j - index of l_j from list of nodes + x_nodes - array of x node values + x - point where interpolated value is returned + """ + function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly + end + + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) + zero = 1.0e-10 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero # divergence at upper endpoint + nquad = size(x_laguerre,1) + @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) + @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre + nquad_coord = nquad + #println("upper divergence") + elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + nquad = size(x_laguerre,1) + for j in 1:nquad + x_scaled[nquad+1-j] = node_min + (node_max - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad+1-j] = (node_max - node_min)*w_laguerre[j] + end + nquad_coord = nquad + #println("lower divergence") + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + nquad = size(x_laguerre,1) + n = 2*nquad + # lower half of domain + for j in 1:nquad + x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) + w_scaled[j] = (coord_val - node_min)*w_laguerre[j] + end + # upper half of domain + for j in 1:nquad + x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) + w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] + end + nquad_coord = n + #println("intermediate divergence") + #else # no divergences + # nquad = size(x_legendre,1) + # shift = 0.5*(node_min + node_max) + # scale = 0.5*(node_max - node_min) + # @. x_scaled[1:nquad] = scale*x_legendre + shift + # @. w_scaled[1:nquad] = scale*w_legendre + # #println("no divergence") + # nquad_coord = nquad + end + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad_coord + end + + function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) + zero = 1.0e-6 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + nquad = size(x_legendre,1) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad + end + + # function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid + function ng_low(igrid,ngrid) + return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) + end + # function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid + function ng_hi(igrid,ngrid) + return floor(mk_int, igrid/ngrid) + end + # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 + function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) + end + # function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement + function nel_hi(ielement,nelement) + return 1- floor(mk_int, ielement/nelement) + end + + function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) + #mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (n_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing + end + + function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + + function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing + end + + function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + + function loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing + end + + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + @. G_weights[ivpa,ivperp,:,:] = 0.0 + @. G1_weights[ivpa,ivperp,:,:] = 0.0 + @. G2_weights[ivpa,ivperp,:,:] = 0.0 + @. G3_weights[ivpa,ivperp,:,:] = 0.0 + @. H_weights[ivpa,ivperp,:,:] = 0.0 + @. H1_weights[ivpa,ivperp,:,:] = 0.0 + @. H2_weights[ivpa,ivperp,:,:] = 0.0 + @. H3_weights[ivpa,ivperp,:,:] = 0.0 + @. n_weights[ivpa,ivperp,:,:] = 0.0 + # loop over elements and grid points within elements on primed coordinate + #loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) + end + + #_block_synchronize() + begin_serial_region() + @serial_region begin + println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) + end + + begin_vperp_vpa_region() + + # use precalculated weights to calculate Gsp using nodal values of fs + @loop_vperp_vpa ivperp ivpa begin + #for ivperp in 1:nvperp + #for ivpa in 1:nvpa + d2Gspdvpa2[ivpa,ivperp] = 0.0 + dGspdvperp[ivpa,ivperp] = 0.0 + d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gspdvperp2[ivpa,ivperp] = 0.0 + Gsp[ivpa,ivperp] = 0.0 + Hsp[ivpa,ivperp] = 0.0 + dHspdvpa[ivpa,ivperp] = 0.0 + dHspdvperp[ivpa,ivperp] = 0.0 + nsp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + end + end + #end + + (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + + d2Gspdvperp2[ivpa,ivperp] + + (1.0/vperp.grid[ivperp])*dGspdvperp[ivpa,ivperp])) + end + + begin_vperp_region() + @loop_vperp ivperp begin + @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) + @. dHspdvpa_from_Gsp[:,ivperp] = vpa.scratch + end + begin_vpa_region() + @loop_vpa ivpa begin + @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) + @. dHspdvperp_from_Gsp[ivpa,:] = vperp.scratch + end + + # evaluate collsion operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + # fully expanded form + (Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + + d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + + 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + + (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + + 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + + (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) + # collisional fluxes + ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = + calculate_collisional_fluxes(fs_in[ivpa,ivperp], + dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp], + d2Gspdvpa2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp], + d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], + ms,msp) ) + end + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_numerical = 0.0 + begin_vperp_region() + @loop_vperp ivperp begin + @views mul!(vpa.scratch,vpa_spectral.S_matrix,Cflux_vpa[:,ivperp]) + gausslegendre_mass_matrix_solve!(vpa.scratch2,vpa.scratch,vpa_spectral) + Cssp_div_numerical[:,ivperp] += vpa.scratch2 + end + begin_vpa_region() + @loop_vpa ivpa begin + @views mul!(vperp.scratch,vperp_spectral.S_matrix,Cflux_vperp[ivpa,:]) + gausslegendre_mass_matrix_solve!(vperp.scratch2,vperp.scratch,vperp_spectral) + Cssp_div_numerical[ivpa,:] += vperp.scratch2 + end + @. Cssp_div_numerical *= nussp + end + + plot_H = false #true + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + plot_G = false #true + plot_C = false #true + plot_n = false #true + + begin_serial_region() + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. n_err = abs(nsp - denssp) + max_n_err = maximum(n_err) + println("max_n_err: ",max_n_err) + println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) + L2_n_err = L2norm_vspace(n_err,vpa,vperp) + println("L2_n_err: ",L2_n_err) + @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) + max_C_err = maximum(Cssp_err) + max_C_Maxwell_val = maximum(Cssp_Maxwell) + max_C_numerical_val = maximum(Cssp_numerical) + println("max_C_err: ",max_C_err) + L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) + println("L2_C_err: ",L2_C_err) + println("max_C_Maxwell_val: ",max_C_Maxwell_val) + println("max_C_numerical_val: ",max_C_numerical_val) + if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" + @. Cssp_div_err = abs(Cssp_div_numerical - Cssp_Maxwell) + max_C_div_err = maximum(Cssp_div_err) + println("max_C_div_err: ",max_C_div_err) + end + @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) + max_Cflux_vpa_err = maximum(Cflux_vpa_err) + println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) + @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) + max_Cflux_vperp_err = maximum(Cflux_vperp_err) + println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) + @. H_err = abs(Hsp_from_Gsp - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_from_G_err: ",max_H_err) + @. dHdvperp_err = abs(dHspdvperp_from_Gsp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err (from G): ",max_dHdvperp_err) + @. dHdvpa_err = abs(dHspdvpa_from_Gsp - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err (from G): ",max_dHdvpa_err) + @. H_err = abs(Hsp - H_Maxwell) + max_H_err = maximum(H_err) + println("max_H_err: ",max_H_err) + println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) + L2_H_err = L2norm_vspace(H_err,vpa,vperp) + println("L2_H_err: ",L2_H_err) + @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) + max_dHdvperp_err = maximum(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) + L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) + println("L2_dHdvperp_err: ",L2_dHdvperp_err) + @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) + max_dHdvpa_err = maximum(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) + L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) + println("L2_dHdvpa_err: ",L2_dHdvpa_err) + + if plot_n + @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, n_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_n_err.pdf") + savefig(outfile) + end + if plot_C + @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Cssp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_C_err.pdf") + savefig(outfile) + end + if plot_H + @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, Hsp_from_Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_from_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_H_err.pdf") + savefig(outfile) + end + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) + end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) + L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) + println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) + end + @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) + L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) + println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) + end + @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) + max_dGdvperp_err = maximum(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) + L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) + println("L2_dGdvperp_err: ",L2_dGdvperp_err) + if plot_dGdvperp + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) + L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) + println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) + if plot_d2Gdvpa2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end + @. G_err = abs(Gsp - G_Maxwell) + max_G_err = maximum(G_err) + println("max_G_err: ",max_G_err) + println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) + L2_G_err = L2norm_vspace(G_err,vpa,vperp) + println("L2_G_err: ",L2_G_err) + if plot_G + @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_G_err.pdf") + savefig(outfile) + end + end + _block_synchronize() + if standalone + finalize_comms!() + end + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + (results = (maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), + maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + L2norm_vspace(Cssp_err,vpa,vperp), L2norm_vspace(G_err,vpa,vperp), L2norm_vspace(H_err,vpa,vperp), L2norm_vspace(dHdvpa_err,vpa,vperp), + L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), + L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), + maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), + maximum(n_err), L2norm_vspace(n_err,vpa,vperp) )) + return results + end + + if test_Lagrange_integral + ngrid = 9 + nelement = 4 + test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) + end + if test_Lagrange_integral_scan + initialize_comms!() + ngrid = 5 + nscan = 1 + #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + #nelement_list = Int[2, 4, 8] + nelement_list = Int[2] + max_C_err = Array{mk_float,1}(undef,nscan) + max_Gvpa_err = Array{mk_float,1}(undef,nscan) + max_Gvperp_err = Array{mk_float,1}(undef,nscan) + max_G_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) + max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + max_dfspdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_C_err = Array{mk_float,1}(undef,nscan) + L2_G_err = Array{mk_float,1}(undef,nscan) + L2_H_err = Array{mk_float,1}(undef,nscan) + L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_n_err = Array{mk_float,1}(undef,nscan) + L2_n_err = Array{mk_float,1}(undef,nscan) + + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" + + for iscan in 1:nscan + local nelement = nelement_list[iscan] + ((max_C_err[iscan], max_Gvpa_err[iscan], max_Gvperp_err[iscan], + max_G_err[iscan], max_H_err[iscan], + max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], + max_dGdvperp_err[iscan], L2_C_err[iscan], + L2_G_err[iscan], L2_H_err[iscan], L2_dHdvpa_err[iscan], + L2_dHdvperp_err[iscan], L2_d2Gdvperp2_err[iscan], L2_d2Gdvpa2_err[iscan], + L2_d2Gdvperpdvpa_err[iscan], L2_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], + max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], + max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], + max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], + max_n_err[iscan], L2_n_err[iscan]) + = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) + end + if global_rank[]==0 + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + nlabel = L"\epsilon(n)" + Clabel = L"\epsilon(C)" + Gvpalabel = L"\epsilon(\Gamma_{\|\|})" + Gvperplabel = L"\epsilon(\Gamma_{\perp})" + Glabel = L"\epsilon(G)" + Hlabel = L"\epsilon(H)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected,expected_integral], + xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" + dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" + d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" + d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" + d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" + plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], + xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfspdvperp_label = L"\epsilon(d F_{s^\prime} /d v_{\perp})" + d2fspdvpa2_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\|\|}^2)" + d2fspdvperpdvpa_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\perp}d v_{\|\|})" + d2fspdvperp2_label = L"\epsilon(d^2 F_{s^\prime}/ d v_{\perp}^2)" + plot(nelement_list, [max_dfspdvperp_err,max_d2fspdvpa2_err,max_d2fspdvperpdvpa_err,max_d2fspdvperp2_err,expected], + xlabel=xlabel, label=[dfspdvperp_label d2fspdvpa2_label d2fspdvperpdvpa_label d2fspdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fsp_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + end + finalize_comms!() + end + +end From 89d0ff2bf1c30794d69cefa6e0db2064a2ff670c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 14 Aug 2023 14:59:20 +0100 Subject: [PATCH 103/331] Script for testing integration weights for field vpa and vperp ~= 0. By looking at a single field location we can speed up testing, at the cost of not seeing the largest possible errors over all vpa and vperp. --- fkpl_single_field_point_test.jl | 399 ++++++++++---------------------- 1 file changed, 124 insertions(+), 275 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 275492ceb..a171475f2 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -203,6 +203,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up necessary inputs for collision operator functions nvperp = vperp.n nvpa = vpa.n + ivpa_field = floor(mk_int,nvpa/2) + ivperp_field = 1 #floor(mk_int,nvperp/6) + println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI if standalone @@ -253,60 +256,47 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) - G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Gsp = allocate_shared_float(nvpa,nvperp) - d2Gspdvpa2 = allocate_shared_float(nvpa,nvperp) - dGspdvperp = allocate_shared_float(nvpa,nvperp) - d2Gspdvperpdvpa = allocate_shared_float(nvpa,nvperp) - d2Gspdvperp2 = allocate_shared_float(nvpa,nvperp) - #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) - G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - G_err = allocate_shared_float(nvpa,nvperp) - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) - dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dGdvperp_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + G_weights = allocate_shared_float(1,1,nvpa,nvperp) + G1_weights = allocate_shared_float(1,1,nvpa,nvperp) + G2_weights = allocate_shared_float(1,1,nvpa,nvperp) + G3_weights = allocate_shared_float(1,1,nvpa,nvperp) + Gsp = allocate_shared_float(1,1) + d2Gspdvpa2 = allocate_shared_float(1,1) + dGspdvperp = allocate_shared_float(1,1) + d2Gspdvperpdvpa = allocate_shared_float(1,1) + d2Gspdvperp2 = allocate_shared_float(1,1) + #Gsp = Array{mk_float,2}(undef,1,1) + G_Maxwell = Array{mk_float,2}(undef,1,1) + G_err = allocate_shared_float(1,1) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,1,1) + d2Gdvpa2_err = allocate_shared_float(1,1) + dGdvperp_Maxwell = Array{mk_float,2}(undef,1,1) + dGdvperp_err = allocate_shared_float(1,1) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,1,1) + d2Gdvperpdvpa_err = allocate_shared_float(1,1) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,1,1) + d2Gdvperp2_err = allocate_shared_float(1,1) - n_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - nsp = allocate_shared_float(nvpa,nvperp) - n_err = allocate_shared_float(nvpa,nvperp) + n_weights = allocate_shared_float(1,1,nvpa,nvperp) + nsp = allocate_shared_float(1,1) + n_err = allocate_shared_float(1,1) - H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Hsp = allocate_shared_float(nvpa,nvperp) - Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvperp_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvpa = allocate_shared_float(nvpa,nvperp) - dHspdvperp = allocate_shared_float(nvpa,nvperp) + H_weights = allocate_shared_float(1,1,nvpa,nvperp) + H1_weights = allocate_shared_float(1,1,nvpa,nvperp) + H2_weights = allocate_shared_float(1,1,nvpa,nvperp) + H3_weights = allocate_shared_float(1,1,nvpa,nvperp) + Hsp_from_Gsp = allocate_shared_float(1,1) + Hsp = allocate_shared_float(1,1) + dHspdvpa = allocate_shared_float(1,1) + dHspdvperp = allocate_shared_float(1,1) #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) - H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_err = allocate_shared_float(nvpa,nvperp) - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = allocate_shared_float(nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = allocate_shared_float(nvpa,nvperp) + H_Maxwell = Array{mk_float,2}(undef,1,1) + H_err = allocate_shared_float(1,1) + dHdvpa_Maxwell = Array{mk_float,2}(undef,1,1) + dHdvpa_err = allocate_shared_float(1,1) + dHdvperp_Maxwell = Array{mk_float,2}(undef,1,1) + dHdvperp_err = allocate_shared_float(1,1) - Cssp_numerical = allocate_shared_float(nvpa,nvperp) - Cssp_err = allocate_shared_float(nvpa,nvperp) - Cssp_div_numerical = allocate_shared_float(nvpa,nvperp) - Cssp_div_err = allocate_shared_float(nvpa,nvperp) - Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Cflux_vpa = allocate_shared_float(nvpa,nvperp) - Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) - Cflux_vperp = allocate_shared_float(nvpa,nvperp) - Cflux_vperp_err = allocate_shared_float(nvpa,nvperp) - Cflux_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Cflux_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) @serial_region begin println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) @@ -346,27 +336,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - denssp,uparsp,vthsp,msp, - nussp,vpa,vperp,ivpa,ivperp) - Cflux_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(ms,denss,upars,vths, - msp,denssp,uparsp,vthsp, - vpa,vperp,ivpa,ivperp) - Cflux_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(ms,denss,upars,vths, - msp,denssp,uparsp,vthsp, - vpa,vperp,ivpa,ivperp) end end + + ivpa = ivpa_field + ivperp = ivperp_field + G_Maxwell[1,1] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + H_Maxwell[1,1] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[1,1] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[1,1] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[1,1] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[1,1] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[1,1] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[1,1] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + for ivperp in 1:nvperp # s @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) @@ -606,40 +589,40 @@ if abspath(PROGRAM_FILE) == @__FILE__ lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - (G_weights[ivpa,ivperp,ivpap,ivperpp] += + (G_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + (G1_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (G2_weights[ivpa,ivperp,ivpap,ivperpp] += + (G2_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (G3_weights[ivpa,ivperp,ivpap,ivperpp] += + (G3_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H_weights[ivpa,ivperp,ivpap,ivperpp] += + (H_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + (H1_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + (H2_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + (H3_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*(vpa_val - x_kvpa)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (n_weights[ivpa,ivperp,ivpap,ivperpp] += + (n_weights[1,1,ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end @@ -812,8 +795,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid # precalculated weights, integrating over Lagrange polynomials - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin + #begin_vperp_vpa_region() + #@loop_vperp_vpa ivperp ivpa begin + ivpa = ivpa_field + ivperp = ivperp_field #limits where checks required to determine which divergence-safe grid is needed igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) @@ -826,15 +811,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] - @. G_weights[ivpa,ivperp,:,:] = 0.0 - @. G1_weights[ivpa,ivperp,:,:] = 0.0 - @. G2_weights[ivpa,ivperp,:,:] = 0.0 - @. G3_weights[ivpa,ivperp,:,:] = 0.0 - @. H_weights[ivpa,ivperp,:,:] = 0.0 - @. H1_weights[ivpa,ivperp,:,:] = 0.0 - @. H2_weights[ivpa,ivperp,:,:] = 0.0 - @. H3_weights[ivpa,ivperp,:,:] = 0.0 - @. n_weights[ivpa,ivperp,:,:] = 0.0 + @. G_weights[1,1,:,:] = 0.0 + @. G1_weights[1,1,:,:] = 0.0 + @. G2_weights[1,1,:,:] = 0.0 + @. G3_weights[1,1,:,:] = 0.0 + @. H_weights[1,1,:,:] = 0.0 + @. H1_weights[1,1,:,:] = 0.0 + @. H2_weights[1,1,:,:] = 0.0 + @. H3_weights[1,1,:,:] = 0.0 + @. n_weights[1,1,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate #loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, @@ -843,7 +828,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids vpa_val, vperp_val, ivpa, ivperp) - end + #end #_block_synchronize() begin_serial_region() @@ -854,85 +839,39 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_vperp_vpa_region() # use precalculated weights to calculate Gsp using nodal values of fs - @loop_vperp_vpa ivperp ivpa begin + #@loop_vperp_vpa ivperp ivpa begin #for ivperp in 1:nvperp #for ivpa in 1:nvpa - d2Gspdvpa2[ivpa,ivperp] = 0.0 - dGspdvperp[ivpa,ivperp] = 0.0 - d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 - d2Gspdvperp2[ivpa,ivperp] = 0.0 - Gsp[ivpa,ivperp] = 0.0 - Hsp[ivpa,ivperp] = 0.0 - dHspdvpa[ivpa,ivperp] = 0.0 - dHspdvperp[ivpa,ivperp] = 0.0 - nsp[ivpa,ivperp] = 0.0 + d2Gspdvpa2[1,1] = 0.0 + dGspdvperp[1,1] = 0.0 + d2Gspdvperpdvpa[1,1] = 0.0 + d2Gspdvperp2[1,1] = 0.0 + Gsp[1,1] = 0.0 + Hsp[1,1] = 0.0 + dHspdvpa[1,1] = 0.0 + dHspdvperp[1,1] = 0.0 + nsp[1,1] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa - #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] - d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + #d2Gspdvpa2[1,1] += G_weights[1,1,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[1,1] += H3_weights[1,1,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[1,1] += G1_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[1,1] += G1_weights[1,1,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[1,1] += G2_weights[1,1,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[1,1] += H2_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + Gsp[1,1] += G_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + Hsp[1,1] += H_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] + dHspdvpa[1,1] += H_weights[1,1,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[1,1] += H1_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + nsp[1,1] += n_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] end end #end - (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + - d2Gspdvperp2[ivpa,ivperp] + - (1.0/vperp.grid[ivperp])*dGspdvperp[ivpa,ivperp])) - end - - begin_vperp_region() - @loop_vperp ivperp begin - @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) - @. dHspdvpa_from_Gsp[:,ivperp] = vpa.scratch - end - begin_vpa_region() - @loop_vpa ivpa begin - @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) - @. dHspdvperp_from_Gsp[ivpa,:] = vperp.scratch - end - - # evaluate collsion operator - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - # fully expanded form - (Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + - d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + - 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + - (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + - 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + - (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) - # collisional fluxes - ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = - calculate_collisional_fluxes(fs_in[ivpa,ivperp], - dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp], - d2Gspdvpa2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp], - d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], - ms,msp) ) - end - if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" - @. Cssp_div_numerical = 0.0 - begin_vperp_region() - @loop_vperp ivperp begin - @views mul!(vpa.scratch,vpa_spectral.S_matrix,Cflux_vpa[:,ivperp]) - gausslegendre_mass_matrix_solve!(vpa.scratch2,vpa.scratch,vpa_spectral) - Cssp_div_numerical[:,ivperp] += vpa.scratch2 - end - begin_vpa_region() - @loop_vpa ivpa begin - @views mul!(vperp.scratch,vperp_spectral.S_matrix,Cflux_vperp[ivpa,:]) - gausslegendre_mass_matrix_solve!(vperp.scratch2,vperp.scratch,vperp_spectral) - Cssp_div_numerical[ivpa,:] += vperp.scratch2 - end - @. Cssp_div_numerical *= nussp - end + (Hsp_from_Gsp[1,1] = 0.5*( d2Gspdvpa2[1,1] + + d2Gspdvperp2[1,1] + + (1.0/vperp.grid[ivperp])*dGspdvperp[1,1])) + #end plot_H = false #true plot_dHdvpa = false #true @@ -942,7 +881,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_dGdvperp = false #true plot_d2Gdvpa2 = false #true plot_G = false #true - plot_C = false #true plot_n = false #true begin_serial_region() @@ -950,57 +888,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. n_err = abs(nsp - denssp) max_n_err = maximum(n_err) - println("max_n_err: ",max_n_err) + #println("max_n_err: ",max_n_err) println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) - L2_n_err = L2norm_vspace(n_err,vpa,vperp) - println("L2_n_err: ",L2_n_err) - @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) - max_C_err = maximum(Cssp_err) - max_C_Maxwell_val = maximum(Cssp_Maxwell) - max_C_numerical_val = maximum(Cssp_numerical) - println("max_C_err: ",max_C_err) - L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) - println("L2_C_err: ",L2_C_err) - println("max_C_Maxwell_val: ",max_C_Maxwell_val) - println("max_C_numerical_val: ",max_C_numerical_val) - if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" - @. Cssp_div_err = abs(Cssp_div_numerical - Cssp_Maxwell) - max_C_div_err = maximum(Cssp_div_err) - println("max_C_div_err: ",max_C_div_err) - end - @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) - max_Cflux_vpa_err = maximum(Cflux_vpa_err) - println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) - @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) - max_Cflux_vperp_err = maximum(Cflux_vperp_err) - println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) @. H_err = abs(Hsp_from_Gsp - H_Maxwell) max_H_err = maximum(H_err) - println("max_H_from_G_err: ",max_H_err) - @. dHdvperp_err = abs(dHspdvperp_from_Gsp - dHdvperp_Maxwell) - max_dHdvperp_err = maximum(dHdvperp_err) - println("max_dHdvperp_err (from G): ",max_dHdvperp_err) - @. dHdvpa_err = abs(dHspdvpa_from_Gsp - dHdvpa_Maxwell) - max_dHdvpa_err = maximum(dHdvpa_err) - println("max_dHdvpa_err (from G): ",max_dHdvpa_err) + #println("max_H_from_G_err: ",max_H_err) @. H_err = abs(Hsp - H_Maxwell) max_H_err = maximum(H_err) - println("max_H_err: ",max_H_err) + #println("max_H_err: ",max_H_err) println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) - L2_H_err = L2norm_vspace(H_err,vpa,vperp) - println("L2_H_err: ",L2_H_err) @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) max_dHdvperp_err = maximum(dHdvperp_err) - println("max_dHdvperp_err: ",max_dHdvperp_err) + #println("max_dHdvperp_err: ",max_dHdvperp_err) println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) - L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) - println("L2_dHdvperp_err: ",L2_dHdvperp_err) @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) max_dHdvpa_err = maximum(dHdvpa_err) - println("max_dHdvpa_err: ",max_dHdvpa_err) + #println("max_dHdvpa_err: ",max_dHdvpa_err) println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) - L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) - println("L2_dHdvpa_err: ",L2_dHdvpa_err) if plot_n @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, @@ -1012,20 +916,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("fkpl_n_err.pdf") savefig(outfile) end - if plot_C - @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Cssp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Cssp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_err.pdf") - savefig(outfile) - end if plot_H @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1074,10 +964,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) - println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + #println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) - L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) - println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) if plot_d2Gdvperp2 @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1094,10 +982,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) - println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + #println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) - L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) - println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) if plot_d2Gdvperpdvpa @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1114,10 +1000,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) max_dGdvperp_err = maximum(dGdvperp_err) - println("max_dGdvperp_err: ",max_dGdvperp_err) + #println("max_dGdvperp_err: ",max_dGdvperp_err) println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) - L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) - println("L2_dGdvperp_err: ",L2_dGdvperp_err) if plot_dGdvperp @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1134,10 +1018,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) - println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + #println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) - L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) - println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) if plot_d2Gdvpa2 @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1154,10 +1036,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end @. G_err = abs(Gsp - G_Maxwell) max_G_err = maximum(G_err) - println("max_G_err: ",max_G_err) + #println("max_G_err: ",max_G_err) println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) - L2_G_err = L2norm_vspace(G_err,vpa,vperp) - println("L2_G_err: ",L2_G_err) if plot_G @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -1178,14 +1058,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ finalize_comms!() end #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - (results = (maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), + (results = (maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), - L2norm_vspace(Cssp_err,vpa,vperp), L2norm_vspace(G_err,vpa,vperp), L2norm_vspace(H_err,vpa,vperp), L2norm_vspace(dHdvpa_err,vpa,vperp), - L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), - L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), - maximum(n_err), L2norm_vspace(n_err,vpa,vperp) )) + maximum(n_err))) return results end @@ -1196,15 +1073,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 5 - nscan = 1 + ngrid = 9 + nscan = 7 + nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - nelement_list = Int[2] - max_C_err = Array{mk_float,1}(undef,nscan) - max_Gvpa_err = Array{mk_float,1}(undef,nscan) - max_Gvperp_err = Array{mk_float,1}(undef,nscan) + #nelement_list = Int[8] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) @@ -1222,17 +1097,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) - L2_C_err = Array{mk_float,1}(undef,nscan) - L2_G_err = Array{mk_float,1}(undef,nscan) - L2_H_err = Array{mk_float,1}(undef,nscan) - L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) max_n_err = Array{mk_float,1}(undef,nscan) - L2_n_err = Array{mk_float,1}(undef,nscan) expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) @@ -1244,20 +1109,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ for iscan in 1:nscan local nelement = nelement_list[iscan] - ((max_C_err[iscan], max_Gvpa_err[iscan], max_Gvperp_err[iscan], - max_G_err[iscan], max_H_err[iscan], + ((max_G_err[iscan], max_H_err[iscan], max_dHdvpa_err[iscan], max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan], L2_C_err[iscan], - L2_G_err[iscan], L2_H_err[iscan], L2_dHdvpa_err[iscan], - L2_dHdvperp_err[iscan], L2_d2Gdvperp2_err[iscan], L2_d2Gdvpa2_err[iscan], - L2_d2Gdvperpdvpa_err[iscan], L2_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], - max_n_err[iscan], L2_n_err[iscan]) + max_n_err[iscan]) = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) end if global_rank[]==0 @@ -1265,9 +1126,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) xlabel = L"N_{element}" nlabel = L"\epsilon(n)" - Clabel = L"\epsilon(C)" - Gvpalabel = L"\epsilon(\Gamma_{\|\|})" - Gvperplabel = L"\epsilon(\Gamma_{\perp})" Glabel = L"\epsilon(G)" Hlabel = L"\epsilon(H)" dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" @@ -1276,9 +1134,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" dGdvperplabel = L"\epsilon(dG/d v_{\perp})" - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) + plot(nelement_list, [max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) @@ -1286,17 +1144,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected,expected_integral], - xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected,expected_integral], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", + plot(nelement_list, [max_G_err,max_H_err,max_n_err,expected,expected_integral], + xlabel=xlabel, label=[Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) From fa158416603b5591107572389f25501113d24eaf Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 15 Aug 2023 10:18:45 +0000 Subject: [PATCH 104/331] Improved quadrature scheme for divergences using a up to 4 sub grids, consisting of Gauss-Legendre points far from the divergences and Gauss-Laguerre in the points closest to the divergence. --- fkpl_single_field_point_test.jl | 110 ++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index a171475f2..2cb3ed070 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up necessary inputs for collision operator functions nvperp = vperp.n nvpa = vpa.n - ivpa_field = floor(mk_int,nvpa/2) - ivperp_field = 1 #floor(mk_int,nvperp/6) + ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) + ivperp_field = floor(mk_int,nvperp/10) println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -468,42 +468,82 @@ if abspath(PROGRAM_FILE) == @__FILE__ return poly end - function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) + #println("nodes ",nodes) zero = 1.0e-10 @. x_scaled = 0.0 @. w_scaled = 0.0 + nnodes = size(nodes,1) + nquad_legendre = size(x_legendre,1) + nquad_laguerre = size(x_laguerre,1) # assume x_scaled, w_scaled are arrays of length 2*nquad # use only nquad points for most elements, but use 2*nquad for # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint - nquad = size(x_laguerre,1) - @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) - @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre - nquad_coord = nquad + node_cut = nodes[nnodes-1] + + n = nquad_laguerre + nquad_legendre + shift = 0.5*(node_min + node_cut) + scale = 0.5*(node_cut - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + + @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) + @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre + + nquad_coord = n #println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + n = nquad_laguerre + nquad_legendre nquad = size(x_laguerre,1) - for j in 1:nquad - x_scaled[nquad+1-j] = node_min + (node_max - node_min)*exp(-x_laguerre[j]) - w_scaled[nquad+1-j] = (node_max - node_min)*w_laguerre[j] + node_cut = nodes[2] + for j in 1:nquad_laguerre + x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] end - nquad_coord = nquad + shift = 0.5*(node_max + node_cut) + scale = 0.5*(node_max - node_cut) + @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift + @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre + + nquad_coord = n #println("lower divergence") else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - nquad = size(x_laguerre,1) - n = 2*nquad + #println(nodes[igrid_coord]," ", coord_val) + n = 2*nquad_laguerre + node_cut_high = nodes[igrid_coord+1] + if igrid_coord == 1 + # exception for vperp coordinate near orgin + k = 0 + node_cut_low = node_min + nquad_coord = nquad_legendre + 2*nquad_laguerre + else + # fill in lower Gauss-Legendre points + node_cut_low = nodes[igrid_coord-1] + shift = 0.5*(node_cut_low + node_min) + scale = 0.5*(node_cut_low - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + k = nquad_legendre + nquad_coord = 2*(nquad_laguerre + nquad_legendre) + end # lower half of domain - for j in 1:nquad - x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) - w_scaled[j] = (coord_val - node_min)*w_laguerre[j] + for j in 1:nquad_laguerre + x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] end # upper half of domain - for j in 1:nquad - x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) - w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] + for j in 1:nquad_laguerre + x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] end - nquad_coord = n + # fill in upper Gauss-Legendre points + shift = 0.5*(node_cut_high + node_max) + scale = 0.5*(node_max - node_cut_high) + @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift + @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre + #println("intermediate divergence") #else # no divergences # nquad = size(x_legendre,1) @@ -637,7 +677,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -656,7 +696,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, @@ -707,7 +747,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 vperp_nodes = get_nodes(vperp,ielement_vperpp) @@ -727,13 +767,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -756,7 +796,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:vperp.nelement_local vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] @@ -784,8 +824,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_laguerre, w_laguerre = gausslaguerre(nlaguerre) #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) @serial_region begin @@ -821,13 +861,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H3_weights[1,1,:,:] = 0.0 @. n_weights[1,1,:,:] = 0.0 # loop over elements and grid points within elements on primed coordinate - #loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, + loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, + #loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) #end #_block_synchronize() @@ -1073,13 +1113,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 - nscan = 7 - nelement_list = Int[2, 4, 8, 16, 32, 64, 128] + ngrid = 17 + nscan = 1 + #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - #nelement_list = Int[8] + nelement_list = Int[8] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) From 1debba36cc68ee9fde6079c1bfad56992c2e5627 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 15 Aug 2023 10:58:33 +0000 Subject: [PATCH 105/331] Carried the improved quadrature into the full testing script. The errors in the output potentials as a function of vpa and vperp are now extremely localised to vperp element boundaries for vpa small, at the cost of a longer evaluation time. --- fkpl_test.jl | 96 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index f7d2146a9..90cd745f1 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -721,42 +721,82 @@ if abspath(PROGRAM_FILE) == @__FILE__ return poly end - function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, coord_val) + function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) + #println("nodes ",nodes) zero = 1.0e-10 @. x_scaled = 0.0 @. w_scaled = 0.0 + nnodes = size(nodes,1) + nquad_legendre = size(x_legendre,1) + nquad_laguerre = size(x_laguerre,1) # assume x_scaled, w_scaled are arrays of length 2*nquad # use only nquad points for most elements, but use 2*nquad for # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint - nquad = size(x_laguerre,1) - @. x_scaled[1:nquad] = node_max + (node_min - node_max)*exp(-x_laguerre) - @. w_scaled[1:nquad] = (node_max - node_min)*w_laguerre - nquad_coord = nquad + node_cut = nodes[nnodes-1] + + n = nquad_laguerre + nquad_legendre + shift = 0.5*(node_min + node_cut) + scale = 0.5*(node_cut - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + + @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) + @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre + + nquad_coord = n #println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + n = nquad_laguerre + nquad_legendre nquad = size(x_laguerre,1) - for j in 1:nquad - x_scaled[nquad+1-j] = node_min + (node_max - node_min)*exp(-x_laguerre[j]) - w_scaled[nquad+1-j] = (node_max - node_min)*w_laguerre[j] + node_cut = nodes[2] + for j in 1:nquad_laguerre + x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] end - nquad_coord = nquad + shift = 0.5*(node_max + node_cut) + scale = 0.5*(node_max - node_cut) + @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift + @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre + + nquad_coord = n #println("lower divergence") else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - nquad = size(x_laguerre,1) - n = 2*nquad + #println(nodes[igrid_coord]," ", coord_val) + n = 2*nquad_laguerre + node_cut_high = nodes[igrid_coord+1] + if igrid_coord == 1 + # exception for vperp coordinate near orgin + k = 0 + node_cut_low = node_min + nquad_coord = nquad_legendre + 2*nquad_laguerre + else + # fill in lower Gauss-Legendre points + node_cut_low = nodes[igrid_coord-1] + shift = 0.5*(node_cut_low + node_min) + scale = 0.5*(node_cut_low - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + k = nquad_legendre + nquad_coord = 2*(nquad_laguerre + nquad_legendre) + end # lower half of domain - for j in 1:nquad - x_scaled[j] = coord_val + (node_min - coord_val)*exp(-x_laguerre[j]) - w_scaled[j] = (coord_val - node_min)*w_laguerre[j] + for j in 1:nquad_laguerre + x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] end # upper half of domain - for j in 1:nquad - x_scaled[n+1-j] = coord_val + (node_max - coord_val)*exp(-x_laguerre[j]) - w_scaled[n+1-j] = (node_max - coord_val)*w_laguerre[j] + for j in 1:nquad_laguerre + x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] end - nquad_coord = n + # fill in upper Gauss-Legendre points + shift = 0.5*(node_cut_high + node_max) + scale = 0.5*(node_max - node_cut_high) + @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift + @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre + #println("intermediate divergence") #else # no divergences # nquad = size(x_legendre,1) @@ -890,7 +930,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -909,7 +949,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_val) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, @@ -960,7 +1000,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 vperp_nodes = get_nodes(vperp,ielement_vperpp) @@ -980,13 +1020,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_val) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -1009,7 +1049,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:vperp.nelement_local vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] @@ -1037,8 +1077,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_laguerre, w_laguerre = gausslaguerre(nlaguerre) #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,2*nquad), Array{mk_float,1}(undef,2*nquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) @serial_region begin @@ -1078,7 +1118,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end #_block_synchronize() @@ -1750,7 +1790,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 17 nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] From 6f914bfdbecb0abc7853a277d545dca31700da91 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 15 Aug 2023 18:47:56 +0100 Subject: [PATCH 106/331] Attempt to use ArbNumerics to improve accuracy. --- fkpl_single_field_point_test.jl | 57 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 2cb3ed070..364c1f3f9 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -7,7 +7,7 @@ using SpecialFunctions: erf, ellipe, ellipk using FastGaussQuadrature using Dates using LinearAlgebra: mul! - +using ArbNumerics: ArbFloat, sqrt import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up necessary inputs for collision operator functions nvperp = vperp.n nvpa = vpa.n - ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) - ivperp_field = floor(mk_int,nvperp/10) + ivpa_field = floor(mk_int,nvpa/2 + 3*nvpa/32 - 1) + ivperp_field = floor(mk_int,nvperp/8 -1) println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -609,13 +609,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_kvperp = x_vperp[kvperp] w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + denom = (ArbFloat(vpa_val) - x_kvpa)^2 + (ArbFloat(vperp_val) + x_kvperp)^2 + prefac = mk_float(sqrt(denom)) + mm = min(ArbFloat(4.0)*ArbFloat(vperp_val)*x_kvperp/denom,1.0 - 1.0e-15) #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) #mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) + ellipe_mm = ellipe(mk_float(mm)) + ellipk_mm = ellipk(mk_float(mm)) #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end @@ -675,8 +675,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + nquad_vperp_xx,nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences @@ -686,22 +687,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vpap in ielement_vpa_low:ielement_vpa_hi #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_nodes = ArbFloat.(get_nodes(vpa,ielement_vpap)) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + #nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + nquad_vpa = get_scaled_x_w!(xx_vpa, ww_vpa, xx_legendre, ww_legendre, xx_laguerre, ww_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, ArbFloat(vpa_val)) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, + nquad_vperp_xx,ielement_vperpp,vperp_nodes,vperp, + xx_vpa, ww_vpa, xx_vperp, ww_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local @@ -712,7 +714,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) @@ -747,6 +749,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 @@ -763,16 +766,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ end for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_nodes = ArbFloat.(get_nodes(vperp,ielement_vperpp)) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + nquad_vperp_x = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + nquad_vperp_xx = get_scaled_x_w!(xx_vperp, ww_vperp, xx_legendre, ww_legendre, xx_laguerre, ww_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, ArbFloat(vperp_val)) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + nquad_vperp_xx,nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -823,9 +828,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ nlaguerre = nquad x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + xx_legendre, ww_legendre = Array{ArbFloat,1}(undef,nquad), Array{ArbFloat,1}(undef,nquad) + @. xx_legendre = ArbFloat(x_legendre) + @. ww_legendre = ArbFloat(w_legendre) + xx_laguerre, ww_laguerre = Array{ArbFloat,1}(undef,nlaguerre), Array{ArbFloat,1}(undef,nlaguerre) + @. xx_laguerre = ArbFloat(x_laguerre) + @. ww_laguerre = ArbFloat(w_laguerre) + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + xx_vpa, ww_vpa = Array{ArbFloat,1}(undef,4*nquad), Array{ArbFloat,1}(undef,4*nquad) + xx_vperp, ww_vperp = Array{ArbFloat,1}(undef,4*nquad), Array{ArbFloat,1}(undef,4*nquad) @serial_region begin @@ -867,6 +881,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) #end @@ -1113,13 +1128,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 17 + ngrid = 9 nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - nelement_list = Int[8] + nelement_list = Int[4] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) From 1b147763389e734874fa794b44ab3168317a758f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 15 Aug 2023 18:50:59 +0100 Subject: [PATCH 107/331] Version of script without ArbNumerics --- fkpl_single_field_point_test.jl | 57 ++++++++++++--------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 364c1f3f9..2cb3ed070 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -7,7 +7,7 @@ using SpecialFunctions: erf, ellipe, ellipk using FastGaussQuadrature using Dates using LinearAlgebra: mul! -using ArbNumerics: ArbFloat, sqrt + import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate @@ -203,8 +203,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up necessary inputs for collision operator functions nvperp = vperp.n nvpa = vpa.n - ivpa_field = floor(mk_int,nvpa/2 + 3*nvpa/32 - 1) - ivperp_field = floor(mk_int,nvperp/8 -1) + ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) + ivperp_field = floor(mk_int,nvperp/10) println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -609,13 +609,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_kvperp = x_vperp[kvperp] w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] - denom = (ArbFloat(vpa_val) - x_kvpa)^2 + (ArbFloat(vperp_val) + x_kvperp)^2 - prefac = mk_float(sqrt(denom)) - mm = min(ArbFloat(4.0)*ArbFloat(vperp_val)*x_kvperp/denom,1.0 - 1.0e-15) + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) #mm = 4.0*vperp_val*x_kvperp/denom - ellipe_mm = ellipe(mk_float(mm)) - ellipk_mm = ellipk(mk_float(mm)) + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end @@ -675,9 +675,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp_xx,nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences @@ -687,23 +686,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vpap in ielement_vpa_low:ielement_vpa_hi #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences - vpa_nodes = ArbFloat.(get_nodes(vpa,ielement_vpap)) + vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - #nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - nquad_vpa = get_scaled_x_w!(xx_vpa, ww_vpa, xx_legendre, ww_legendre, xx_laguerre, ww_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, ArbFloat(vpa_val)) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp_xx,ielement_vperpp,vperp_nodes,vperp, - xx_vpa, ww_vpa, xx_vperp, ww_vperp, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local @@ -714,7 +712,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) @@ -749,7 +747,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 @@ -766,18 +763,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ end for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - vperp_nodes = ArbFloat.(get_nodes(vperp,ielement_vperpp)) + vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp_x = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - nquad_vperp_xx = get_scaled_x_w!(xx_vperp, ww_vperp, xx_legendre, ww_legendre, xx_laguerre, ww_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, ArbFloat(vperp_val)) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp_xx,nquad_vperp_x,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -828,18 +823,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ nlaguerre = nquad x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - xx_legendre, ww_legendre = Array{ArbFloat,1}(undef,nquad), Array{ArbFloat,1}(undef,nquad) - @. xx_legendre = ArbFloat(x_legendre) - @. ww_legendre = ArbFloat(w_legendre) - xx_laguerre, ww_laguerre = Array{ArbFloat,1}(undef,nlaguerre), Array{ArbFloat,1}(undef,nlaguerre) - @. xx_laguerre = ArbFloat(x_laguerre) - @. ww_laguerre = ArbFloat(w_laguerre) - #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - xx_vpa, ww_vpa = Array{ArbFloat,1}(undef,4*nquad), Array{ArbFloat,1}(undef,4*nquad) - xx_vperp, ww_vperp = Array{ArbFloat,1}(undef,4*nquad), Array{ArbFloat,1}(undef,4*nquad) @serial_region begin @@ -881,7 +867,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - xx_vpa, ww_vpa, xx_vperp, ww_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) #end @@ -1128,13 +1113,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 17 nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] - nelement_list = Int[4] + nelement_list = Int[8] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) From 9cd18edb5b656b9491a124ab19520daafaef63f9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 16 Aug 2023 08:13:44 +0100 Subject: [PATCH 108/331] Corrected mistake that should have been addressed in 8ca4ac9. Make sure to use a quadrature for vperp integration that has divergence accommodation only in the (vpa,vperp) element where the divergence is present. In principle the same change has to be made to the loops over ivpa and ivperp within each element. --- fkpl_single_field_point_test.jl | 26 +++++++++++++++++++------- fkpl_test.jl | 24 +++++++++++++++--------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 2cb3ed070..dd6641a97 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -203,8 +203,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # set up necessary inputs for collision operator functions nvperp = vperp.n nvpa = vpa.n - ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) - ivperp_field = floor(mk_int,nvperp/10) + #ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) + #ivperp_field = floor(mk_int,nvperp/10) + ivpa_field = floor(mk_int,nvpa/2 + nvpa/16) + ivperp_field = floor(mk_int,nvperp/8 -1) + #ivpa_field = floor(mk_int,nvpa/2) + #ivperp_field = floor(mk_int,1) println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -675,9 +679,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + vperp,ielement_vperpp, + #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -690,8 +700,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi - #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] @@ -704,6 +714,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -771,9 +782,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + vperp,ielement_vperpp, + #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local diff --git a/fkpl_test.jl b/fkpl_test.jl index 90cd745f1..c3cdabf7f 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -928,9 +928,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) for ielement_vpap in 1:ielement_vpa_low-1 # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -943,6 +947,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences @@ -957,6 +962,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) @@ -1016,17 +1022,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ end for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #vperp_nodes = get_nodes(vperp,ielement_vperpp) + #vperp_max = vperp_nodes[end] + #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -1790,7 +1796,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 17 + ngrid = 9 nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] From 70f7c76f69ab049aa717767f361acd10cfd7bf2e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 16 Aug 2023 08:31:11 +0100 Subject: [PATCH 109/331] added diagnostic to find the indices where the error is maximum in the unprimed grid --- fkpl_test.jl | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index c3cdabf7f..37dd71ff2 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -1231,16 +1231,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. n_err = abs(nsp - denssp) - max_n_err = maximum(n_err) - println("max_n_err: ",max_n_err) + max_n_err, max_n_index = findmax(n_err) + println("max_n_err: ",max_n_err," ",max_n_index) println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) L2_n_err = L2norm_vspace(n_err,vpa,vperp) println("L2_n_err: ",L2_n_err) @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) - max_C_err = maximum(Cssp_err) + max_C_err, max_C_index = findmax(Cssp_err) max_C_Maxwell_val = maximum(Cssp_Maxwell) max_C_numerical_val = maximum(Cssp_numerical) - println("max_C_err: ",max_C_err) + println("max_C_err: ",max_C_err," ",max_C_index) L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) println("L2_C_err: ",L2_C_err) println("max_C_Maxwell_val: ",max_C_Maxwell_val) @@ -1266,20 +1266,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_dHdvpa_err = maximum(dHdvpa_err) println("max_dHdvpa_err (from G): ",max_dHdvpa_err) @. H_err = abs(Hsp - H_Maxwell) - max_H_err = maximum(H_err) - println("max_H_err: ",max_H_err) + max_H_err, max_H_index = findmax(H_err) + println("max_H_err: ",max_H_err," ",max_H_index) println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) L2_H_err = L2norm_vspace(H_err,vpa,vperp) println("L2_H_err: ",L2_H_err) @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) - max_dHdvperp_err = maximum(dHdvperp_err) - println("max_dHdvperp_err: ",max_dHdvperp_err) + max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) println("L2_dHdvperp_err: ",L2_dHdvperp_err) @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) - max_dHdvpa_err = maximum(dHdvpa_err) - println("max_dHdvpa_err: ",max_dHdvpa_err) + max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) println("L2_dHdvpa_err: ",L2_dHdvpa_err) @@ -1355,8 +1355,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) - max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) - println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) + max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) @@ -1375,8 +1375,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) - max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) - println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) + max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) @@ -1395,8 +1395,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) - max_dGdvperp_err = maximum(dGdvperp_err) - println("max_dGdvperp_err: ",max_dGdvperp_err) + max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) println("L2_dGdvperp_err: ",L2_dGdvperp_err) @@ -1415,8 +1415,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) - max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) - println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) + max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) @@ -1435,8 +1435,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) end @. G_err = abs(Gsp - G_Maxwell) - max_G_err = maximum(G_err) - println("max_G_err: ",max_G_err) + max_G_err, max_G_index = findmax(G_err) + println("max_G_err: ",max_G_err," ",max_G_index) println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) L2_G_err = L2norm_vspace(G_err,vpa,vperp) println("L2_G_err: ",L2_G_err) From c4806f33625fd9a7cab73019ab8becca4639dda9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 16 Aug 2023 13:48:54 +0100 Subject: [PATCH 110/331] Move boundaries of Gauss-Legendre and Gauss-Laguerre grids to all ngrid = 3 to be used. --- fkpl_single_field_point_test.jl | 18 ++++++++++-------- fkpl_test.jl | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index dd6641a97..177240438 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -209,6 +209,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_field = floor(mk_int,nvperp/8 -1) #ivpa_field = floor(mk_int,nvpa/2) #ivperp_field = floor(mk_int,1) + #ivpa_field = 37 + #ivperp_field = 15 println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -514,9 +516,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_coord = n #println("lower divergence") else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - #println(nodes[igrid_coord]," ", coord_val) + #println("igrid_coord ", igrid_coord, " ", nodes[igrid_coord]," ", coord_val) n = 2*nquad_laguerre - node_cut_high = nodes[igrid_coord+1] + node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 if igrid_coord == 1 # exception for vperp coordinate near orgin k = 0 @@ -524,7 +526,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_coord = nquad_legendre + 2*nquad_laguerre else # fill in lower Gauss-Legendre points - node_cut_low = nodes[igrid_coord-1] + node_cut_low = (nodes[igrid_coord-1] + nodes[igrid_coord])/2.0 shift = 0.5*(node_cut_low + node_min) scale = 0.5*(node_cut_low - node_min) @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift @@ -774,11 +776,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ end for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #vperp_nodes = get_nodes(vperp,ielement_vperpp) + #vperp_max = vperp_nodes[end] + #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids @@ -1125,7 +1127,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 17 + ngrid = 9 nscan = 1 #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] diff --git a/fkpl_test.jl b/fkpl_test.jl index 37dd71ff2..fd288e02f 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -765,7 +765,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence #println(nodes[igrid_coord]," ", coord_val) n = 2*nquad_laguerre - node_cut_high = nodes[igrid_coord+1] + node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 if igrid_coord == 1 # exception for vperp coordinate near orgin k = 0 @@ -773,7 +773,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_coord = nquad_legendre + 2*nquad_laguerre else # fill in lower Gauss-Legendre points - node_cut_low = nodes[igrid_coord-1] + node_cut_low = (nodes[igrid_coord-1]+nodes[igrid_coord])/2.0 shift = 0.5*(node_cut_low + node_min) scale = 0.5*(node_cut_low - node_min) @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift From 8da388fb355d32e13aed3ee9b16dedfa6ce45dd0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 16 Aug 2023 14:35:35 +0000 Subject: [PATCH 111/331] Use halfway point to split integral also for upper and lower endpoint cases --- fkpl_single_field_point_test.jl | 9 +++++---- fkpl_test.jl | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 177240438..5e0c1604a 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -209,8 +209,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_field = floor(mk_int,nvperp/8 -1) #ivpa_field = floor(mk_int,nvpa/2) #ivperp_field = floor(mk_int,1) - #ivpa_field = 37 - #ivperp_field = 15 + ivpa_field = 37 + ivperp_field = 8 println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -487,7 +487,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint - node_cut = nodes[nnodes-1] + node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 n = nquad_laguerre + nquad_legendre shift = 0.5*(node_min + node_cut) @@ -503,7 +503,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ elseif abs(coord_val - node_min) < zero # divergence at lower endpoint n = nquad_laguerre + nquad_legendre nquad = size(x_laguerre,1) - node_cut = nodes[2] + node_cut = (nodes[1] + nodes[2])/2.0 for j in 1:nquad_laguerre x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] @@ -1133,6 +1133,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] + #nelement_list = Int[100] nelement_list = Int[8] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) diff --git a/fkpl_test.jl b/fkpl_test.jl index fd288e02f..4fcc8e091 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -734,7 +734,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # elements with interior divergences #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint - node_cut = nodes[nnodes-1] + node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 n = nquad_laguerre + nquad_legendre shift = 0.5*(node_min + node_cut) @@ -750,7 +750,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ elseif abs(coord_val - node_min) < zero # divergence at lower endpoint n = nquad_laguerre + nquad_legendre nquad = size(x_laguerre,1) - node_cut = nodes[2] + node_cut = (nodes[1] + nodes[2])/2.0 for j in 1:nquad_laguerre x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] @@ -1796,12 +1796,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 - nscan = 1 + ngrid = 2 + nscan = 3 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] - nelement_list = Int[8] + nelement_list = Int[50, 100, 200] + #nelement_list = Int[8] max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) From b7451ad321cced627878b519323c6aad4172be67 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 19 Aug 2023 06:08:04 +0000 Subject: [PATCH 112/331] Reduce volume of Gauss-Laguerre region. No or limited impact on convergence of integrals. --- fkpl_single_field_point_test.jl | 41 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 5e0c1604a..7f6700a23 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -209,8 +209,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_field = floor(mk_int,nvperp/8 -1) #ivpa_field = floor(mk_int,nvpa/2) #ivperp_field = floor(mk_int,1) - ivpa_field = 37 - ivperp_field = 8 + ivpa_field = floor(mk_int,nvpa/2) + 5# + 4 + ivperp_field = 9 println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI @@ -475,7 +475,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) - #println("nodes ",nodes) + println("nodes ",nodes) zero = 1.0e-10 @. x_scaled = 0.0 @. w_scaled = 0.0 @@ -485,7 +485,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # assume x_scaled, w_scaled are arrays of length 2*nquad # use only nquad points for most elements, but use 2*nquad for # elements with interior divergences - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) if abs(coord_val - node_max) < zero # divergence at upper endpoint node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 @@ -499,7 +499,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre nquad_coord = n - #println("upper divergence") + println("upper divergence") elseif abs(coord_val - node_min) < zero # divergence at lower endpoint n = nquad_laguerre + nquad_legendre nquad = size(x_laguerre,1) @@ -514,11 +514,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre nquad_coord = n - #println("lower divergence") + println("lower divergence") else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - #println("igrid_coord ", igrid_coord, " ", nodes[igrid_coord]," ", coord_val) + println("igrid_coord ", igrid_coord, " ", nodes[igrid_coord]," ", coord_val) + if nnodes > 3 + delta_nodes = nodes[2:end] - nodes[1:nnodes-1] + min_spacing = minimum(delta_nodes) + else # use that for ngrid = 2, 3 nodes are equally spaced + min_spacing = nodes[2] - nodes[1] + end + println("min spacing",min_spacing) n = 2*nquad_laguerre - node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 + #node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 + node_cut_high = 0.5*min_spacing + nodes[igrid_coord] if igrid_coord == 1 # exception for vperp coordinate near orgin k = 0 @@ -526,7 +534,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ nquad_coord = nquad_legendre + 2*nquad_laguerre else # fill in lower Gauss-Legendre points - node_cut_low = (nodes[igrid_coord-1] + nodes[igrid_coord])/2.0 + #node_cut_low = (nodes[igrid_coord-1] + nodes[igrid_coord])/2.0 + node_cut_low = nodes[igrid_coord] - 0.5*min_spacing shift = 0.5*(node_cut_low + node_min) scale = 0.5*(node_cut_low - node_min) @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift @@ -550,7 +559,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre - #println("intermediate divergence") + println("intermediate divergence") #else # no divergences # nquad = size(x_legendre,1) # shift = 0.5*(node_min + node_max) @@ -560,8 +569,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # #println("no divergence") # nquad_coord = nquad end - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) + println("x_scaled",x_scaled) + println("w_scaled",w_scaled) + println("nquad_coord ",nquad_coord) return nquad_coord end @@ -1127,14 +1137,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 5 nscan = 1 + plot_scan = false #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] #nelement_list = Int[100] - nelement_list = Int[8] + nelement_list = Int[32] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) @@ -1176,7 +1187,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_n_err[iscan]) = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) end - if global_rank[]==0 + if global_rank[]==0 && plot_scan fontsize = 8 ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) xlabel = L"N_{element}" From 60733c6100c0d1dbd78195c5a3acddbcff9a249f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 19 Aug 2023 07:07:21 +0000 Subject: [PATCH 113/331] Attempt to improve convergence by only using Gauss-Laguerre quadrature for Lagrange polynomial integrals where divergence in integrand is present (where the divergence lines up with the collocation point where the polynomial is 1). --- fkpl_single_field_point_test.jl | 164 +++++++++++++++++++++++++++++--- 1 file changed, 151 insertions(+), 13 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 7f6700a23..0677de3b1 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -210,8 +210,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ #ivpa_field = floor(mk_int,nvpa/2) #ivperp_field = floor(mk_int,1) ivpa_field = floor(mk_int,nvpa/2) + 5# + 4 - ivperp_field = 9 - println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) + ivperp_field = 8 + println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field], " ivpa = ",ivpa_field, " ivperp = ",ivperp_field) # Set up MPI if standalone @@ -575,6 +575,45 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nquad_coord end + function get_scaled_x_w_off_divergence!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max, coord_val) + zero = 1.0e-10 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + nquad_legendre = size(x_legendre,1) + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences + println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero || abs(coord_val - node_min) < zero # divergence at lower endpoint or divergence at upper endpoint + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + nquad_coord = nquad_legendre + println("off upper or lower divergence") + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + + # fill in lower Gauss-Legendre points + shift = 0.5*(coord_val + node_min) + scale = 0.5*(coord_val - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + nquad_coord = 2*nquad_legendre + + # fill in upper Gauss-Legendre points + shift = 0.5*(coord_val + node_max) + scale = 0.5*(node_max - coord_val) + @. x_scaled[1+nquad_legendre:nquad_coord] = scale*x_legendre + shift + @. w_scaled[1+nquad_legendre:nquad_coord] = scale*w_legendre + + println("off intermediate divergence") + end + println("x_scaled",x_scaled) + println("w_scaled",w_scaled) + println("nquad_coord ",nquad_coord) + return nquad_coord + end + function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) zero = 1.0e-6 @. x_scaled = 0.0 @@ -626,12 +665,102 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + mm_arg = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - #mm = 4.0*vperp_val*x_kvperp/denom + mm = 4.0*vperp_val*x_kvperp/denom prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) + ellipe_mm = ellipe(mm_arg) + ellipk_mm = ellipk(mm_arg) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G2_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G3_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (n_weights[1,1,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing + end + + function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, + H_weights,H1_weights,H2_weights,H3_weights,n_weights, + ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids + xx_vpa, wx_vpa, nquad_vpa_x, xx_vperp, wx_vperp, nquad_vperp_x, # points and weights for primed (source) grids + yy_vpa, wy_vpa, nquad_vpa_y, y_vperp, wy_vperp, nquad_vperp_y, # points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + if ivpa == ivpap && ivperp == ivperpp + println("divergence grid") + x_vpa, w_vpa, nquad_vpa = xx_vpa, wx_vpa, nquad_vpa_x + x_vperp, w_vperp, nquad_vperp = xx_vperp, wx_vperp, nquad_vperp_x + else + x_vpa, w_vpa, nquad_vpa = yy_vpa, wy_vpa, nquad_vpa_y + x_vperp, w_vperp, nquad_vperp = yy_vperp, wy_vperp, nquad_vperp_y + end + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm_arg = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) + mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm_arg) + ellipk_mm = ellipk(mm_arg) #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end @@ -694,6 +823,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp,ielement_vperpp, #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) vperp_nodes = get_nodes(vperp,ielement_vperpp) @@ -712,18 +842,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + nquad_vperp_x = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + nquad_vperp_y = get_scaled_x_w_off_divergence!(yy_vperp, wy_vperp, x_legendre, w_legendre, vperp_min, vperp_max, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + nquad_vpa_x = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + nquad_vpa_y = get_scaled_x_w_off_divergence!(yy_vpa, wy_vpa, x_legendre, w_legendre, vpa_min, vpa_max, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, + ielement_vpap,vpa_nodes,vpa, + ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, nquad_vpa_x, x_vperp, w_vperp, nquad_vperp_x, + yy_vpa, wy_vpa, nquad_vpa_y, yy_vperp, wy_vperp, nquad_vperp_y, vpa_val, vperp_val, ivpa, ivperp) end nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) @@ -770,6 +903,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 @@ -797,6 +931,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp,ielement_vperpp, #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -850,6 +985,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + yy_vpa, wy_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + yy_vperp, wy_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) @serial_region begin @@ -891,6 +1028,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) #end @@ -1137,7 +1275,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 5 + ngrid = 9 nscan = 1 plot_scan = false #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] @@ -1145,7 +1283,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] #nelement_list = Int[100] - nelement_list = Int[32] + nelement_list = Int[8] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) From 6b164afc061a23f0c0b6cb2ea8332870a927d757 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 24 Aug 2023 14:24:30 +0000 Subject: [PATCH 114/331] Revert quadrature in single point test script to use the same quadrature within a 2D element, for all Lagrange polynomials --- fkpl_single_field_point_test.jl | 164 +++----------------------------- 1 file changed, 13 insertions(+), 151 deletions(-) diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl index 0677de3b1..7f6700a23 100644 --- a/fkpl_single_field_point_test.jl +++ b/fkpl_single_field_point_test.jl @@ -210,8 +210,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ #ivpa_field = floor(mk_int,nvpa/2) #ivperp_field = floor(mk_int,1) ivpa_field = floor(mk_int,nvpa/2) + 5# + 4 - ivperp_field = 8 - println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field], " ivpa = ",ivpa_field, " ivperp = ",ivperp_field) + ivperp_field = 9 + println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) # Set up MPI if standalone @@ -575,45 +575,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nquad_coord end - function get_scaled_x_w_off_divergence!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max, coord_val) - zero = 1.0e-10 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - nquad_legendre = size(x_legendre,1) - # assume x_scaled, w_scaled are arrays of length 2*nquad - # use only nquad points for most elements, but use 2*nquad for - # elements with interior divergences - println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - if abs(coord_val - node_max) < zero || abs(coord_val - node_min) < zero # divergence at lower endpoint or divergence at upper endpoint - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - nquad_coord = nquad_legendre - println("off upper or lower divergence") - else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - - # fill in lower Gauss-Legendre points - shift = 0.5*(coord_val + node_min) - scale = 0.5*(coord_val - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - nquad_coord = 2*nquad_legendre - - # fill in upper Gauss-Legendre points - shift = 0.5*(coord_val + node_max) - scale = 0.5*(node_max - coord_val) - @. x_scaled[1+nquad_legendre:nquad_coord] = scale*x_legendre + shift - @. w_scaled[1+nquad_legendre:nquad_coord] = scale*w_legendre - - println("off intermediate divergence") - end - println("x_scaled",x_scaled) - println("w_scaled",w_scaled) - println("nquad_coord ",nquad_coord) - return nquad_coord - end - function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) zero = 1.0e-6 @. x_scaled = 0.0 @@ -665,102 +626,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ w_kvperp = w_vperp[kvperp] w_kvpa = w_vpa[kvpa] denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm_arg = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - mm = 4.0*vperp_val*x_kvperp/denom + #mm = 4.0*vperp_val*x_kvperp/denom prefac = sqrt(denom) - ellipe_mm = ellipe(mm_arg) - ellipk_mm = ellipk(mm_arg) - #if mm_test > 1.0 - # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - #end - G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - - (G_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G1_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G2_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G3_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H1_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H2_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*(vpa_val - x_kvpa)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (n_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - end - end - end - end - return nothing - end - - function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids - ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids - xx_vpa, wx_vpa, nquad_vpa_x, xx_vperp, wx_vperp, nquad_vperp_x, # points and weights for primed (source) grids - yy_vpa, wy_vpa, nquad_vpa_y, y_vperp, wy_vperp, nquad_vperp_y, # points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids - for igrid_vperp in 1:vperp.ngrid - for igrid_vpa in 1:vpa.ngrid - # get grid index for point on full grid - ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - if ivpa == ivpap && ivperp == ivperpp - println("divergence grid") - x_vpa, w_vpa, nquad_vpa = xx_vpa, wx_vpa, nquad_vpa_x - x_vperp, w_vperp, nquad_vperp = xx_vperp, wx_vperp, nquad_vperp_x - else - x_vpa, w_vpa, nquad_vpa = yy_vpa, wy_vpa, nquad_vpa_y - x_vperp, w_vperp, nquad_vperp = yy_vperp, wy_vperp, nquad_vperp_y - end - # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad_vperp - for kvpa in 1:nquad_vpa - x_kvpa = x_vpa[kvpa] - x_kvperp = x_vperp[kvperp] - w_kvperp = w_vperp[kvperp] - w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm_arg = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm_arg) - ellipk_mm = ellipk(mm_arg) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end @@ -823,7 +694,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp,ielement_vperpp, #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) vperp_nodes = get_nodes(vperp,ielement_vperpp) @@ -842,21 +712,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end - nquad_vperp_x = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - nquad_vperp_y = get_scaled_x_w_off_divergence!(yy_vperp, wy_vperp, x_legendre, w_legendre, vperp_min, vperp_max, vperp_val) + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa_x = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - nquad_vpa_y = get_scaled_x_w_off_divergence!(yy_vpa, wy_vpa, x_legendre, w_legendre, vpa_min, vpa_max, vpa_val) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, H_weights,H1_weights,H2_weights,H3_weights,n_weights, - ielement_vpap,vpa_nodes,vpa, - ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, nquad_vpa_x, x_vperp, w_vperp, nquad_vperp_x, - yy_vpa, wy_vpa, nquad_vpa_y, yy_vperp, wy_vperp, nquad_vperp_y, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val, ivpa, ivperp) end nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) @@ -903,7 +770,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) for ielement_vperpp in 1:ielement_vperp_low-1 @@ -931,7 +797,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp,ielement_vperpp, #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -985,8 +850,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - yy_vpa, wy_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - yy_vperp, wy_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) @serial_region begin @@ -1028,7 +891,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - yy_vpa, wy_vpa, yy_vperp, wy_vperp, # arrays to store points and weights for primed (source) grids igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) #end @@ -1275,7 +1137,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 5 nscan = 1 plot_scan = false #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] @@ -1283,7 +1145,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] #nelement_list = Int[100] - nelement_list = Int[8] + nelement_list = Int[32] max_G_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) From 3e863df1307eb3fa554c842c5a5d73262993d647 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 10:14:29 +0100 Subject: [PATCH 115/331] Ported the base-level functions for doing the basic integration operations needed for the collision operator. --- fkpl_test.jl | 81 +++--- src/fokker_planck.jl | 577 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 593 insertions(+), 65 deletions(-) diff --git a/fkpl_test.jl b/fkpl_test.jl index 4fcc8e091..aa4795f0d 100644 --- a/fkpl_test.jl +++ b/fkpl_test.jl @@ -24,6 +24,7 @@ using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvper using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.fokker_planck: Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative!, second_derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -1127,6 +1128,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end + fkarrays = init_fokker_planck_collisions(vperp,vpa,precompute_weights=true) + G1_weights = fkarrays.G1_weights + H_weights = fkarrays.H0_weights + H1_weights = fkarrays.H1_weights + H2_weights = fkarrays.H2_weights + H3_weights = fkarrays.H3_weights + #_block_synchronize() begin_serial_region() @serial_region begin @@ -1139,30 +1147,35 @@ if abspath(PROGRAM_FILE) == @__FILE__ @loop_vperp_vpa ivperp ivpa begin #for ivperp in 1:nvperp #for ivpa in 1:nvpa - d2Gspdvpa2[ivpa,ivperp] = 0.0 - dGspdvperp[ivpa,ivperp] = 0.0 - d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 - d2Gspdvperp2[ivpa,ivperp] = 0.0 + #d2Gspdvpa2[ivpa,ivperp] = 0.0 + #dGspdvperp[ivpa,ivperp] = 0.0 + #d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + #d2Gspdvperp2[ivpa,ivperp] = 0.0 Gsp[ivpa,ivperp] = 0.0 Hsp[ivpa,ivperp] = 0.0 - dHspdvpa[ivpa,ivperp] = 0.0 - dHspdvperp[ivpa,ivperp] = 0.0 + #dHspdvpa[ivpa,ivperp] = 0.0 + #dHspdvperp[ivpa,ivperp] = 0.0 nsp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa - #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] - d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + ##d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + #d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + #dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + ##d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + #dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] end end + get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp, + dfspdvpa,dfspdvperp,d2fspdvperpdvpa, + G1_weights,H_weights,H1_weights,H2_weights,H3_weights, + ivpa,ivperp,nvpa,nvperp) #end (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + @@ -1185,12 +1198,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin # fully expanded form - (Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + - d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + - 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + - (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + - 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + - (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) + #(Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + + # d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + + # 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + + # (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + + # 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + + # (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) + (Cssp_numerical[ivpa,ivperp] = Cssp_fully_expanded_form(nussp,ms,msp, + d2fsdvpa2[ivpa,ivperp],d2fsdvperp2[ivpa,ivperp],d2fsdvperpdvpa[ivpa,ivperp],dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp],fs_in[ivpa,ivperp], + d2Gspdvpa2[ivpa,ivperp],d2Gspdvperp2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp],dGspdvperp[ivpa,ivperp], + dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp],fsp_in[ivpa,ivperp],vperp.grid[ivperp]) ) # collisional fluxes ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = calculate_collisional_fluxes(fs_in[ivpa,ivperp], @@ -1216,14 +1233,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. Cssp_div_numerical *= nussp end - plot_H = true - plot_dHdvpa = true - plot_dHdvperp = true - plot_d2Gdvperp2 = true - plot_d2Gdvperpdvpa = true - plot_dGdvperp = true - plot_d2Gdvpa2 = true - plot_G = true + plot_H = false #true + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + plot_G = false #true plot_C = false #true plot_n = false #true @@ -1796,12 +1813,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 2 - nscan = 3 + ngrid = 9 #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] - nelement_list = Int[50, 100, 200] - #nelement_list = Int[8] + #nelement_list = Int[50, 100, 200] + nelement_list = Int[4] + nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_Gvpa_err = Array{mk_float,1}(undef,nscan) max_Gvperp_err = Array{mk_float,1}(undef,nscan) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6ffc3fc06..d1b271ec0 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -16,7 +16,11 @@ export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions + using SpecialFunctions: ellipk, ellipe, erf +using FastGaussQuadrature +using Dates using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..communication: MPISharedArray @@ -29,15 +33,18 @@ for the Fokker-Planck collision operator """ struct fokkerplanck_arrays_struct - elliptic_integral_E_factor::Array{mk_float,4} - elliptic_integral_K_factor::Array{mk_float,4} - Rosenbluth_G::Array{mk_float,2} - #Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} - #Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} - #Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} - Rosenbluth_H::Array{mk_float,2} - #Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} - #Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} + G1_weights::MPISharedArray{mk_float,4} + H0_weights::MPISharedArray{mk_float,4} + H1_weights::MPISharedArray{mk_float,4} + H2_weights::MPISharedArray{mk_float,4} + H3_weights::MPISharedArray{mk_float,4} + #Rosenbluth_G::Array{mk_float,2} + Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} + Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} + Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} + #Rosenbluth_H::Array{mk_float,2} + Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} + Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} #Cflux_vpa::MPISharedArray{mk_float,2} #Cflux_vperp::MPISharedArray{mk_float,2} buffer_vpavperp_1::Array{mk_float,2} @@ -53,27 +60,28 @@ function allocate_fokkerplanck_arrays(vperp,vpa) nvpa = vpa.n nvperp = vperp.n - elliptic_integral_E_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) - elliptic_integral_K_factor = allocate_float(nvpa,nvperp,nvpa,nvperp) - Rosenbluth_G = allocate_float(nvpa,nvperp) - #Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) - #Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) - #Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) - Rosenbluth_H = allocate_float(nvpa,nvperp) - #Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) - #Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) + G1_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + H0_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + #Rosenbluth_G = allocate_float(nvpa,nvperp) + Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_H = allocate_float(nvpa,nvperp) + Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) + Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) #Cflux_vpa = allocate_shared_float(nvpa,nvperp) #Cflux_vperp = allocate_shared_float(nvpa,nvperp) buffer_vpavperp_1 = allocate_float(nvpa,nvperp) buffer_vpavperp_2 = allocate_float(nvpa,nvperp) #Cssp_result_vpavperp = allocate_float(nvpa,nvperp) - return fokkerplanck_arrays_struct(elliptic_integral_E_factor,elliptic_integral_K_factor, - Rosenbluth_G,#Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, - Rosenbluth_H,#Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - #Cflux_vpa,Cflux_vperp, + return fokkerplanck_arrays_struct(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, + Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, buffer_vpavperp_1,buffer_vpavperp_2) - #Cssp_result_vpavperp) end @@ -125,14 +133,466 @@ end function that initialises the arrays needed for Fokker Planck collisions """ -function init_fokker_planck_collisions(vperp,vpa;init_integral_factors=false) - fokkerplanck_arrays = allocate_fokkerplanck_arrays(vperp,vpa) - if vperp.n > 1 && init_integral_factors - @views init_elliptic_integral_factors!(fokkerplanck_arrays.elliptic_integral_E_factor, - fokkerplanck_arrays.elliptic_integral_K_factor, - vperp,vpa) +function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false) + fka = allocate_fokkerplanck_arrays(vperp,vpa) + if vperp.n > 1 && precompute_weights + @views init_Rosenbluth_potential_integration_weights!(fka.G1_weights, fka.H0_weights, fka.H1_weights, + fka.H2_weights, fka.H3_weights, vperp, vpa) end - return fokkerplanck_arrays + return fka +end + +""" +function that precomputes the required integration weights +""" +function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + ngrid = max(ngrid_vpa,ngrid_vperp) + + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + # G_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + G1_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + # G2_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + # G3_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + H0_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + H1_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + H2_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + H3_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + #@. n_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + end + + return nothing +end + +function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax +end + +function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes +end +""" +Lagrange polynomial +args: +j - index of l_j from list of nodes +x_nodes - array of x node values +x - point where interpolated value is returned +""" +function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly +end + +function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) + #println("nodes ",nodes) + zero = 1.0e-10 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + nnodes = size(nodes,1) + nquad_legendre = size(x_legendre,1) + nquad_laguerre = size(x_laguerre,1) + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero # divergence at upper endpoint + node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 + + n = nquad_laguerre + nquad_legendre + shift = 0.5*(node_min + node_cut) + scale = 0.5*(node_cut - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + + @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) + @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre + + nquad_coord = n + #println("upper divergence") + elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + n = nquad_laguerre + nquad_legendre + nquad = size(x_laguerre,1) + node_cut = (nodes[1] + nodes[2])/2.0 + for j in 1:nquad_laguerre + x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] + end + shift = 0.5*(node_max + node_cut) + scale = 0.5*(node_max - node_cut) + @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift + @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre + + nquad_coord = n + #println("lower divergence") + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + #println(nodes[igrid_coord]," ", coord_val) + n = 2*nquad_laguerre + node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 + if igrid_coord == 1 + # exception for vperp coordinate near orgin + k = 0 + node_cut_low = node_min + nquad_coord = nquad_legendre + 2*nquad_laguerre + else + # fill in lower Gauss-Legendre points + node_cut_low = (nodes[igrid_coord-1]+nodes[igrid_coord])/2.0 + shift = 0.5*(node_cut_low + node_min) + scale = 0.5*(node_cut_low - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + k = nquad_legendre + nquad_coord = 2*(nquad_laguerre + nquad_legendre) + end + # lower half of domain + for j in 1:nquad_laguerre + x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] + end + # upper half of domain + for j in 1:nquad_laguerre + x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] + end + # fill in upper Gauss-Legendre points + shift = 0.5*(node_cut_high + node_max) + scale = 0.5*(node_max - node_cut_high) + @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift + @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre + + #println("intermediate divergence") + #else # no divergences + # nquad = size(x_legendre,1) + # shift = 0.5*(node_min + node_max) + # scale = 0.5*(node_max - node_min) + # @. x_scaled[1:nquad] = scale*x_legendre + shift + # @. w_scaled[1:nquad] = scale*w_legendre + # #println("no divergence") + # nquad_coord = nquad + end + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad_coord +end + +function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) + zero = 1.0e-6 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + nquad = size(x_legendre,1) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad +end + +# function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid +function ng_low(igrid,ngrid) + return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) +end +# function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid +function ng_hi(igrid,ngrid) + return floor(mk_int, igrid/ngrid) +end +# function returns 1 for nelement >= ielement > 1, 0 for ielement =1 +function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) +end +# function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement +function nel_hi(ielement,nelement) + return 1- floor(mk_int, ielement/nelement) +end + +function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids + vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) + #mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + #G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + #(G_weights[ivpa,ivperp,ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(G2_weights[ivpa,ivperp,ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(G3_weights[ivpa,ivperp,ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H0_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(n_weights[ivpa,ivperp,ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing +end + +function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + vperp,ielement_vperpp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + end + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing +end + +function loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val, ivpa, ivperp) + for ielement_vpap in 1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val, ivpa, ivperp) + + end + return nothing +end + +function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + + #vperp_nodes = get_nodes(vperp,ielement_vperpp) + #vperp_max = vperp_nodes[end] + #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperpp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing +end + +function loop_over_vperp_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + for ielement_vperpp in 1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val, ivpa, ivperp) + end + return nothing end """ @@ -343,6 +803,60 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collision end end +""" +Function to carry out the integration of the revelant +distribution functions to form the required coefficients +for the full-F operator. We assume that the weights are +precalculated. The function takes as arguments the arrays +of coefficients (which we fill), the required distributions, +the precomputed weights, the indicies of the `field' velocities, +and the sizes of the primed vpa and vperp coordinates arrays. +""" +function get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp, + dfspdvpa,dfspdvperp,d2fspdvperpdvpa, + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + ivpa,ivperp,nvpa,nvperp) + d2Gspdvpa2[ivpa,ivperp] = 0.0 + dGspdvperp[ivpa,ivperp] = 0.0 + d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gspdvperp2[ivpa,ivperp] = 0.0 + dHspdvpa[ivpa,ivperp] = 0.0 + dHspdvperp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H0_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + end + end + return nothing +end +""" +Function calculating the fully expanded form of the collision operator +taking floats as arguments. This function is designed to be used at the +lowest level of a coordinate loop, with derivatives and integrals +all previously calculated. +""" + +function Cssp_fully_expanded_form(nussp,ms,msp, + d2fsdvpa2,d2fsdvperp2,d2fsdvperpdvpa,dfsdvpa,dfsdvperp,fs, + d2Gspdvpa2,d2Gspdvperp2,d2Gspdvperpdvpa,dGspdvperp, + dHspdvpa,dHspdvperp,fsp,vperp_val) + ( Cssp = nussp*( d2fsdvpa2*d2Gspdvpa2 + + d2fsdvperp2*d2Gspdvperp2 + + 2.0*d2fsdvperpdvpa*d2Gspdvperpdvpa + + (1.0/(vperp_val^2))*dfsdvperp*dGspdvperp + + 2.0*(1.0 - (ms/msp))*(dfsdvpa*dHspdvpa + dfsdvperp*dHspdvperp) + + (8.0/sqrt(pi))*(ms/msp)*fs*fsp) ) + return Cssp +end + function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_in,dens_in,upar_in,vth_in, composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) @@ -384,9 +898,6 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ @loop_s_r_z is ir iz begin @loop_vperp_vpa ivperp ivpa begin # first compute local (in z,r) Rosenbluth potential coefficients, summing over all s' - # ((fk.Rosenbluth_d2Gdvpa2[ivpa,ivperp], fk.Rosenbluth_d2Gdvperpdvpa[ivpa,ivperp], - #fk.Rosenbluth_d2Gdvperp2[ivpa,ivperp],fk.Rosenbluth_dHdvpa[ivpa,ivperp], - #fk.Rosenbluth_dHdvperp[ivpa,ivperp]) ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[iz,ir,:], From d8f093c8010da6611b9d1780715839c1724d6ea8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 14:10:48 +0100 Subject: [PATCH 116/331] Full integration of Full-F Fokker-Planck operator into the main moment_kinetics code and standalone test script demonstrating the correct implementation. --- fkpl_functional_test.jl | 489 ++++++++++++++++++++++++++++++++++++++++ src/fokker_planck.jl | 146 ++++++++++-- src/time_advance.jl | 9 +- 3 files changed, 627 insertions(+), 17 deletions(-) create mode 100644 fkpl_functional_test.jl diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl new file mode 100644 index 000000000..563c6723d --- /dev/null +++ b/fkpl_functional_test.jl @@ -0,0 +1,489 @@ +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using Dates + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input, species_composition, collisions_input, boltzmann_electron_response +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! +using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! +#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! +using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients +using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! +using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative!, second_derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure +using moment_kinetics.velocity_moments: integrate_over_vspace +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float, allocate_float +using moment_kinetics.time_advance: setup_dummy_and_buffer_arrays + +function get_vth(pres,dens,mass) + return sqrt(pres/(dens*mass)) +end + +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + +function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end +end +""" +L2norm assuming the input is the +absolution error ff_err = ff - ff_exact +We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) +where the volume of velocity space is finite +""" +function L2norm_vspace(ff_err,vpa,vperp) + ff_ones = copy(ff_err) + @. ff_ones = 1.0 + gg = copy(ff_err) + @. gg = (ff_err)^2 + num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + L2norm = sqrt(num/denom) + return L2norm +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) + + # create the coordinate structs + #println("made inputs") + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + #println(vperp.grid) + #println(vperp.wgts) + if discretization == "chebyshev_pseudospectral" + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + #println("using chebyshev_pseudospectral") + elseif discretization == "gausslegendre_pseudospectral" + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + #println("using gausslegendre_pseudospectral") + end + return vpa, vperp, vpa_spectral, vperp_spectral + end + + test_Lagrange_integral = false #true + test_Lagrange_integral_scan = true + + function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + nz = 1 + nr = 1 + n_ion_species = 1 + n_neutral_species = 1 + nvzeta = 1 + nvr = 1 + nvz = 1 + dt = 1.0 + adv_input = advection_input("default", 1.0, 0.0, 0.0) + r_input = grid_input("r", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) + z_input = grid_input("z", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) + r = define_coordinate(r_input) + z = define_coordinate(z_input) + composition = species_composition(n_ion_species, n_ion_species, n_neutral_species, + boltzmann_electron_response, false, 1:n_ion_species, n_ion_species+1:n_ion_species, 1.0, 1.0, + 1.0, 0.0, 0.0, false, 1.0, 1.0, 0.0, allocate_float(n_ion_species)) + nuii = 1.0 + collisions = collisions_input(0.0, 0.0, false, nuii, 0.0, 0.0) + + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=n_ion_species, sn=n_neutral_species, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + scratch_dummy = setup_dummy_and_buffer_arrays(r.n,z.n,vpa.n,vperp.n,1,1,1, + composition.n_ion_species,1) + + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + fs_in = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) + fs_out = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) + + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) + + Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # set up test Maxwellian + denss = 1.0 #3.0/4.0 + upars = 0.0 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + + nussp = nuii + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + denss,upars,vths,ms, + nussp,vpa,vperp,ivpa,ivperp) + end + end + + # initialise the weights + fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + # evaluate the collision operator + explicit_fokker_planck_collisions!(fs_out,fs_in,composition,collisions,dt,fokkerplanck_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + fka = fokkerplanck_arrays + # error analysis of distribution function + begin_serial_region() + @serial_region begin + @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. d2fsdvpa2_err = abs(fka.d2fdvpa2 - d2fsdvpa2_Maxwell) + max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) + println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) + @. dfsdvperp_err = abs(fka.dfdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(fka.d2fdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + @. d2fsdvperp2_err = abs(fka.d2fdvperp2 - d2fsdvperp2_Maxwell) + max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) + println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) + + end + + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. Cssp_err = abs(fka.Cssp_result_vpavperp - Cssp_Maxwell) + max_C_err, max_C_index = findmax(Cssp_err) + println("max_C_err: ",max_C_err," ",max_C_index) + println("spot check C_err: ",Cssp_err[end,end], " Cssp: ",fka.Cssp_result_vpavperp[end,end]) + @. dHdvperp_err = abs(fka.dHdvperp - dHdvperp_Maxwell) + max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",fka.dHdvperp[end,end]) + @. dHdvpa_err = abs(fka.dHdvpa - dHdvpa_Maxwell) + max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",fka.dHdvpa[end,end]) + + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) + end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvperp2_err = abs(fka.d2Gdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",fka.d2Gdvperp2[end,end]) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) + end + @. d2Gdvperpdvpa_err = abs(fka.d2Gdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",fka.d2Gdvperpdvpa[end,end]) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) + end + @. dGdvperp_err = abs(fka.dGdvperp - dGdvperp_Maxwell) + max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",fka.dGdvperp[end,end]) + if plot_dGdvperp + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvpa2_err = abs(fka.d2Gdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",fka.d2Gdvpa2[end,end]) + if plot_d2Gdvpa2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end + + end + _block_synchronize() + if standalone + finalize_comms!() + end + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + (results = (maximum(Cssp_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err))) + return results + end + + if test_Lagrange_integral + ngrid = 9 + nelement = 4 + test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) + end + if test_Lagrange_integral_scan + initialize_comms!() + ngrid = 9 + plot_scan = true + #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] + #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + #nelement_list = Int[2, 4, 8] + #nelement_list = Int[100] + nelement_list = Int[2,4,8,16] + nscan = size(nelement_list,1) + max_C_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) + max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" + + for iscan in 1:nscan + local nelement = nelement_list[iscan] + ((max_C_err[iscan],max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], + max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], + max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan]) + = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) + end + if global_rank[]==0 && plot_scan + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Clabel = L"\epsilon(C)" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) + plot(nelement_list, [max_C_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" + dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" + d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" + d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" + d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" + plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], + xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + end + finalize_comms!() + end + +end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index d1b271ec0..c42aef2b9 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -39,17 +39,23 @@ struct fokkerplanck_arrays_struct H2_weights::MPISharedArray{mk_float,4} H3_weights::MPISharedArray{mk_float,4} #Rosenbluth_G::Array{mk_float,2} - Rosenbluth_d2Gdvpa2::MPISharedArray{mk_float,2} - Rosenbluth_d2Gdvperpdvpa::MPISharedArray{mk_float,2} - Rosenbluth_d2Gdvperp2::MPISharedArray{mk_float,2} + d2Gdvpa2::MPISharedArray{mk_float,2} + d2Gdvperpdvpa::MPISharedArray{mk_float,2} + d2Gdvperp2::MPISharedArray{mk_float,2} + dGdvperp::MPISharedArray{mk_float,2} #Rosenbluth_H::Array{mk_float,2} - Rosenbluth_dHdvpa::MPISharedArray{mk_float,2} - Rosenbluth_dHdvperp::MPISharedArray{mk_float,2} + dHdvpa::MPISharedArray{mk_float,2} + dHdvperp::MPISharedArray{mk_float,2} #Cflux_vpa::MPISharedArray{mk_float,2} #Cflux_vperp::MPISharedArray{mk_float,2} buffer_vpavperp_1::Array{mk_float,2} buffer_vpavperp_2::Array{mk_float,2} - #Cssp_result_vpavperp::Array{mk_float,2} + Cssp_result_vpavperp::MPISharedArray{mk_float,2} + dfdvpa::MPISharedArray{mk_float,2} + d2fdvpa2::MPISharedArray{mk_float,2} + d2fdvperpdvpa::MPISharedArray{mk_float,2} + dfdvperp::MPISharedArray{mk_float,2} + d2fdvperp2::MPISharedArray{mk_float,2} end """ @@ -66,22 +72,29 @@ function allocate_fokkerplanck_arrays(vperp,vpa) H2_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) H3_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) #Rosenbluth_G = allocate_float(nvpa,nvperp) - Rosenbluth_d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) - Rosenbluth_d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) - Rosenbluth_d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + dGdvperp = allocate_shared_float(nvpa,nvperp) #Rosenbluth_H = allocate_float(nvpa,nvperp) - Rosenbluth_dHdvpa = allocate_shared_float(nvpa,nvperp) - Rosenbluth_dHdvperp = allocate_shared_float(nvpa,nvperp) + dHdvpa = allocate_shared_float(nvpa,nvperp) + dHdvperp = allocate_shared_float(nvpa,nvperp) #Cflux_vpa = allocate_shared_float(nvpa,nvperp) #Cflux_vperp = allocate_shared_float(nvpa,nvperp) buffer_vpavperp_1 = allocate_float(nvpa,nvperp) buffer_vpavperp_2 = allocate_float(nvpa,nvperp) - #Cssp_result_vpavperp = allocate_float(nvpa,nvperp) + Cssp_result_vpavperp = allocate_shared_float(nvpa,nvperp) + dfdvpa = allocate_shared_float(nvpa,nvperp) + d2fdvpa2 = allocate_shared_float(nvpa,nvperp) + d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) + dfdvperp = allocate_shared_float(nvpa,nvperp) + d2fdvperp2 = allocate_shared_float(nvpa,nvperp) return fokkerplanck_arrays_struct(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2, - Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - buffer_vpavperp_1,buffer_vpavperp_2) + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, + dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, + Cssp_result_vpavperp, dfdvpa, d2fdvpa2, + d2fdvperpdvpa, dfdvperp, d2fdvperp2) end @@ -206,6 +219,10 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) end + + @serial_region begin + println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end return nothing end @@ -772,7 +789,7 @@ function evaluate_RMJ_collision_operator!(Cssp_out,fs_in,fsp_in,ms,msp,cfreqssp, @views @. Cssp_out = cfreqssp*Cssp_out end -function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, +function explicit_fokker_planck_collisions_old!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) n_ion_species = composition.n_ion_species @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) @@ -857,6 +874,103 @@ function Cssp_fully_expanded_form(nussp,ms,msp, return Cssp end +""" +Evaluate the Fokker Planck collision Operator +using dummy arrays to store the 5 required derivatives. +For a single species, ir, and iz, this routine leaves +in place the fokkerplanck_arrays struct with testable +distributions function derivatives, Rosenbluth potentials, +and collision operator in place. +""" + +function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + + n_ion_species = composition.n_ion_species + @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) + @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) + @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) + @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + + # setup species information + mass = Array{mk_float,1}(undef,n_ion_species) + mass[1] = 1.0 # generalise! + nussp = Array{mk_float,2}(undef,n_ion_species,n_ion_species) + nussp[1,1] = collisions.nuii # generalise! + + # first, compute the require derivatives and store in the buffer arrays + dfdvpa = scratch_dummy.buffer_vpavperpzrs_1 + d2fdvpa2 = scratch_dummy.buffer_vpavperpzrs_2 + d2fdvperpdvpa = scratch_dummy.buffer_vpavperpzrs_3 + dfdvperp = scratch_dummy.buffer_vpavperpzrs_4 + d2fdvperp2 = scratch_dummy.buffer_vpavperpzrs_5 + + begin_s_r_z_vperp_region() + @loop_s_r_z_vperp is ir iz ivperp begin + @views derivative!(vpa.scratch, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) + @. dfdvpa[:,ivperp,iz,ir,is] = vpa.scratch + @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) + @. d2fdvpa2[:,ivperp,iz,ir,is] = vpa.scratch2 + end + if vpa.discretization == "gausslegendre_pseudospectral" + @loop_s_r_z_vperp is ir iz ivperp begin + @views second_derivative!(vpa.scratch2, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) + @. d2fdvpa2[:,ivperp,iz,ir,is] = vpa.scratch2 + end + end + + begin_s_r_z_vpa_region() + + @loop_s_r_z_vpa is ir iz ivpa begin + @views derivative!(vperp.scratch, pdf_in[ivpa,:,iz,ir,is], vperp, vperp_spectral) + @. dfdvperp[ivpa,:,iz,ir,is] = vperp.scratch + @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) + @. d2fdvperp2[ivpa,:,iz,ir,is] = vperp.scratch2 + @views derivative!(vperp.scratch, dfdvpa[ivpa,:,iz,ir,is], vperp, vperp_spectral) + @. d2fdvperpdvpa[ivpa,:,iz,ir,is] = vperp.scratch + end + + # now parallelise over all dimensions and calculate the + # collision operator coefficients and the collision operator + # in one loop, noting that we only require data local to + # each ivpa,ivperp,iz,ir,is now that the derivatives are precomputed + fka = fokkerplanck_arrays + begin_s_r_z_vperp_vpa_region() + @loop_s_r_z is ir iz begin + @loop_vperp_vpa ivperp ivpa begin + for isp in 1:n_ion_species # make sure to sum over all ion species + # get the local (in ivpa, ivperp) values of the coeffs + @views get_local_Cssp_coefficients!(fka.d2Gdvpa2,fka.dGdvperp,fka.d2Gdvperpdvpa, + fka.d2Gdvperp2,fka.dHdvpa,fka.dHdvperp, + dfdvpa[:,:,iz,ir,isp],dfdvperp[:,:,iz,ir,isp],d2fdvperpdvpa[:,:,iz,ir,isp], + fka.G1_weights,fka.H0_weights,fka.H1_weights,fka.H2_weights,fka.H3_weights, + ivpa,ivperp,vpa.n,vperp.n) + + (Cssp = Cssp_fully_expanded_form(nussp[is,isp],mass[is],mass[isp], + d2fdvpa2[ivpa,ivperp,iz,ir,is],d2fdvperp2[ivpa,ivperp,iz,ir,is],d2fdvperpdvpa[ivpa,ivperp,iz,ir,is],dfdvpa[ivpa,ivperp,iz,ir,is],dfdvperp[ivpa,ivperp,iz,ir,is],pdf_in[ivpa,ivperp,iz,ir,is], + fka.d2Gdvpa2[ivpa,ivperp],fka.d2Gdvperp2[ivpa,ivperp],fka.d2Gdvperpdvpa[ivpa,ivperp],fka.dGdvperp[ivpa,ivperp], + fka.dHdvpa[ivpa,ivperp],fka.dHdvperp[ivpa,ivperp],pdf_in[ivpa,ivperp,iz,ir,isp],vperp.grid[ivperp]) ) + pdf_out[ivpa,ivperp,iz,ir,is] += dt*Cssp + # for testing + fka.Cssp_result_vpavperp[ivpa,ivperp] = Cssp + fka.dfdvpa[ivpa,ivperp] = dfdvpa[ivpa,ivperp,iz,ir,is] + fka.d2fdvpa2[ivpa,ivperp] = d2fdvpa2[ivpa,ivperp,iz,ir,is] + fka.d2fdvperpdvpa[ivpa,ivperp] = d2fdvperpdvpa[ivpa,ivperp,iz,ir,is] + fka.dfdvperp[ivpa,ivperp] = dfdvperp[ivpa,ivperp,iz,ir,is] + fka.d2fdvperp2[ivpa,ivperp] = d2fdvperp2[ivpa,ivperp,iz,ir,is] + end + end + end + + return nothing +end + function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_in,dens_in,upar_in,vth_in, composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) diff --git a/src/time_advance.jl b/src/time_advance.jl index 17adaa04c..3292e6b2f 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -4,6 +4,7 @@ module time_advance export setup_time_advance! export time_advance! +export setup_dummy_and_buffer_arrays using MPI using ..type_definitions: mk_float @@ -84,6 +85,9 @@ mutable struct scratch_dummy_arrays # needs to be shared memory buffer_vpavperpzrs_1::MPISharedArray{mk_float,5} buffer_vpavperpzrs_2::MPISharedArray{mk_float,5} + buffer_vpavperpzrs_3::MPISharedArray{mk_float,5} + buffer_vpavperpzrs_4::MPISharedArray{mk_float,5} + buffer_vpavperpzrs_5::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_1::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_2::MPISharedArray{mk_float,5} @@ -505,6 +509,9 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vpavperpzrs_1 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vpavperpzrs_2 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) + buffer_vpavperpzrs_3 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) + buffer_vpavperpzrs_4 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) + buffer_vpavperpzrs_5 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vzvrvzetazsn_1 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) buffer_vzvrvzetazsn_2 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) @@ -527,7 +534,7 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_r_1,buffer_r_2,buffer_r_3,buffer_r_4, buffer_vpavperpzs_1,buffer_vpavperpzs_2,buffer_vpavperpzs_3,buffer_vpavperpzs_4,buffer_vpavperpzs_5,buffer_vpavperpzs_6, buffer_vpavperprs_1,buffer_vpavperprs_2,buffer_vpavperprs_3,buffer_vpavperprs_4,buffer_vpavperprs_5,buffer_vpavperprs_6, - buffer_vpavperpzrs_1,buffer_vpavperpzrs_2, + buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5, buffer_vzvrvzetazsn_1,buffer_vzvrvzetazsn_2,buffer_vzvrvzetazsn_3,buffer_vzvrvzetazsn_4,buffer_vzvrvzetazsn_5,buffer_vzvrvzetazsn_6, buffer_vzvrvzetarsn_1,buffer_vzvrvzetarsn_2,buffer_vzvrvzetarsn_3,buffer_vzvrvzetarsn_4,buffer_vzvrvzetarsn_5,buffer_vzvrvzetarsn_6, buffer_vzvrvzetazrsn) From e165a76ef25b5147f6dac287dca2b7772e7ffd2d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 14:41:10 +0100 Subject: [PATCH 117/331] Changes to allow for evolution in vpa and vperp only, using a diffusion operator. --- src/em_fields.jl | 8 +++++++- src/numerical_dissipation.jl | 6 +++--- src/time_advance.jl | 10 +++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/em_fields.jl b/src/em_fields.jl index b9d5bc735..a796b864a 100644 --- a/src/em_fields.jl +++ b/src/em_fields.jl @@ -140,10 +140,16 @@ function update_phi!(fields, fvec, z, r, composition, z_spectral, r_spectral, sc end end #Ez = - d phi / dz - @views derivative_z!(fields.Ez,-fields.phi, + if z.n > 1 + @views derivative_z!(fields.Ez,-fields.phi, scratch_dummy.buffer_r_1, scratch_dummy.buffer_r_2, scratch_dummy.buffer_r_3,scratch_dummy.buffer_r_4, z_spectral,z) + else + @serial_region begin + fields.Ez[:,:] .= 0.0 + end + end end diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index 2c0c10ae9..27133fbd9 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -47,7 +47,7 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, begin_s_r_z_vperp_region() diffusion_coefficient = num_diss_params.vpa_dissipation_coefficient - if diffusion_coefficient <= 0.0 + if diffusion_coefficient <= 0.0 && !(vpa.n > 1) return nothing end # if T_spectral <: Bool @@ -115,7 +115,7 @@ 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 = num_diss_params.z_dissipation_coefficient - if diffusion_coefficient <= 0.0 + if diffusion_coefficient <= 0.0 && !(z.n > 1) return nothing end @@ -149,7 +149,7 @@ 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 = num_diss_params.r_dissipation_coefficient - if diffusion_coefficient <= 0.0 + if diffusion_coefficient <= 0.0 && !(r.n > 1) return nothing end diff --git a/src/time_advance.jl b/src/time_advance.jl index 3292e6b2f..1c3ec2597 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -201,9 +201,9 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance_ionization_source = false end - advance_vpa_advection = true - advance_z_advection = true - advance_r_advection = true + advance_vpa_advection = true && vpa.n > 1 && z.n > 1 + advance_z_advection = true && z.n > 1 + advance_r_advection = true && r.n > 1 advance_numerical_dissipation = true advance_sources = false advance_continuity = false @@ -234,7 +234,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_fp_F_FM_collisions, explicit_krook_collisions) - if z.discretization == "chebyshev_pseudospectral" + if z.discretization == "chebyshev_pseudospectral" && z.n > 1 # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa # and create the plans for the forward and backward fast Chebyshev transforms z_spectral = setup_chebyshev_pseudospectral(z) @@ -258,7 +258,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, #r.duniform_dgrid .= 1.0 end - if vpa.discretization == "chebyshev_pseudospectral" + if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa # and create the plans for the forward and backward fast Chebyshev transforms vpa_spectral = setup_chebyshev_pseudospectral(vpa) From da8010430a7cfd96d7638b38c24ad518c3a05db2 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 16:34:29 +0100 Subject: [PATCH 118/331] added explicit vperp numerical diffusion --- src/numerical_dissipation.jl | 32 ++++++++++++++++++++++++++++++++ src/time_advance.jl | 4 +++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index 27133fbd9..8deddf940 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -6,6 +6,7 @@ export setup_numerical_dissipation #vpa_boundary_buffer_decay!, #vpa_boundary_buffer_diffusion!, , z_dissipation! export vpa_dissipation! +export vperp_dissipation! export r_dissipation! export z_dissipation! @@ -20,6 +21,7 @@ Base.@kwdef struct numerical_dissipation_parameters 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 end @@ -94,6 +96,36 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, return nothing end +""" +Add diffusion in the vperp direction to suppress oscillations + +Disabled by default. + +The diffusion coefficient is set in the input TOML file by the parameter +``` +[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 + + 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 + + @loop_s_r_z_vpa is ir iz ivpa begin + @views derivative!(vperp.scratch, f_in[ivpa,:,iz,ir,is], vperp, spectral) + @views derivative!(vperp.scratch2, vperp.scratch, vperp, spectral) + @views @. f_out[ivpa,:,iz,ir,is] += dt * diffusion_coefficient * vperp.scratch2 + end + + return nothing +end + """ Add diffusion in the z & r direction to suppress oscillations diff --git a/src/time_advance.jl b/src/time_advance.jl index 1c3ec2597..4e531bb39 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -36,7 +36,7 @@ using ..vperp_advection: update_speed_vperp!, vperp_advection! using ..vpa_advection: update_speed_vpa!, vpa_advection! using ..charge_exchange: charge_exchange_collisions_1V!, charge_exchange_collisions_3V! using ..ionization: ionization_collisions_1V!, ionization_collisions_3V!, constant_ionization_source! -using ..numerical_dissipation: vpa_dissipation!, z_dissipation!, r_dissipation! +using ..numerical_dissipation: vpa_dissipation!, vperp_dissipation!, z_dissipation!, r_dissipation! using ..source_terms: source_terms!, source_terms_manufactured! using ..continuity: continuity_equation! using ..force_balance: force_balance! @@ -1044,6 +1044,8 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, if advance.numerical_dissipation vpa_dissipation!(fvec_out.pdf, fvec_in.pdf, vpa, vpa_spectral, dt, num_diss_params) + vperp_dissipation!(fvec_out.pdf, fvec_in.pdf, vperp, vperp_spectral, dt, + num_diss_params) z_dissipation!(fvec_out.pdf, fvec_in.pdf, z, z_spectral, dt, num_diss_params, scratch_dummy) r_dissipation!(fvec_out.pdf, fvec_in.pdf, r, r_spectral, dt, From 4a7b16087020eadf1db35d181cab6ad687923d09 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 17:10:16 +0100 Subject: [PATCH 119/331] fix boundary conditions to allow nz = 1 evolution --- src/initial_conditions.jl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 46fcfa3a5..c01e109d8 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -578,21 +578,25 @@ function enforce_boundary_conditions!(f, f_r_bc, vpa_adv::T1, z_adv::T2, r_adv::T3, composition, scratch_dummy::T4, advance::T5) where {T1, T2, T3, T4, T5} - 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_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_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_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_diffusion) + end end if vperp.n > 1 begin_s_r_z_vpa_region() @views enforce_vperp_boundary_condition!(f,vperp) end - begin_s_r_vperp_vpa_region() - @views enforce_z_boundary_condition!(f, z_bc, z_adv, vpa, vperp, z, r, composition, - scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, - scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, - scratch_dummy.buffer_vpavperpzrs_1) + if z.n > 1 + begin_s_r_vperp_vpa_region() + @views enforce_z_boundary_condition!(f, z_bc, z_adv, vpa, vperp, z, r, composition, + scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, + scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, + scratch_dummy.buffer_vpavperpzrs_1) + 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, From 3803e11b17273f4cb4d5573169ecdf2526eeaea0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 17:11:19 +0100 Subject: [PATCH 120/331] Support gausslegendre_psuedospectral option for vpa and vperp in the main code. --- src/moment_kinetics_input.jl | 3 +++ src/numerical_dissipation.jl | 17 +++++++++++------ src/time_advance.jl | 5 +++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 974c8f12c..ef3207379 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -977,6 +977,9 @@ function check_input_vpa(vpa, io, vpa_dissipation_coefficient) if vpa.discretization == "chebyshev_pseudospectral" print(io,">vpa.discretization = 'chebyshev_pseudospectral'. ") println(io,"using a Chebyshev pseudospectral method in vpa.") + elseif vpa.discretization == "gausslegendre_pseudospectral" + print(io,">vpa.discretization = 'gausslegendre_pseudospectral'. ") + println(io,"using a Gauss-Legendre-Lobatto pseudospectral method in vpa.") elseif vpa.discretization == "finite_difference" println(io,">vpa.discretization = 'finite_difference', and ", "vpa.fd_option = ", vpa.fd_option, diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index 8deddf940..ed668fedc 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -62,8 +62,13 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, # # expected convergence of Chebyshev pseudospectral scheme # diffusion_coefficient *= (vpa.L/vpa.nelement)^(vpa.ngrid-1) # end - - @loop_s_r_z_vperp is ir iz ivperp begin + if vpa.discretization == "gausslegendre_pseudospectral" + @loop_s_r_z_vperp is ir iz ivperp begin + @views second_derivative!(vpa.scratch2, f_in[:,ivperp,iz,ir,is], vpa, spectral) + @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch2 + end + else + @loop_s_r_z_vperp is ir iz ivperp begin # # Don't want to dissipate the fluid moments, so divide out the Maxwellian, then # # diffuse the result, i.e. # # df/dt += diffusion_coefficient * f_M d2(f/f_M)/dvpa2 @@ -88,11 +93,11 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, # derivative!(vpa.scratch3, vpa.scratch2, vpa, spectral, Val(2)) # @views @. f_out[:,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch * # vpa.scratch3 - vpa.scratch2 .= 1.0 # placeholder for Q in d / d vpa ( Q d f / d vpa) - @views second_derivative!(vpa.scratch, f_in[:,ivperp,iz,ir,is], vpa.scratch2, vpa, spectral) - @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch + vpa.scratch2 .= 1.0 # placeholder for Q in d / d vpa ( Q d f / d vpa) + @views second_derivative!(vpa.scratch, f_in[:,ivperp,iz,ir,is], vpa.scratch2, vpa, spectral) + @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch + end end - return nothing end diff --git a/src/time_advance.jl b/src/time_advance.jl index 4e531bb39..0d18dabc7 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -16,6 +16,7 @@ using ..looping using ..moment_kinetics_structs: scratch_pdf using ..chebyshev: setup_chebyshev_pseudospectral using ..chebyshev: chebyshev_derivative! +using ..gauss_legendre: setup_gausslegendre_pseudospectral using ..velocity_moments: update_moments!, reset_moments_status! using ..velocity_moments: enforce_moment_constraints! using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_qpar!, update_pperp!, update_vth! @@ -264,6 +265,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, vpa_spectral = setup_chebyshev_pseudospectral(vpa) # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) + elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) else # create dummy Bool variable to return in place of the above struct vpa_spectral = false @@ -276,6 +279,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, vperp_spectral = setup_chebyshev_pseudospectral(vperp) # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) + elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) else # create dummy Bool variable to return in place of the above struct vperp_spectral = false From 7fb0b0569465f1b72e6d244691139eff08cea25d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 30 Aug 2023 17:37:01 +0100 Subject: [PATCH 121/331] pdf of f(vpa,vperp) at final time of evolution --- src/post_processing.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/post_processing.jl b/src/post_processing.jl index c2f070921..f8a03d8f2 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -1677,6 +1677,10 @@ function plot_charged_pdf(pdf, vpa, vperp, z, r, end outfile = string(run_name, "_pdf_vs_vperp_vpa", iz0_string, ir0_string, spec_string, ".gif") gif(anim, outfile, fps=5) + + @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,itime_max], xlabel="r", ylabel="vpa", c = :deep, interpolation = :cubic) + outfile = string(run_name, "_pdf_vs_vpa_vperp", ir0_string, iz0_string, spec_string, ".pdf") + savefig(outfile) end # make a gif animation of f(z,r,t) at a given (vpa,vperp) location if pp.animate_f_vs_r_z From 323051d8d8ea78f0656ab6f0d3ea86fdaa398c36 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 31 Aug 2023 09:46:47 +0100 Subject: [PATCH 122/331] Further fixes to permit simulations with nr = nz = 1. Boundary condition routines must also be avoided in the initialisation. --- src/initial_conditions.jl | 14 +++++++------ src/time_advance.jl | 43 ++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index c01e109d8..b5e3a0ad2 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -775,12 +775,14 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, boundary_dis # f_initial contains the initial condition for enforcing a fixed-boundary-value condition # no bc on vz vr vzeta required as no advection in these coordinates - begin_sn_r_vzeta_vr_vz_region() - @views enforce_neutral_z_boundary_condition!(f_neutral, f_charged, boundary_distributions, - z_adv_neutral, z_adv_charged, vz, vr, vzeta, vpa, vperp, z, r, composition, - scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, - scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4, - scratch_dummy.buffer_vzvrvzetazrsn) + if z.n > 1 + begin_sn_r_vzeta_vr_vz_region() + @views enforce_neutral_z_boundary_condition!(f_neutral, f_charged, boundary_distributions, + z_adv_neutral, z_adv_charged, vz, vr, vzeta, vpa, vperp, z, r, composition, + scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, + scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4, + scratch_dummy.buffer_vzvrvzetazrsn) + 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, diff --git a/src/time_advance.jl b/src/time_advance.jl index 0d18dabc7..d9f232d0f 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -153,8 +153,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, manufactured_solns_test = t_input.use_manufactured_solns_for_advance if composition.n_neutral_species > 0 - advance_neutral_z_advection = true - advance_neutral_r_advection = true + advance_neutral_z_advection = true && z.n > 1 + advance_neutral_r_advection = true && r.n > 1 if collisions.charge_exchange > 0.0 if vz.n == vpa.n && vperp.n == 1 && vr.n == 1 && vzeta.n == 1 advance_cx_1V = true @@ -371,17 +371,18 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # with advection in z begin_serial_region() z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) - # initialise the z advection speed - begin_s_r_vperp_vpa_region() - @loop_s is begin - @views update_speed_z!(z_advect[is], fields, vpa, vperp, z, r, 0.0, geometry) + if z.n > 1 + # initialise the z advection speed + begin_s_r_vperp_vpa_region() + @loop_s is begin + @views update_speed_z!(z_advect[is], fields, vpa, vperp, z, r, 0.0, geometry) + end + # enforce prescribed boundary condition in z on the distribution function f + @views enforce_z_boundary_condition!(pdf.charged.unnorm, z.bc, z_advect, vpa, vperp, z, r, composition, + scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, + scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, + scratch_dummy.buffer_vpavperpzrs_1) end - # enforce prescribed boundary condition in z on the distribution function f - @views enforce_z_boundary_condition!(pdf.charged.unnorm, z.bc, z_advect, vpa, vperp, z, r, composition, - scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, - scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, - scratch_dummy.buffer_vpavperpzrs_1) - begin_serial_region() @@ -407,15 +408,15 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, begin_serial_region() vperp_advect = setup_advection(n_ion_species, vperp, vpa, z, r) # initialise the vperp advection speed - begin_serial_region() - @serial_region begin - for is ∈ 1:n_ion_species - @views update_speed_vperp!(vperp_advect[is], vpa, vperp, z, r) - - end - end - # enforce prescribed boundary condition in vperp on the distribution function f if vperp.n > 1 + begin_serial_region() + @serial_region begin + for is ∈ 1:n_ion_species + @views update_speed_vperp!(vperp_advect[is], vpa, vperp, z, r) + + end + end + # enforce prescribed boundary condition in vperp on the distribution function f begin_s_r_z_vpa_region() @views enforce_vperp_boundary_condition!(pdf.charged.norm[:,:,:,:,:],vperp) end @@ -444,7 +445,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, # 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 + if n_neutral_species > 0 && z.n > 1 # initialise the z advection speed begin_sn_vzeta_vr_vz_region() @loop_sn isn begin From 70d34f26c2c3d98f8be81dcc9f305dae9087fa0c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 31 Aug 2023 11:34:23 +0100 Subject: [PATCH 123/331] Fix to GaussLegendre_derivative_vector! to compute the derivative at the lower endpoint (rather than lowest grid point) on the Radau grid for the vperp coordinate. --- GaussLobattoLegendre_test.jl | 6 +++++- src/gauss_legendre.jl | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 9c0903666..4728e153f 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -204,7 +204,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # elemental grid tests ngrid = 17 - nelement = 10 + nelement = 2 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -252,6 +252,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ #print_matrix(y_spectral.L_matrix,"global L matrix",y.n,y.n) #@views y_spectral.K_matrix[1,:] *= (4.0/3.0) #print_matrix(y_spectral.K_matrix,"global K matrix (hacked) ",y.n,y.n) + print_matrix(y_spectral.radau.Dmat,"local radau D matrix Dmat",y.ngrid,y.ngrid) + print_vector(y_spectral.radau.D0,"local radau D matrix D0",y.ngrid) + print_matrix(y_spectral.lobatto.Dmat,"local lobatto D matrix Dmat",y.ngrid,y.ngrid) + print_vector(y_spectral.lobatto.D0,"local lobatto D matrix D0",y.ngrid) Dmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) Dmat_test = Array{Float64,2}(undef,y.ngrid,y.ngrid) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index ae64edaea..7468c35e6 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -119,7 +119,7 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,coord.L,coord.nelement_global,"P0") D0 = allocate_float(coord.ngrid) #@. D0 = Dmat[1,:] # values at lower extreme of element - GaussLegendre_derivative_vector!(D0,1,coord.ngrid,x,w,coord.L,coord.nelement_global) + GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w,coord.L,coord.nelement_global) return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) end @@ -151,7 +151,7 @@ function setup_gausslegendre_pseudospectral_radau(coord) P0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P0",radau=true) D0 = allocate_float(coord.ngrid) - GaussLegendre_derivative_vector!(D0,1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) + GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) end """ @@ -373,12 +373,12 @@ end """ Gauss-Legendre derivative at arbitrary x values, for boundary condition on radau points D0 -- the vector -j -- the index of x where the derivative is evaluated +xj -- the x location where the derivative is evaluated ngrid -- number of points in x x -- the grid from -1, 1 L -- size of physical domain """ -function GaussLegendre_derivative_vector!(D0,j,ngrid,x,wgts,L,nelement_global;radau=false) +function GaussLegendre_derivative_vector!(D0,xj,ngrid,x,wgts,L,nelement_global;radau=false) # coefficient in expansion of # lagrange polys in terms of Legendre polys gamma = allocate_float(ngrid) @@ -394,12 +394,12 @@ function GaussLegendre_derivative_vector!(D0,j,ngrid,x,wgts,L,nelement_global;ra @. D0 = 0.0 for i in 1:ngrid for k in 1:ngrid - D0[i] += wgts[i]*Pl(x[i],k-1)*dnPl(x[j],k-1,1)/gamma[k] + D0[i] += wgts[i]*Pl(x[i],k-1)*dnPl(xj,k-1,1)/gamma[k] end end # set `diagonal' value - D0[j] = 0.0 - D0[j] = -sum(D0[:]) + D0[1] = 0.0 + D0[1] = -sum(D0[:]) @. D0 *= 2.0*float(nelement_global)/L end From 746743d6a7a3e4b906ed275b18e4561f619fb03b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 31 Aug 2023 11:37:11 +0100 Subject: [PATCH 124/331] Addition of vperp regularity condition at the origin which forces d f / d vperp = 0 at vperp 0. This ensures that f(\vec{v}) has continuous derivatives in physical space. Correction of typo: make sure that the collision operator integration weights are precomputed when required. --- src/initial_conditions.jl | 18 +++++++++++++++--- src/time_advance.jl | 6 +++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index b5e3a0ad2..a422de397 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -576,7 +576,7 @@ end function enforce_boundary_conditions!(f, f_r_bc, vpa_bc, z_bc, r_bc, vpa, vperp, z, r, vpa_adv::T1, z_adv::T2, r_adv::T3, composition, - scratch_dummy::T4, advance::T5) where {T1, T2, T3, T4, T5} + scratch_dummy::T4, advance::T5, vperp_spectral::T6) where {T1, T2, T3, T4, T5, T6} if vpa.n > 1 begin_s_r_z_vperp_region() @@ -588,7 +588,7 @@ function enforce_boundary_conditions!(f, f_r_bc, end if vperp.n > 1 begin_s_r_z_vpa_region() - @views enforce_vperp_boundary_condition!(f,vperp) + @views enforce_vperp_boundary_condition!(f,vperp,vperp_spectral) end if z.n > 1 begin_s_r_vperp_vpa_region() @@ -762,11 +762,23 @@ end """ enforce zero boundary condition at vperp -> infinity """ -function enforce_vperp_boundary_condition!(f,vperp) +function enforce_vperp_boundary_condition!(f,vperp,vperp_spectral) nvperp = vperp.n + ngrid = vperp.ngrid + # set zero boundary condition @loop_s_r_z_vpa is ir iz ivpa begin f[ivpa,nvperp,iz,ir,is] = 0.0 end + # set regularity condition d F / d vperp = 0 at vperp = 0 + if vperp.discretization == "gausslegendre_pseudospectral" + D0 = vperp_spectral.radau.D0 + @loop_s_r_z_vpa is ir iz ivpa begin + # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 + f[ivpa,1,iz,ir,is] = -sum(D0[2:ngrid].*f[ivpa,2:ngrid,iz,ir,is])/D0[1] + end + else + println("vperp bc not supported") + end end """ diff --git a/src/time_advance.jl b/src/time_advance.jl index d9f232d0f..90ad50f69 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -331,7 +331,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, 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 - fp_arrays = init_fokker_planck_collisions(vperp,vpa) + fp_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=explicit_fp_collisions) # create the "fields" structure that contains arrays # for the electrostatic potential phi and eventually the electromagnetic fields fields = setup_em_fields(z.n, r.n, drive_input.force_phi, drive_input.amplitude, drive_input.frequency, drive_input.force_Er_zero_at_wall) @@ -418,7 +418,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, end # enforce prescribed boundary condition in vperp on the distribution function f begin_s_r_z_vpa_region() - @views enforce_vperp_boundary_condition!(pdf.charged.norm[:,:,:,:,:],vperp) + @views enforce_vperp_boundary_condition!(pdf.charged.norm[:,:,:,:,:],vperp,vperp_spectral) end ## @@ -1077,7 +1077,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, # enforce boundary conditions in r, z and vpa on the charged particle distribution function enforce_boundary_conditions!(fvec_out.pdf, boundary_distributions.pdf_rboundary_charged, vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance) + scratch_dummy, advance, vperp_spectral) # enforce boundary conditions in r and z on the neutral particle distribution function if n_neutral_species > 0 enforce_neutral_boundary_conditions!(fvec_out.pdf_neutral, fvec_out.pdf, boundary_distributions, From 044c35797fbc69a80c0509a2ed154f71a4fb63a5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 31 Aug 2023 11:38:06 +0100 Subject: [PATCH 125/331] Change default so that vperp vpa animations are written out. --- src/post_processing_input.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/post_processing_input.jl b/src/post_processing_input.jl index 084945f4e..03ba32590 100644 --- a/src/post_processing_input.jl +++ b/src/post_processing_input.jl @@ -56,7 +56,7 @@ const animate_f_vs_vperp_z = true # if animate_f_vs_vperp_r = true, create animation of f(vperp,r) at different time slices const animate_f_vs_vperp_r = false # if animate_f_vs_vperp_vpa = true, create animation of f(vperp,vpa) at different time slices -const animate_f_vs_vperp_vpa = false +const animate_f_vs_vperp_vpa = true # if animate_f_vs_r_z = true, create animation of f(r,z) at different time slices const animate_f_vs_r_z = true # if animate_f_vs_vz_z = true, create animation of f(vz,z) at different time slices From d0a5da81e6adac0eae1d9bc3abaade3614a39a55 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 31 Aug 2023 16:08:38 +0000 Subject: [PATCH 126/331] Attempt to optimise the code by reordering the weights function indices. Moderate speedup. --- src/fokker_planck.jl | 58 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index c42aef2b9..972009714 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -131,9 +131,9 @@ function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_in prefac = sqrt(denom) #end #println(mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) - elliptic_integral_E_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipe(mm)*prefac/pi - elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp] = 2.0*ellipk(mm)/(pi*prefac) - #println(elliptic_integral_K_factor[ivpa,ivperp,ivpap,ivperpp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) + elliptic_integral_E_factor[ivpap,ivperpp,ivpa,ivperp] = 2.0*ellipe(mm)*prefac/pi + elliptic_integral_K_factor[ivpap,ivperpp,ivpa,ivperp] = 2.0*ellipk(mm)/(pi*prefac) + #println(elliptic_integral_K_factor[ivpap,ivperpp,ivpa,ivperp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) end end @@ -199,15 +199,15 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 vpa_val = vpa.grid[ivpa] for ivperpp in 1:vperp.n for ivpap in 1:vpa.n - # G_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - G1_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - # G2_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - # G3_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - H0_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - H1_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - H2_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - H3_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 - #@. n_weights[ivpa,ivperp,ivpap,ivperpp] = 0.0 + # G_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + G1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 end end # loop over elements and grid points within elements on primed coordinate @@ -427,40 +427,40 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - #(G_weights[ivpa,ivperp,ivpap,ivperpp] += + #(G_weights[ivpap,ivperpp,ivpa,ivperp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (G1_weights[ivpa,ivperp,ivpap,ivperpp] += + (G1_weights[ivpap,ivperpp,ivpa,ivperp] += lagrange_poly_vpa*lagrange_poly_vperp* G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(G2_weights[ivpa,ivperp,ivpap,ivperpp] += + #(G2_weights[ivpap,ivperpp,ivpa,ivperp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(G3_weights[ivpa,ivperp,ivpap,ivperpp] += + #(G3_weights[ivpap,ivperpp,ivpa,ivperp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H0_weights[ivpa,ivperp,ivpap,ivperpp] += + (H0_weights[ivpap,ivperpp,ivpa,ivperp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H1_weights[ivpa,ivperp,ivpap,ivperpp] += + (H1_weights[ivpap,ivperpp,ivpa,ivperp] += lagrange_poly_vpa*lagrange_poly_vperp* H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H2_weights[ivpa,ivperp,ivpap,ivperpp] += + (H2_weights[ivpap,ivperpp,ivpa,ivperp] += lagrange_poly_vpa*lagrange_poly_vperp* (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpa,ivperp,ivpap,ivperpp] += + (H3_weights[ivpap,ivperpp,ivpa,ivperp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*(vpa_val - x_kvpa)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(n_weights[ivpa,ivperp,ivpap,ivperpp] += + #(n_weights[ivpap,ivperpp,ivpa,ivperp] += # lagrange_poly_vpa*lagrange_poly_vperp* # x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end @@ -842,14 +842,14 @@ function get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, dHspdvperp[ivpa,ivperp] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa - #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] - d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - dHspdvpa[ivpa,ivperp] += H0_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] + #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H0_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] end end return nothing From 34c217d4e900699d6b4a0ebe8c751fb642024f23 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 1 Sep 2023 10:06:28 +0000 Subject: [PATCH 127/331] Plots for diagnosing simulations with the FP collision operator. --- src/input_structs.jl | 2 ++ src/post_processing.jl | 65 +++++++++++++++++++++++++++++++++--- src/post_processing_input.jl | 14 ++++---- 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/input_structs.jl b/src/input_structs.jl index 9f2b70d8e..14d1a13f9 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -359,6 +359,8 @@ struct pp_input plot_upar0_vs_t::Bool # if plot_ppar0_vs_t = true, create plots of species ppar(z0) vs time plot_ppar0_vs_t::Bool + # if plot_pperp0_vs_t = true, create plots of species pperp(z0) vs time + plot_pperp0_vs_t::Bool # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time plot_qpar0_vs_t::Bool # if plot_dens_vs_z_t = true, create plot of species density vs z and time diff --git a/src/post_processing.jl b/src/post_processing.jl index f8a03d8f2..ae4a07da1 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -446,7 +446,7 @@ function analyze_and_plot_data(path) end # make plots and animations of the phi, Ez and Er - plot_charged_moments_2D(density, parallel_flow, parallel_pressure, time, z, r, iz0, ir0, n_ion_species, + plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) # make plots and animations of the phi, Ez and Er plot_fields_2D(phi, Ez, Er, time, z, r, iz0, ir0, @@ -1829,7 +1829,7 @@ 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, time, z, r, iz0, ir0, n_ion_species, +function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) nr = size(r,1) print("Plotting charged moments data...") @@ -1862,7 +1862,14 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, time outfile = string(run_name, "_density"*description*"_vs_r_z.pdf") savefig(outfile) end - + if pp.plot_dens0_vs_t + @views plot(time, density[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"n_i", label = "") + outfile = string(run_name, "_density"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot(time, density[iz0,ir0,is,:] .- density[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"n_i(t) - n_i(0)", label = "") + outfile = string(run_name, "_delta_density"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end # the parallel flow parallel_flowmin = minimum(parallel_flow[:,:,is,:]) parallel_flowmax = maximum(parallel_flow) @@ -1890,7 +1897,14 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, time outfile = string(run_name, "_parallel_flow"*description*"_vs_r_z.pdf") savefig(outfile) end - + if pp.plot_upar0_vs_t + @views plot(time, parallel_flow[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"u_{i\|\|}(t)", label = "") + outfile = string(run_name, "_parallel_flow"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot(time, parallel_flow[iz0,ir0,is,:] .- parallel_flow[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"u_{i\|\|}(t) - u_{i\|\|}(0)", label = "") + outfile = string(run_name, "_delta_parallel_flow"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end # the parallel pressure parallel_pressuremin = minimum(parallel_pressure[:,:,is,:]) parallel_pressuremax = maximum(parallel_pressure) @@ -1918,6 +1932,49 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, time outfile = string(run_name, "_parallel_pressure"*description*"_vs_r_z.pdf") savefig(outfile) end + if pp.plot_ppar0_vs_t + @views plot(time, parallel_pressure[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"p_{i\|\|}(t)", label = "") + outfile = string(run_name, "_parallel_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot(time, parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"p_{i\|\|}(t) - p_{i\|\|}(0)", label = "") + outfile = string(run_name, "_delta_parallel_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end + # the perpendicular pressure + if pp.plot_pperp0_vs_t + @views plot(time, perpendicular_pressure[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"p_{i\perp}(t)", label = "") + outfile = string(run_name, "_perpendicular_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot(time, perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"p_{i\perp}(t) - p_{i\perp}(0)", label = "") + outfile = string(run_name, "_delta_perpendicular_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end + if pp.plot_ppar0_vs_t && pp.plot_pperp0_vs_t + @views plot([time, time, time] , + [parallel_pressure[iz0,ir0,is,:], perpendicular_pressure[iz0,ir0,is,:], + (2.0/3.0).*perpendicular_pressure[iz0,ir0,is,:] .+ (1.0/3.0).*parallel_pressure[iz0,ir0,is,:]], + xlabel=L"t/c_{ref}", ylabel="", label = [L"p_{i\|\|}(t)" L"p_{i\perp}(t)" L"p_{i}(t)"]) + outfile = string(run_name, "_pressures"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot([time, time, time] , + [parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1], perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1], + (2.0/3.0).*(perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1]).+ + (1.0/3.0).*(parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1])], + xlabel=L"t/c_{ref}", ylabel="", label = [L"p_{i\|\|}(t) - p_{i\|\|}(0)" L"p_{i\perp}(t) - p_{i\perp}(0)" L"p_{i}(t) - p_{i}(0)"]) + outfile = string(run_name, "_delta_pressures"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot([time] , + [(2.0/3.0).*perpendicular_pressure[iz0,ir0,is,:] .+ (1.0/3.0).*parallel_pressure[iz0,ir0,is,:]], + xlabel=L"t/c_{ref}", ylabel=L"p_{i}(t)", label = "") + outfile = string(run_name, "_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot([time] , + [(2.0/3.0).*(perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1]).+ + (1.0/3.0).*(parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1])], + xlabel=L"t/c_{ref}", ylabel=L"p_{i}(t) - p_{i}(0)", label = "") + outfile = string(run_name, "_delta_pressure"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end end println("done.") end diff --git a/src/post_processing_input.jl b/src/post_processing_input.jl index 03ba32590..a97a1acb0 100644 --- a/src/post_processing_input.jl +++ b/src/post_processing_input.jl @@ -17,12 +17,14 @@ const plot_phi0_vs_t = true const plot_phi_vs_z_t = true # if animate_phi_vs_z = true, create animation of phi(z) at different time slices const animate_phi_vs_z = true -# if plot_dens0_vs_t = true, create plots of species density(z0) vs time +# if plot_dens0_vs_t = true, create plots of species density(z0,r0) vs time const plot_dens0_vs_t = true -# if plot_upar0_vs_t = true, create plots of species upar(z0) vs time -const plot_upar0_vs_t = false -# if plot_ppar0_vs_t = true, create plots of species ppar(z0) vs time -const plot_ppar0_vs_t = false +# if plot_upar0_vs_t = true, create plots of species upar(z0,r0) vs time +const plot_upar0_vs_t = true +# if plot_ppar0_vs_t = true, create plots of species ppar(z0,r0) vs time +const plot_ppar0_vs_t = true +# if plot_pperp0_vs_t = true, create plots of species pperp(z0,r0) vs time +const plot_pperp0_vs_t = true # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time const plot_qpar0_vs_t = false # if plot_dens_vs_z_t = true, create heatmap of species density vs z and time @@ -117,7 +119,7 @@ const ivr0 = -1 const ivzeta0 = -1 pp = pp_input(calculate_frequencies, plot_phi0_vs_t, plot_phi_vs_z_t, - animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_qpar0_vs_t, + animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_pperp0_vs_t, plot_qpar0_vs_t, plot_dens_vs_z_t, plot_upar_vs_z_t, plot_ppar_vs_z_t, plot_qpar_vs_z_t, animate_dens_vs_z, animate_upar_vs_z, animate_f_vs_vpa_z, animate_f_vs_vpa_z0, animate_f_vs_z0_vpa, From e7b6546089a5f7743946f6127bb54d36b5e4aaa2 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 1 Sep 2023 10:55:48 +0000 Subject: [PATCH 128/331] Plot of vth vs time and plot of the L2 norm of f - f_M to test how the distribution approaches the Maxwellian distribution. --- src/input_structs.jl | 2 ++ src/post_processing.jl | 62 ++++++++++++++++++++++++++++++++++-- src/post_processing_input.jl | 4 ++- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/input_structs.jl b/src/input_structs.jl index 14d1a13f9..b7ed3483c 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -361,6 +361,8 @@ struct pp_input plot_ppar0_vs_t::Bool # if plot_pperp0_vs_t = true, create plots of species pperp(z0) vs time plot_pperp0_vs_t::Bool + # if plot_vth0_vs_t = true, create plots of species vth(z0) vs time + plot_vth0_vs_t::Bool # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time plot_qpar0_vs_t::Bool # if plot_dens_vs_z_t = true, create plot of species density vs z and time diff --git a/src/post_processing.jl b/src/post_processing.jl index ae4a07da1..3abc44c64 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -446,7 +446,7 @@ function analyze_and_plot_data(path) end # make plots and animations of the phi, Ez and Er - plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, time, z, r, iz0, ir0, n_ion_species, + plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, thermal_speed, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) # make plots and animations of the phi, Ez and Er plot_fields_2D(phi, Ez, Er, time, z, r, iz0, ir0, @@ -458,6 +458,14 @@ function analyze_and_plot_data(path) plot_charged_pdf(ff, vpa_local, vperp_local, z_local, r_local, ivpa0, ivperp0, iz0, ir0, spec_type, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) + Maxwellian_diagnostic = true + if Maxwellian_diagnostic + pressure = copy(parallel_pressure) + @. pressure = (2.0*perpendicular_pressure + parallel_pressure)/3.0 + plot_Maxwellian_diagnostic(ff[:,:,iz0,ir0,:,:], density[iz0,ir0,:,:], + parallel_flow[iz0,ir0,:,:], thermal_speed[iz0,ir0,:,:], vpa_local, vpa_local_wgts, + vperp_local, vperp_local_wgts, time, iz0, ir0, run_name, n_ion_species) + end # make plots and animations of the neutral pdf if n_neutral_species > 0 spec_type = "neutral" @@ -1829,7 +1837,7 @@ 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, perpendicular_pressure, time, z, r, iz0, ir0, n_ion_species, +function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, thermal_speed, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) nr = size(r,1) print("Plotting charged moments data...") @@ -1949,6 +1957,7 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp outfile = string(run_name, "_delta_perpendicular_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end + # the total pressure if pp.plot_ppar0_vs_t && pp.plot_pperp0_vs_t @views plot([time, time, time] , [parallel_pressure[iz0,ir0,is,:], perpendicular_pressure[iz0,ir0,is,:], @@ -1975,10 +1984,59 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp outfile = string(run_name, "_delta_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end + # the thermal speed + if pp.plot_vth0_vs_t + @views plot(time, thermal_speed[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"v_{i,th}(t)", label = "") + outfile = string(run_name, "_thermal_speed"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + @views plot(time, thermal_speed[iz0,ir0,is,:] .- thermal_speed[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"v_{i,th}(t) - v_{i,th}(0)", label = "") + outfile = string(run_name, "_delta_thermal_speed"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end end println("done.") end +function plot_Maxwellian_diagnostic(ff, density, parallel_flow, thermal_speed, vpa_local, vpa_local_wgts, + vperp_local, vperp_local_wgts, time, iz0, ir0, run_name, n_ion_species) + ff_Maxwellian = copy(ff) + ff_ones = copy(ff) + nvpa = size(vpa_local,1) + nvperp = size(vperp_local,1) + ntime = size(time,1) + if nvperp > 1 + pvth = 3 + else + pvth = 1 + end + for it in 1:ntime + for is in 1:n_ion_species + for ivperp in 1:nvperp + for ivpa in 1:nvpa + ff_Maxwellian[ivpa,ivperp,is,it] = (density[is,it]/thermal_speed[is,it]^pvth)* + exp(- (((vpa_local[ivpa] - parallel_flow[is,it])^2) + + (vperp_local[ivperp]^2) )/(thermal_speed[is,it]^2) ) + ff_ones[ivpa,ivperp,is,it] = 1.0 + end + end + end + end + # form the L2 norm + ff_norm = copy(time) + for is in 1:n_ion_species + for it in 1:ntime + @views ff_norm[it] = integrate_over_vspace( (ff[:,:,is,it] .- ff_Maxwellian[:,:,is,it]).^2 , vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) + @views ff_norm[it] /= integrate_over_vspace(ff_ones[:,:,is,it], vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) + end + iz0_string = string("_iz0", string(iz0)) + ir0_string = string("_ir0", string(ir0)) + description = "_ion_spec"*string(is)*"_"*iz0_string*ir0_string + @views plot(time, ff_norm, xlabel=L"t/c_{ref}", ylabel=L"L^2(f - f_M)(t)", label = "") + outfile = string(run_name, "_L2_Maxwellian_norm"*description*"_vs_t.pdf") + savefig(outfile) + end + return nothing +end function plot_charged_pdf_2D_at_wall(run_name) print("Plotting charged pdf data at wall boundaries...") # open a dfn file diff --git a/src/post_processing_input.jl b/src/post_processing_input.jl index a97a1acb0..0b4b88532 100644 --- a/src/post_processing_input.jl +++ b/src/post_processing_input.jl @@ -25,6 +25,8 @@ const plot_upar0_vs_t = true const plot_ppar0_vs_t = true # if plot_pperp0_vs_t = true, create plots of species pperp(z0,r0) vs time const plot_pperp0_vs_t = true +# if plot_vth0_vs_t = true, create plots of species vth(z0,r0) vs time +const plot_vth0_vs_t = true # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time const plot_qpar0_vs_t = false # if plot_dens_vs_z_t = true, create heatmap of species density vs z and time @@ -119,7 +121,7 @@ const ivr0 = -1 const ivzeta0 = -1 pp = pp_input(calculate_frequencies, plot_phi0_vs_t, plot_phi_vs_z_t, - animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_pperp0_vs_t, plot_qpar0_vs_t, + animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_pperp0_vs_t, plot_vth0_vs_t, plot_qpar0_vs_t, plot_dens_vs_z_t, plot_upar_vs_z_t, plot_ppar_vs_z_t, plot_qpar_vs_z_t, animate_dens_vs_z, animate_upar_vs_z, animate_f_vs_vpa_z, animate_f_vs_vpa_z0, animate_f_vs_z0_vpa, From b3fa53ec9b66134d4fc2bd8f02b80caaeeef4d7f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 6 Sep 2023 11:06:16 +0000 Subject: [PATCH 129/331] Fix typo -- make sure that both boundary conditions are imposed by separating items in if else list for vpa boundary conditions. --- src/initial_conditions.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index a422de397..476a41d0a 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -751,7 +751,8 @@ function enforce_vpa_boundary_condition_local!(f::T, bc, adv_speed, vpa_diffusio if bc == "zero" if dvpadt > zero || vpa_diffusion f[1] = 0.0 # -infty forced to zero - elseif dvpadt < zero || vpa_diffusion + end + if dvpadt < zero || vpa_diffusion f[end] = 0.0 # +infty forced to zero end elseif bc == "periodic" From 4029a31feb56e5c5d90156868f4382c9394b258f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 6 Sep 2023 14:38:32 +0000 Subject: [PATCH 130/331] Addition of entropy production diagnostic. Note that this diagnostic requires an additional distribution function sized dummy array. The Fokker Planck operator thus uses 6 distribution function sized dummy arrays to store the various terms needed for the calculation. --- src/file_io.jl | 15 ++++++++++++++- src/fokker_planck.jl | 34 +++++++++++++++++++++++++++------- src/input_structs.jl | 2 ++ src/post_processing.jl | 21 ++++++++++++++++----- src/post_processing_input.jl | 4 +++- src/time_advance.jl | 19 ++++++++++++++----- src/velocity_moments.jl | 7 +++++-- 7 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/file_io.jl b/src/file_io.jl index 398d06eb7..7485eb7dc 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -60,6 +60,8 @@ struct io_moments_info{Tfile, Ttime, Tphi, Tmomi, Tmomn} parallel_heat_flux::Tmomi # handle for the charged species thermal speed thermal_speed::Tmomi + # handle for the charged species entropy production + entropy_production::Tmomi # handle for the neutral species density density_neutral::Tmomn @@ -380,6 +382,13 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, parallel_io=parallel_io, description="charged species thermal speed", units="c_ref") + + # io_vth is the handle for the ion thermal speed + io_dSdt = create_dynamic_variable!(dynamic, "entropy_production", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="charged species entropy production", + units="") # io_density_neutral is the handle for the neutral particle density io_density_neutral = create_dynamic_variable!(dynamic, "density_neutral", mk_float, z, r; @@ -417,7 +426,7 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, units="c_ref") 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_density_neutral, io_uz_neutral, + io_ppar, io_pperp, io_qpar, io_vth, io_dSdt, io_density_neutral, io_uz_neutral, io_pz_neutral, io_qz_neutral, io_thermal_speed_neutral, parallel_io) end @@ -587,6 +596,8 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, z, r, n_ion_species) append_to_dynamic_var(io_moments.thermal_speed, moments.charged.vth, t_idx, z, r, n_ion_species) + append_to_dynamic_var(io_moments.entropy_production, moments.charged.dSdt, t_idx, z, r, + n_ion_species) if n_neutral_species > 0 append_to_dynamic_var(io_moments.density_neutral, moments.neutral.dens, t_idx, z, r, n_neutral_species) @@ -653,6 +664,8 @@ end moments.charged.qpar, t_idx, z, r, n_ion_species) append_to_dynamic_var(io_moments.thermal_speed.data, moments.charged.vth, t_idx, z, r, n_ion_species) + append_to_dynamic_var(io_moments.entropy_production.data, moments.charged.dSdt, + t_idx, z, r, n_ion_species) if n_neutral_species > 0 append_to_dynamic_var(io_moments.density_neutral.data, moments.neutral.dens, t_idx, z, r, diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 972009714..fef8a6da4 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -883,8 +883,8 @@ distributions function derivatives, Rosenbluth potentials, and collision operator in place. """ -function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) +function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral; diagnose_entropy_production = true) n_ion_species = composition.n_ion_species @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) @@ -897,19 +897,26 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collision @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(dSdt,1) || throw(BoundsError(dSdt)) + @boundscheck r.n == size(dSdt,2) || throw(BoundsError(dSdt)) + @boundscheck n_ion_species == size(dSdt,3) || throw(BoundsError(dSdt)) # setup species information mass = Array{mk_float,1}(undef,n_ion_species) mass[1] = 1.0 # generalise! nussp = Array{mk_float,2}(undef,n_ion_species,n_ion_species) nussp[1,1] = collisions.nuii # generalise! - + + # assign Cssp to a dummy array + Cssp = scratch_dummy.dummy_s + # first, compute the require derivatives and store in the buffer arrays dfdvpa = scratch_dummy.buffer_vpavperpzrs_1 d2fdvpa2 = scratch_dummy.buffer_vpavperpzrs_2 d2fdvperpdvpa = scratch_dummy.buffer_vpavperpzrs_3 dfdvperp = scratch_dummy.buffer_vpavperpzrs_4 d2fdvperp2 = scratch_dummy.buffer_vpavperpzrs_5 + logfC = scratch_dummy.buffer_vpavperpzrs_5 begin_s_r_z_vperp_region() @loop_s_r_z_vperp is ir iz ivperp begin @@ -952,22 +959,35 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,composition,collision fka.G1_weights,fka.H0_weights,fka.H1_weights,fka.H2_weights,fka.H3_weights, ivpa,ivperp,vpa.n,vperp.n) - (Cssp = Cssp_fully_expanded_form(nussp[is,isp],mass[is],mass[isp], + (Cssp[isp] = Cssp_fully_expanded_form(nussp[is,isp],mass[is],mass[isp], d2fdvpa2[ivpa,ivperp,iz,ir,is],d2fdvperp2[ivpa,ivperp,iz,ir,is],d2fdvperpdvpa[ivpa,ivperp,iz,ir,is],dfdvpa[ivpa,ivperp,iz,ir,is],dfdvperp[ivpa,ivperp,iz,ir,is],pdf_in[ivpa,ivperp,iz,ir,is], fka.d2Gdvpa2[ivpa,ivperp],fka.d2Gdvperp2[ivpa,ivperp],fka.d2Gdvperpdvpa[ivpa,ivperp],fka.dGdvperp[ivpa,ivperp], fka.dHdvpa[ivpa,ivperp],fka.dHdvperp[ivpa,ivperp],pdf_in[ivpa,ivperp,iz,ir,isp],vperp.grid[ivperp]) ) - pdf_out[ivpa,ivperp,iz,ir,is] += dt*Cssp + pdf_out[ivpa,ivperp,iz,ir,is] += dt*Cssp[isp] # for testing - fka.Cssp_result_vpavperp[ivpa,ivperp] = Cssp + fka.Cssp_result_vpavperp[ivpa,ivperp] = Cssp[isp] fka.dfdvpa[ivpa,ivperp] = dfdvpa[ivpa,ivperp,iz,ir,is] fka.d2fdvpa2[ivpa,ivperp] = d2fdvpa2[ivpa,ivperp,iz,ir,is] fka.d2fdvperpdvpa[ivpa,ivperp] = d2fdvperpdvpa[ivpa,ivperp,iz,ir,is] fka.dfdvperp[ivpa,ivperp] = dfdvperp[ivpa,ivperp,iz,ir,is] fka.d2fdvperp2[ivpa,ivperp] = d2fdvperp2[ivpa,ivperp,iz,ir,is] end + # store the entropy production + # we use ln|f| to avoid problems with f < 0. This is ok if C_s[f,f] is small where f ~< 0 + # + 1.0e-15 in case f = 0 exactly + #println(Cssp[:]) + #println(pdf_in[ivpa,ivperp,iz,ir,is]) + logfC[ivpa,ivperp,iz,ir,is] = log(abs(pdf_in[ivpa,ivperp,iz,ir,is]) + 1.0e-15)*sum(Cssp[:]) + #println(dfdvpa[ivpa,ivperp,iz,ir,is]) end end - + if diagnose_entropy_production + # compute entropy production diagnostic + begin_s_r_z_region() + @loop_s_r_z is ir iz begin + @views dSdt[iz,ir,is] = -integrate_over_vspace(logfC[:,:,iz,ir,is], vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + end + end return nothing end diff --git a/src/input_structs.jl b/src/input_structs.jl index b7ed3483c..599431180 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -363,6 +363,8 @@ struct pp_input plot_pperp0_vs_t::Bool # if plot_vth0_vs_t = true, create plots of species vth(z0) vs time plot_vth0_vs_t::Bool + # if plot_dSdt0_vs_t = true, create plots of species vth(z0) vs time + plot_dSdt0_vs_t::Bool # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time plot_qpar0_vs_t::Bool # if plot_dens_vs_z_t = true, create plot of species density vs z and time diff --git a/src/post_processing.jl b/src/post_processing.jl index 3abc44c64..66faf5004 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -195,7 +195,8 @@ function allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,nt perpendicular_pressure = allocate_float(nz_global,nr_global,n_ion_species,ntime) parallel_heat_flux = allocate_float(nz_global,nr_global,n_ion_species,ntime) thermal_speed = allocate_float(nz_global,nr_global,n_ion_species,ntime) - return density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed + entropy_production = allocate_float(nz_global,nr_global,n_ion_species,ntime) + return density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production end function allocate_global_zr_neutral_moments(nz_global,nr_global,n_neutral_species,ntime) @@ -324,7 +325,7 @@ function analyze_and_plot_data(path) # allocate arrays to contain the global fields as a function of (z,r,t) phi, Ez, Er = allocate_global_zr_fields(nz_global,nr_global,ntime) - density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed = + density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production = allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,ntime) if n_neutral_species > 0 neutral_density, neutral_uz, neutral_pz, neutral_qz, neutral_thermal_speed = @@ -348,6 +349,7 @@ function analyze_and_plot_data(path) read_distributed_zr_data!(perpendicular_pressure,"perpendicular_pressure",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(parallel_heat_flux,"parallel_heat_flux",run_name,"moments",nblocks,nz_local,nr_local) read_distributed_zr_data!(thermal_speed,"thermal_speed",run_name,"moments",nblocks,nz_local,nr_local) + read_distributed_zr_data!(entropy_production,"entropy_production",run_name,"moments",nblocks,nz_local,nr_local) # neutral particle moments if n_neutral_species > 0 read_distributed_zr_data!(neutral_density,"density_neutral",run_name,"moments",nblocks,nz_local,nr_local) @@ -446,7 +448,8 @@ function analyze_and_plot_data(path) end # make plots and animations of the phi, Ez and Er - plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, thermal_speed, time, z, r, iz0, ir0, n_ion_species, + plot_charged_moments_2D(density, parallel_flow, parallel_pressure, + perpendicular_pressure, thermal_speed, entropy_production, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) # make plots and animations of the phi, Ez and Er plot_fields_2D(phi, Ez, Er, time, z, r, iz0, ir0, @@ -1686,7 +1689,7 @@ function plot_charged_pdf(pdf, vpa, vperp, z, r, outfile = string(run_name, "_pdf_vs_vperp_vpa", iz0_string, ir0_string, spec_string, ".gif") gif(anim, outfile, fps=5) - @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,itime_max], xlabel="r", ylabel="vpa", c = :deep, interpolation = :cubic) + @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,itime_max], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) outfile = string(run_name, "_pdf_vs_vpa_vperp", ir0_string, iz0_string, spec_string, ".pdf") savefig(outfile) end @@ -1837,9 +1840,11 @@ 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, perpendicular_pressure, thermal_speed, time, z, r, iz0, ir0, n_ion_species, +function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, thermal_speed, + entropy_production, 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...") for is in 1:n_ion_species description = "_ion_spec"*string(is)*"_" @@ -1993,6 +1998,12 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp outfile = string(run_name, "_delta_thermal_speed"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end + # the entropy production + if pp.plot_dSdt0_vs_t + @views plot(time[2:ntime], entropy_production[iz0,ir0,is,2:ntime], xlabel=L"t/c_{ref}", ylabel=L"\dot{S}(t)", label = "") + outfile = string(run_name, "_entropy production"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end end println("done.") end diff --git a/src/post_processing_input.jl b/src/post_processing_input.jl index 0b4b88532..5847e5904 100644 --- a/src/post_processing_input.jl +++ b/src/post_processing_input.jl @@ -27,6 +27,8 @@ const plot_ppar0_vs_t = true const plot_pperp0_vs_t = true # if plot_vth0_vs_t = true, create plots of species vth(z0,r0) vs time const plot_vth0_vs_t = true +# if plot_dSdt0_vs_t = true, create plots of species dSdt(z0,r0) vs time +const plot_dSdt0_vs_t = true # if plot_qpar0_vs_t = true, create plots of species qpar(z0) vs time const plot_qpar0_vs_t = false # if plot_dens_vs_z_t = true, create heatmap of species density vs z and time @@ -121,7 +123,7 @@ const ivr0 = -1 const ivzeta0 = -1 pp = pp_input(calculate_frequencies, plot_phi0_vs_t, plot_phi_vs_z_t, - animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_pperp0_vs_t, plot_vth0_vs_t, plot_qpar0_vs_t, + animate_phi_vs_z, plot_dens0_vs_t, plot_upar0_vs_t, plot_ppar0_vs_t, plot_pperp0_vs_t, plot_vth0_vs_t, plot_dSdt0_vs_t, plot_qpar0_vs_t, plot_dens_vs_z_t, plot_upar_vs_z_t, plot_ppar_vs_z_t, plot_qpar_vs_z_t, animate_dens_vs_z, animate_upar_vs_z, animate_f_vs_vpa_z, animate_f_vs_vpa_z0, animate_f_vs_z0_vpa, diff --git a/src/time_advance.jl b/src/time_advance.jl index 90ad50f69..42ee7d0bb 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -53,6 +53,8 @@ using ..advection: advection_info @debug_detect_redundant_block_synchronize using ..communication: debug_detect_redundant_is_active mutable struct scratch_dummy_arrays + dummy_s::Array{mk_float,1} + dummy_sr::Array{mk_float,2} dummy_vpavperp::Array{mk_float,2} dummy_zr::MPISharedArray{mk_float,2} @@ -89,6 +91,7 @@ mutable struct scratch_dummy_arrays buffer_vpavperpzrs_3::MPISharedArray{mk_float,5} buffer_vpavperpzrs_4::MPISharedArray{mk_float,5} buffer_vpavperpzrs_5::MPISharedArray{mk_float,5} + buffer_vpavperpzrs_6::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_1::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_2::MPISharedArray{mk_float,5} @@ -484,7 +487,9 @@ end function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies_ion,nspecies_neutral) - dummy_sr = allocate_float(nr, nspecies_ion) + dummy_s = allocate_float(nspecies_ion) + + dummy_sr = allocate_float(nr, nspecies_ion) dummy_zr = allocate_shared_float(nz, nr) dummy_vpavperp = allocate_float(nvpa, nvperp) @@ -518,6 +523,7 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vpavperpzrs_3 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vpavperpzrs_4 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vpavperpzrs_5 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) + buffer_vpavperpzrs_6 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vzvrvzetazsn_1 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) buffer_vzvrvzetazsn_2 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) @@ -535,12 +541,12 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vzvrvzetazrsn = allocate_shared_float(nvz,nvr,nvzeta,nz,nr,nspecies_neutral) - return scratch_dummy_arrays(dummy_sr,dummy_vpavperp,dummy_zr, + return scratch_dummy_arrays(dummy_s, dummy_sr,dummy_vpavperp,dummy_zr, buffer_z_1,buffer_z_2,buffer_z_3,buffer_z_4, buffer_r_1,buffer_r_2,buffer_r_3,buffer_r_4, buffer_vpavperpzs_1,buffer_vpavperpzs_2,buffer_vpavperpzs_3,buffer_vpavperpzs_4,buffer_vpavperpzs_5,buffer_vpavperpzs_6, buffer_vpavperprs_1,buffer_vpavperprs_2,buffer_vpavperprs_3,buffer_vpavperprs_4,buffer_vpavperprs_5,buffer_vpavperprs_6, - buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5, + buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5,buffer_vpavperpzrs_6, buffer_vzvrvzetazsn_1,buffer_vzvrvzetazsn_2,buffer_vzvrvzetazsn_3,buffer_vzvrvzetazsn_4,buffer_vzvrvzetazsn_5,buffer_vzvrvzetazsn_6, buffer_vzvrvzetarsn_1,buffer_vzvrvzetarsn_2,buffer_vzvrvzetarsn_3,buffer_vzvrvzetarsn_4,buffer_vzvrvzetarsn_5,buffer_vzvrvzetarsn_6, buffer_vzvrvzetazrsn) @@ -1059,8 +1065,11 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, end if advance.explicit_fp_collisions - explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf,composition,collisions,dt,fp_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + update_entropy_diagnostic = (istage == 1) + explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf, moments.charged.dSdt, composition,collisions,dt,fp_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, + diagnose_entropy_production = update_entropy_diagnostic) + #println(moments.charged.dSdt) end if advance.explicit_fp_F_FM_collisions explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index c436326f1..20432c86d 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -59,6 +59,8 @@ mutable struct moments_charged_substruct qpar::MPISharedArray{mk_float,3} # this is the thermal speed based on the parallel temperature Tpar = ppar/dens: vth = sqrt(2*Tpar/m) vth::MPISharedArray{mk_float,3} + # this is the entropy production dS/dt = - ln f sum_s' C_ss' [F_s,F_s'] + dSdt::MPISharedArray{mk_float,3} end """ @@ -102,9 +104,10 @@ function create_moments_charged(nz, nr, n_species) # allocate array of Bools that indicate if the parallel flow is updated for each species # allocate array used for the thermal speed thermal_speed = allocate_shared_float(nz, nr, n_species) - + entropy_production = allocate_shared_float(nz, nr, n_species) # return struct containing arrays needed to update moments - return moments_charged_substruct(density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed) + return moments_charged_substruct(density, parallel_flow, parallel_pressure, + perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production) end # neutral particles have natural mean velocities From c75ec4356b55ead0bd1c86b5c6c9514a7f153942 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 6 Sep 2023 15:23:19 +0000 Subject: [PATCH 131/331] Correct the calculation of qpar appropriate to the 2V case with standard drift kinetics (this change may break tests based on 1D1V results), in anticipation of calculating numerical conserving terms with the moment functions in the collision operator. --- src/initial_conditions.jl | 12 ++++++------ src/time_advance.jl | 2 +- src/velocity_moments.jl | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 476a41d0a..284f95b67 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -16,7 +16,7 @@ export enforce_neutral_z_boundary_condition! using SpecialFunctions: erfc # modules using ..type_definitions: mk_float -using ..array_allocation: allocate_shared_float +using ..array_allocation: allocate_shared_float, allocate_float using ..bgk: init_bgk_pdf! using ..communication using ..calculus: reconcile_element_boundaries_MPI! @@ -88,9 +88,9 @@ function init_pdf_and_moments(vz, vr, vzeta, vpa, vperp, z, r, composition, geom neutral = create_moments_neutral(z.n, r.n, n_neutral_species_alloc) moments = moments_struct(charged,neutral) pdf = create_pdf(vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species_alloc) - + dummy_vpavperp = allocate_float(vpa.n, vperp.n) # integration dummy array if use_manufactured_solns - init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, geometry, composition) + init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, geometry, composition, dummy_vpavperp) else @serial_region begin #charged particles @@ -117,7 +117,7 @@ function init_pdf_and_moments(vz, vr, vzeta, vpa, vperp, z, r, composition, geom init_pdf!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, species) # calculate the self-consistent initial parallel heat flux and pressure from the initial un-normalised pdf - update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) + update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar, dummy_vpavperp) # need neutral version!!! update_qpar!(moments.charged.qpar, moments.charged.qpar_updated, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.vpa_norm_fac) # calculate self-consistent neutral moments if n_neutral_species > 0 @@ -191,7 +191,7 @@ function init_pdf!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, return nothing end -function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, geometry,composition) +function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species, geometry,composition,dummy_vpavperp) manufactured_solns_list = manufactured_solutions(r.L,z.L,r.bc,z.bc,geometry,composition,r.n,vperp.n) dfni_func = manufactured_solns_list.dfni_func densi_func = manufactured_solns_list.densi_func @@ -209,7 +209,7 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, # update upar, ppar, qpar, vth consistent with manufactured solns update_density!(moments.charged.dens, pdf.charged.unnorm, vpa, vperp, z, r, composition) update_upar!(moments.charged.upar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.dens) - update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) + update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar, dummy_vpavperp) update_ppar!(moments.charged.ppar, pdf.charged.unnorm, vpa, vperp, z, r, composition, moments.charged.upar) update_pperp!(moments.charged.pperp, pdf.charged.unnorm, vpa, vperp, z, r, composition) update_vth!(moments.charged.vth, moments.charged.ppar, moments.charged.pperp, moments.charged.dens, vperp, z, r, composition) diff --git a/src/time_advance.jl b/src/time_advance.jl index 42ee7d0bb..2a5255a29 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -820,7 +820,7 @@ function rk_update!(scratch, pdf, moments, fields, vz, vr, vzeta, vpa, vperp, z, rethrow(e) end # update the parallel heat flux - update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition) + update_qpar!(moments.charged.qpar, pdf.charged.unnorm, vpa, vperp, z, r, composition, new_scratch.upar, scratch_dummy.dummy_vpavperp) ## # update the neutral particle distribution and moments diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 20432c86d..4dd20e3d2 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -31,6 +31,7 @@ export get_upar export get_ppar export get_pperp export get_pressure +export get_qpar using ..type_definitions: mk_float using ..array_allocation: allocate_shared_float, allocate_bool @@ -59,7 +60,7 @@ mutable struct moments_charged_substruct qpar::MPISharedArray{mk_float,3} # this is the thermal speed based on the parallel temperature Tpar = ppar/dens: vth = sqrt(2*Tpar/m) vth::MPISharedArray{mk_float,3} - # this is the entropy production dS/dt = - ln f sum_s' C_ss' [F_s,F_s'] + # 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} end @@ -337,20 +338,20 @@ end NB: if this function is called and if ppar_updated is false, then the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density """ -function update_qpar!(qpar, pdf, vpa, vperp, z, r, composition) +function update_qpar!(qpar, pdf, vpa, vperp, z, r, composition, upar, dummy_vpavperp) @boundscheck composition.n_ion_species == size(qpar,3) || throw(BoundsError(qpar)) begin_s_r_z_region() @loop_s is begin - @views update_qpar_species!(qpar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r) + @views update_qpar_species!(qpar[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r, upar[:,:,is], dummy_vpavperp) end end """ calculate the updated parallel heat flux (qpar) for a given species """ -function update_qpar_species!(qpar, ff, vpa, vperp, z, r) +function update_qpar_species!(qpar, ff, vpa, vperp, z, r, upar, dummy_vpavperp) @boundscheck r.n == size(ff, 4) || throw(BoundsError(ff)) @boundscheck z.n == size(ff, 3) || throw(BoundsError(ff)) @boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff)) @@ -358,14 +359,34 @@ function update_qpar_species!(qpar, ff, vpa, vperp, z, r) @boundscheck r.n == size(qpar, 2) || throw(BoundsError(qpar)) @boundscheck z.n == size(qpar, 1) || throw(BoundsError(qpar)) - @loop_r_z ir iz begin - # old ! qpar[iz,ir] = integrate_over_vspace(@view(ff[:,iz,ir]), vpa.grid, 3, vpa.wgts) * vpanorm[iz,ir]^4 - qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), - vpa.grid, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) + mass = 1.0 # only reference mass currently supported for all species + if vperp.n > 1 + @loop_r_z ir iz begin + # old ! qpar[iz,ir] = integrate_over_vspace(@view(ff[:,iz,ir]), vpa.grid, 3, vpa.wgts) * vpanorm[iz,ir]^4 + #qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), + # vpa.grid, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) + @views qpar[iz,ir] = get_qpar(ff[:,:,iz,ir], vpa, vperp, upar[iz,ir], mass, dummy_vpavperp) + end + else #1V definitions + @loop_r_z ir iz begin + qpar[iz,ir] = get_qpar_1V(@view(ff[:,:,iz,ir]), vpa, vperp, upar[iz,ir], mass) + end end return nothing end +function get_qpar_1V(ff, vpa, vperp, upar, mass) + @. vpa.scratch = vpa.grid - upar + return mass*integrate_over_vspace(@view(ff[:,:]), vpa.scratch, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) +end + +function get_qpar(ff, vpa, vperp, upar, mass, dummy_vpavperp) + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa]-upar + dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*wpar*( wpar^2 + vperp.grid[ivperp]^2) + end + return mass*integrate_over_vspace(@view(dummy_vpavperp[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) +end """ calculate the neutral density from the neutral pdf """ From 4966d4eeaedcb883ec902c5d83f8b7aea1859bf1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 6 Sep 2023 15:26:34 +0000 Subject: [PATCH 132/331] Generalised moment R (function only) added for use in the numerical conserving terms in the collision operator. --- src/velocity_moments.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 4dd20e3d2..4a5e6e3da 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -32,6 +32,7 @@ export get_ppar export get_pperp export get_pressure export get_qpar +export get_rmom using ..type_definitions: mk_float using ..array_allocation: allocate_shared_float, allocate_bool @@ -387,6 +388,16 @@ function get_qpar(ff, vpa, vperp, upar, mass, dummy_vpavperp) end return mass*integrate_over_vspace(@view(dummy_vpavperp[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) end + +# generalised moment useful for computing numerical conserving terms in the collision operator +function get_rmom(ff, vpa, vperp, upar, mass, dummy_vpavperp) + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa]-upar + dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*( wpar^2 + vperp.grid[ivperp]^2)^2 + end + return mass*integrate_over_vspace(@view(dummy_vpavperp[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) +end + """ calculate the neutral density from the neutral pdf """ From b783efd48c1ab24e6f7398010db61fcfebd07bcc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 7 Sep 2023 15:03:30 +0000 Subject: [PATCH 133/331] Change style to match the style of the rest of the moment integration commands. --- src/velocity_moments.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 4a5e6e3da..74c26104d 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -366,7 +366,7 @@ function update_qpar_species!(qpar, ff, vpa, vperp, z, r, upar, dummy_vpavperp) # old ! qpar[iz,ir] = integrate_over_vspace(@view(ff[:,iz,ir]), vpa.grid, 3, vpa.wgts) * vpanorm[iz,ir]^4 #qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), # vpa.grid, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) - @views qpar[iz,ir] = get_qpar(ff[:,:,iz,ir], vpa, vperp, upar[iz,ir], mass, dummy_vpavperp) + qpar[iz,ir] = get_qpar(@view(ff[:,:,iz,ir]), vpa, vperp, upar[iz,ir], mass, dummy_vpavperp) end else #1V definitions @loop_r_z ir iz begin From 4b973c3b0f03522b7444e425f2ae1d192a791e58 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 7 Sep 2023 15:05:03 +0000 Subject: [PATCH 134/331] Changes to make sure that 1) the initial condition has boundary conditions applied to the distribution that is later evolved, and 2) recalculate the moments having computing the new initial condition. This is important for making plots showing the conservation of the moments by C[F,F]. --- src/time_advance.jl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/time_advance.jl b/src/time_advance.jl index 2a5255a29..d938dc7d7 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -362,7 +362,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, @views update_speed_r!(r_advect[is], fields, vpa, vperp, z, r, geometry) end # enforce prescribed boundary condition in r on the distribution function f - @views enforce_r_boundary_condition!(pdf.charged.unnorm, boundary_distributions.pdf_rboundary_charged, + @views enforce_r_boundary_condition!(pdf.charged.norm, boundary_distributions.pdf_rboundary_charged, r.bc, r_advect, 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, @@ -381,7 +381,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, @views update_speed_z!(z_advect[is], fields, vpa, vperp, z, r, 0.0, geometry) end # enforce prescribed boundary condition in z on the distribution function f - @views enforce_z_boundary_condition!(pdf.charged.unnorm, z.bc, z_advect, vpa, vperp, z, r, composition, + @views enforce_z_boundary_condition!(pdf.charged.norm, z.bc, z_advect, vpa, vperp, z, r, composition, scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, scratch_dummy.buffer_vpavperpzrs_1) @@ -423,6 +423,15 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, begin_s_r_z_vpa_region() @views enforce_vperp_boundary_condition!(pdf.charged.norm[:,:,:,:,:],vperp,vperp_spectral) end + # update moments so that they are consistent with the distribution function + update_density!(moments.charged.dens, pdf.charged.norm, vpa, vperp, z, r, composition) + update_upar!(moments.charged.upar, pdf.charged.norm, vpa, vperp, z, r, composition, moments.charged.dens) + dummy_vpavperp = allocate_float(vpa.n, vperp.n) # integration dummy array + update_qpar!(moments.charged.qpar, pdf.charged.norm, vpa, vperp, z, r, composition, moments.charged.upar, dummy_vpavperp) + update_ppar!(moments.charged.ppar, pdf.charged.norm, vpa, vperp, z, r, composition, moments.charged.upar) + update_pperp!(moments.charged.pperp, pdf.charged.norm, vpa, vperp, z, r, composition) + update_vth!(moments.charged.vth, moments.charged.ppar, moments.charged.pperp, moments.charged.dens, vperp, z, r, composition) + ## # Neutral particle advection From fe3face06d00fef88cd8dac45a065eaaafe78c39 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 7 Sep 2023 15:07:14 +0000 Subject: [PATCH 135/331] Option for conserving the moments n, u|| and p of the distrubtion (under the self-collision operator) or just n under multispecies operators. Further tests are required, the first option may have a bug, or the present tests may show errors due to low accuracy of numerical integration. --- src/fokker_planck.jl | 103 +++++++++++++++++++++++++++++++++++ src/input_structs.jl | 2 + src/moment_kinetics_input.jl | 6 +- 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index fef8a6da4..46e895988 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -17,6 +17,8 @@ export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions +# testing +export symmetric_matrix_inverse using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature @@ -25,6 +27,7 @@ using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..communication: MPISharedArray using ..velocity_moments: integrate_over_vspace +using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom using ..calculus: derivative!, second_derivative! using ..looping """ @@ -988,6 +991,14 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,coll @views dSdt[iz,ir,is] = -integrate_over_vspace(logfC[:,:,iz,ir,is], vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) end end + if collisions.numerical_conserving_terms && false && n_ion_species == 1 + # use an ad-hoc numerical model to conserve density, upar, vth + # a different model is required for inter-species collisions + # simply conserving particle density may be more appropriate in the multi-species case + apply_numerical_conserving_terms!(pdf_out,pdf_in,vperp,vpa,scratch_dummy.dummy_vpavperp,scratch_dummy.buffer_vpavperpzrs_1) + elseif collisions.numerical_conserving_terms && true #&& n_ion_species > 1 + apply_density_conserving_terms!(pdf_out,pdf_in,vperp,vpa,scratch_dummy.buffer_vpavperpzrs_1) + end return nothing end @@ -1257,4 +1268,96 @@ function Cflux_vperp_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_fl return Cflux end +# solves A x = b for a matrix of the form +# A00 0 A02 +# 0 A11 A12 +# A02 A12 A22 +# appropriate for the moment numerical conserving terms +function symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + # matrix determinant + detA = A00*(A11*A22 - A12^2) - A11*A02^2 + # cofactors C (also a symmetric matrix) + C00 = A11*A22 - A12^2 + C01 = A12*A02 + C02 = -A11*A02 + C11 = A00*A22 - A02^2 + C12 = -A00*A12 + C22 = A00*A11 + x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA + x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA + x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA + return x0, x1, x2 +end + +# applies the numerical conservation to pdf_out, the advanced distribution function +# uses the low-level moment integration routines from velocity moments +# conserves n, upar, total pressure of each species +# only correct for the self collision operator +# multi-species cases requires conservation of particle number and total momentum and total energy ( sum_s m_s upar_s, ... ) +function apply_numerical_conserving_terms!(pdf_out,pdf_in,vperp,vpa,dummy_vpavperp, buffer_pdf) + mass = 1.0 + begin_s_r_z_region() + @loop_s_r_z is ir iz begin + # get moments of incoming and outgoing distribution functions + n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + upar_in = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, n_in) + ppar_in = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar_in, mass) + pperp_in = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, mass) + pressure_in = get_pressure(ppar_in,pperp_in) + + n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + upar_out = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, n_out) + ppar_out = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass) + pperp_out = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, mass) + pressure_out = get_pressure(ppar_out,pperp_out) + qpar_out = get_qpar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass, dummy_vpavperp) + rmom_out = get_rmom(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass, dummy_vpavperp) + + # form the appropriate matrix coefficients + b0, b1, b2 = n_in, n_in*(upar_in - upar_out), (3.0/2.0)*(pressure_in/mass) + n_in*(upar_in - upar_out)^2 + A00, A02, A11, A12, A22 = n_out, (3.0/2.0)*(pressure_out/mass), 0.5*ppar_out/mass, qpar_out/mass, rmom_out/mass + + # obtain the coefficients for the corrections + (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + + # fill the buffer with the corrected pdf + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa] - upar_out + buffer_pdf[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp] + end + + end + begin_s_r_z_vperp_vpa_region() + # update pdf_out + @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin + pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] + end +end + +# function which corrects only for the loss of particles due to numerical error +# suitable for use with multiple species collisions +function apply_density_conserving_terms!(pdf_out,pdf_in,vperp,vpa,buffer_pdf) + begin_s_r_z_region() + @loop_s_r_z is ir iz begin + # get density moment of incoming and outgoing distribution functions + n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + + n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + + # obtain the coefficient for the corrections + x0 = n_in/n_out + + # update pdf_out with the corrections + @loop_vperp_vpa ivperp ivpa begin + buffer_pdf[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp] + end + + end + begin_s_r_z_vperp_vpa_region() + # update pdf_out + @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin + pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] + end +end + end diff --git a/src/input_structs.jl b/src/input_structs.jl index 599431180..9ae6ca9bb 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -308,6 +308,8 @@ mutable struct collisions_input nuii_pitch::mk_float # ion-ion Krook operator collision frequency nuii_krook::mk_float + # numerical conserving terms (for Fokker-Planck operator only) + numerical_conserving_terms::Bool end """ diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index ef3207379..cda63eea4 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -185,7 +185,8 @@ function mk_input(scan_input=Dict()) collisions.nuii = get(scan_input, "nuii", 0.0) collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) collisions.nuii_krook = get(scan_input, "nuii_krook", 0.0) - + collisions.numerical_conserving_terms = get(scan_input, "numerical_conserving_terms", false) + # parameters related to the time stepping nstep = get(scan_input, "nstep", 5) dt = get(scan_input, "dt", 0.00025/sqrt(species.charged[1].initial_temperature)) @@ -883,7 +884,8 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) nuii = 0.0 nuii_pitch = 0.0 nuii_krook = 0.0 - collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_pitch, nuii_krook) + numerical_conserving_terms = false + collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_pitch, nuii_krook, numerical_conserving_terms) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength From e36ba641e55ba3c442e7360d4781118a6127a1c3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 1 Oct 2023 22:46:39 +0100 Subject: [PATCH 136/331] Sensible fallback option for 1V animation of f when vperp.n == 1. --- src/post_processing.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/post_processing.jl b/src/post_processing.jl index 3abc44c64..be32383ba 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -1631,6 +1631,7 @@ function plot_charged_pdf(pdf, vpa, vperp, z, r, ivperp0_string = string("_ivperp0", string(ivperp0)) iz0_string = string("_iz0", string(iz0)) ir0_string = string("_ir0", string(ir0)) + nvperp = size(vperp,1) # create animations of the ion pdf for is ∈ 1:n_species if n_species > 1 @@ -1679,16 +1680,23 @@ function plot_charged_pdf(pdf, vpa, vperp, z, r, gif(anim, outfile, fps=5) end # make a gif animation of f(vpa,vperp,t) at a given (z,r) location - if pp.animate_f_vs_vperp_vpa + if pp.animate_f_vs_vperp_vpa && nvperp > 1 anim = @animate for i ∈ itime_min:nwrite_movie:itime_max @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,i], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) end outfile = string(run_name, "_pdf_vs_vperp_vpa", iz0_string, ir0_string, spec_string, ".gif") gif(anim, outfile, fps=5) - @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,itime_max], xlabel="r", ylabel="vpa", c = :deep, interpolation = :cubic) + @views heatmap(vperp, vpa, pdf[:,:,iz0,ir0,is,itime_max], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) outfile = string(run_name, "_pdf_vs_vpa_vperp", ir0_string, iz0_string, spec_string, ".pdf") savefig(outfile) + elseif pp.animate_f_vs_vperp_vpa && nvperp == 1 + # make a gif animation of ϕ(r) at different times + anim = @animate for i ∈ itime_min:nwrite_movie:itime_max + @views plot(vpa, pdf[:,1,iz0,ir0,is,i], xlabel="vpa", ylabel="f") + end + outfile = string(run_name, "_pdf_vs_vpa", ir0_string, iz0_string, spec_string, ".gif") + gif(anim, outfile, fps=5) end # make a gif animation of f(z,r,t) at a given (vpa,vperp) location if pp.animate_f_vs_r_z From 2534d8535b232328baf29ced25053121332b7d71 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 1 Oct 2023 22:48:36 +0100 Subject: [PATCH 137/331] Changes which add the "zero_gradient" boundary condition option for the vpa grid. This option permits experimentation with different solutions to the diffusion equation which are conservative of the density. --- src/initial_conditions.jl | 22 +++++++++++++++++----- src/moment_kinetics_input.jl | 2 ++ src/time_advance.jl | 4 ++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index a422de397..1b6715f09 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -576,14 +576,14 @@ end function enforce_boundary_conditions!(f, f_r_bc, vpa_bc, z_bc, r_bc, vpa, vperp, z, r, vpa_adv::T1, z_adv::T2, r_adv::T3, composition, - scratch_dummy::T4, advance::T5, vperp_spectral::T6) where {T1, T2, T3, T4, T5, T6} + scratch_dummy::T4, advance::T5, vperp_spectral::T6, vpa_spectral::T7) where {T1, T2, T3, T4, T5, T6, T7} 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_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_diffusion) + @views enforce_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_diffusion, vpa, vpa_spectral) end end if vperp.n > 1 @@ -728,14 +728,14 @@ end impose the prescribed vpa boundary condition on f at every z grid point """ -function enforce_vpa_boundary_condition!(f, bc, src::T, vpa_diffusion::Bool) where T +function enforce_vpa_boundary_condition!(f, bc, src::T, vpa_diffusion::Bool, vpa, vpa_spectral) where T nvperp = size(f,2) nz = size(f,3) nr = size(f,4) for ir ∈ 1:nr for iz ∈ 1:nz for ivperp ∈ 1:nvperp - enforce_vpa_boundary_condition_local!(view(f,:,ivperp,iz,ir), bc, src.speed[:,ivperp,iz,ir], vpa_diffusion) + enforce_vpa_boundary_condition_local!(view(f,:,ivperp,iz,ir), bc, src.speed[:,ivperp,iz,ir], vpa_diffusion, vpa, vpa_spectral) end end end @@ -743,17 +743,29 @@ end """ """ -function enforce_vpa_boundary_condition_local!(f::T, bc, adv_speed, vpa_diffusion::Bool) where T +function enforce_vpa_boundary_condition_local!(f::T, bc, adv_speed, vpa_diffusion::Bool, vpa, vpa_spectral) where T # define a zero that accounts for finite precision zero = 1.0e-10 dvpadt = adv_speed[1] #use that dvpa/dt is indendent of vpa in the current model nvpa = size(f,1) + ngrid = vpa.ngrid if bc == "zero" if dvpadt > zero || vpa_diffusion f[1] = 0.0 # -infty forced to zero elseif dvpadt < zero || vpa_diffusion f[end] = 0.0 # +infty forced to zero end + elseif bc == "zero_gradient" + D0 = vpa_spectral.lobatto.Dmat[1,:] + @loop_s_r_z_vperp is ir iz ivperp begin + # adjust F(vpa = -L/2) so that d F / d vpa = 0 at vpa = -L/2 + f[1,ivperp,iz,ir,is] = -sum(D0[2:ngrid].*f[2:ngrid,ivperp,iz,ir,is])/D0[1] + end + D0 = vpa_spectral.lobatto.Dmat[end,:] + @loop_s_r_z_vperp is ir iz ivperp begin + # adjust F(vpa = L/2) so that d F / d vpa = 0 at vpa = L/2 + f[nvpa,ivperp,iz,ir,is] = -sum(D0[1:ngrid-1].*f[nvpa-ngrid+1:nvpa-1,ivperp,iz,ir,is])/D0[ngrid] + end elseif bc == "periodic" f[1] = 0.5*(f[nvpa]+f[1]) f[nvpa] = f[1] diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index ef3207379..0c491bd55 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -994,6 +994,8 @@ function check_input_vpa(vpa, io, vpa_dissipation_coefficient) println(io,">vpa.bc = 'zero'. enforcing zero incoming BC in vpa.") elseif vpa.bc == "zero" && vpa_dissipation_coefficient > 0.0 println(io,">vpa.bc = 'zero', with vpa collision/diffusion terms: enforcing zero BC in vpa.") + elseif vpa.bc == "zero_gradient" + println(io,">vpa.bc = 'zero_gradient'. enforcing zero gradients at limits of vpa domain.") elseif vpa.bc == "periodic" println(io,">vpa.bc = 'periodic'. enforcing periodicity in vpa.") else diff --git a/src/time_advance.jl b/src/time_advance.jl index 90ad50f69..00643d9a3 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -398,7 +398,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, @serial_region begin for is ∈ 1:n_ion_species # enforce prescribed boundary condition in vpa on the distribution function f - @views enforce_vpa_boundary_condition!(pdf.charged.norm[:,:,:,:,is], vpa.bc, vpa_advect[is], advance.vpa_diffusion) + @views enforce_vpa_boundary_condition!(pdf.charged.norm[:,:,:,:,is], vpa.bc, vpa_advect[is], advance.vpa_diffusion, vpa, vpa_spectral) end end @@ -1077,7 +1077,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, # enforce boundary conditions in r, z and vpa on the charged particle distribution function enforce_boundary_conditions!(fvec_out.pdf, boundary_distributions.pdf_rboundary_charged, vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral) + scratch_dummy, advance, vperp_spectral, vpa_spectral) # enforce boundary conditions in r and z on the neutral particle distribution function if n_neutral_species > 0 enforce_neutral_boundary_conditions!(fvec_out.pdf_neutral, fvec_out.pdf, boundary_distributions, From 18e6af77478463c8d13989dd41ec447d003ae018 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 3 Oct 2023 10:16:31 +0000 Subject: [PATCH 138/331] Changes to 1) make the type of ad-hoc conserving model a string-option input and 2) modify the conserving models to impose the boundary conditions before calculating the moments. This latter changes greatly improves the numerical conservation for the "density" option , although the three-moment option "density+u||+T" still shows a level of numerical error that may indicate a bug. --- src/fokker_planck.jl | 44 ++++++++++++++++++++++++++++-------- src/input_structs.jl | 2 +- src/moment_kinetics_input.jl | 4 ++-- src/time_advance.jl | 3 ++- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 46e895988..d74a31570 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -23,6 +23,7 @@ export symmetric_matrix_inverse using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature using Dates +using ..initial_conditions: enforce_boundary_conditions! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..communication: MPISharedArray @@ -887,8 +888,8 @@ and collision operator in place. """ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral; diagnose_entropy_production = true) - + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, + vpa_advect, z_advect, r_advect; diagnose_entropy_production = true) n_ion_species = composition.n_ion_species @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) @@ -991,13 +992,19 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,coll @views dSdt[iz,ir,is] = -integrate_over_vspace(logfC[:,:,iz,ir,is], vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) end end - if collisions.numerical_conserving_terms && false && n_ion_species == 1 + if collisions.numerical_conserving_terms == "density+u||+T" # use an ad-hoc numerical model to conserve density, upar, vth # a different model is required for inter-species collisions # simply conserving particle density may be more appropriate in the multi-species case - apply_numerical_conserving_terms!(pdf_out,pdf_in,vperp,vpa,scratch_dummy.dummy_vpavperp,scratch_dummy.buffer_vpavperpzrs_1) - elseif collisions.numerical_conserving_terms && true #&& n_ion_species > 1 - apply_density_conserving_terms!(pdf_out,pdf_in,vperp,vpa,scratch_dummy.buffer_vpavperpzrs_1) + apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + elseif collisions.numerical_conserving_terms == "density" + apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + else + println("ERROR: collisions.numerical_conserving_terms = ",collisions.numerical_conserving_terms," NOT SUPPORTED") end return nothing end @@ -1294,7 +1301,16 @@ end # conserves n, upar, total pressure of each species # only correct for the self collision operator # multi-species cases requires conservation of particle number and total momentum and total energy ( sum_s m_s upar_s, ... ) -function apply_numerical_conserving_terms!(pdf_out,pdf_in,vperp,vpa,dummy_vpavperp, buffer_pdf) +function apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # enforce bc prior to imposing conservation + enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, + vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # buffer arrays + buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 + dummy_vpavperp = scratch_dummy.dummy_vpavperp mass = 1.0 begin_s_r_z_region() @loop_s_r_z is ir iz begin @@ -1323,7 +1339,7 @@ function apply_numerical_conserving_terms!(pdf_out,pdf_in,vperp,vpa,dummy_vpavpe # fill the buffer with the corrected pdf @loop_vperp_vpa ivperp ivpa begin wpar = vpa.grid[ivpa] - upar_out - buffer_pdf[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp] + buffer_pdf[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp,iz,ir,is] end end @@ -1336,7 +1352,15 @@ end # function which corrects only for the loss of particles due to numerical error # suitable for use with multiple species collisions -function apply_density_conserving_terms!(pdf_out,pdf_in,vperp,vpa,buffer_pdf) +function apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # enforce bc prior to imposing conservation + enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, + vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # buffer array + buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 begin_s_r_z_region() @loop_s_r_z is ir iz begin # get density moment of incoming and outgoing distribution functions @@ -1349,7 +1373,7 @@ function apply_density_conserving_terms!(pdf_out,pdf_in,vperp,vpa,buffer_pdf) # update pdf_out with the corrections @loop_vperp_vpa ivperp ivpa begin - buffer_pdf[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp] + buffer_pdf[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp,iz,ir,is] end end diff --git a/src/input_structs.jl b/src/input_structs.jl index 9ae6ca9bb..997be4497 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -309,7 +309,7 @@ mutable struct collisions_input # ion-ion Krook operator collision frequency nuii_krook::mk_float # numerical conserving terms (for Fokker-Planck operator only) - numerical_conserving_terms::Bool + numerical_conserving_terms::String end """ diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 468f7f38a..45c9fffce 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -185,7 +185,7 @@ function mk_input(scan_input=Dict()) collisions.nuii = get(scan_input, "nuii", 0.0) collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) collisions.nuii_krook = get(scan_input, "nuii_krook", 0.0) - collisions.numerical_conserving_terms = get(scan_input, "numerical_conserving_terms", false) + collisions.numerical_conserving_terms = get(scan_input, "numerical_conserving_terms", "density") # parameters related to the time stepping nstep = get(scan_input, "nstep", 5) @@ -884,7 +884,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) nuii = 0.0 nuii_pitch = 0.0 nuii_krook = 0.0 - numerical_conserving_terms = false + numerical_conserving_terms = "density" collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_pitch, nuii_krook, numerical_conserving_terms) Bzed = 1.0 # magnetic field component along z diff --git a/src/time_advance.jl b/src/time_advance.jl index 3bce2110b..2174199fe 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -1076,7 +1076,8 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, if advance.explicit_fp_collisions update_entropy_diagnostic = (istage == 1) explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf, moments.charged.dSdt, composition,collisions,dt,fp_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, + vpa_advect, z_advect, r_advect, diagnose_entropy_production = update_entropy_diagnostic) #println(moments.charged.dSdt) end From 7b848f878f479714f784658f5d6c04fc652e5d86 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 5 Oct 2023 07:46:49 +0000 Subject: [PATCH 139/331] Diagnosis of the symmetric_matrix_inverse function (commented out). --- src/fokker_planck.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index d74a31570..849ea416d 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -1293,6 +1293,10 @@ function symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA + #println("b0: ",b0," b1: ",b1," b2: ",b2) + #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) + #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) + #println("x0: ",x0," x1: ",x1," x2: ",x2) return x0, x1, x2 end From cd07e8b40eb9584c6564204b398c2fa16cf32b0a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 5 Oct 2023 10:03:59 +0000 Subject: [PATCH 140/331] Log scale plot of L2 norm of f - f_Maxwellian. --- src/post_processing.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/post_processing.jl b/src/post_processing.jl index 220db59eb..bb2b1b4e3 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -2053,6 +2053,9 @@ function plot_Maxwellian_diagnostic(ff, density, parallel_flow, thermal_speed, v @views plot(time, ff_norm, xlabel=L"t/c_{ref}", ylabel=L"L^2(f - f_M)(t)", label = "") outfile = string(run_name, "_L2_Maxwellian_norm"*description*"_vs_t.pdf") savefig(outfile) + @views plot(time, ff_norm, xlabel=L"t/c_{ref}", ylabel=L"L^2(f - f_M)(t)", label = "", yscale=:log10) + outfile = string(run_name, "_L2_Maxwellian_norm_log_scale"*description*"_vs_t.pdf") + savefig(outfile) end return nothing end From df9fd25e2c78b07a937b5509d7d7623322f4082f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 5 Oct 2023 10:06:19 +0000 Subject: [PATCH 141/331] Refactor conserving terms application so that the moments are forced to be those of the pdf_out that enters the collision operator routine, not the those of pdf_in (which is the pdf at the start of the RK step). This is necessary to make sure that the conserving corrections only force the advance of the collision operator to be conservative, and not the whole time advance. --- src/fokker_planck.jl | 55 +++++++++++++++++++++++++++++++++++++++----- src/time_advance.jl | 9 ++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 849ea416d..c4834fce6 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -947,6 +947,13 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,coll @. d2fdvperpdvpa[ivpa,:,iz,ir,is] = vperp.scratch end + # to permit moment conservation, store the current moments of pdf_out + # this involves imposing the boundary conditions to the present pre-collisions pdf_out + if collisions.numerical_conserving_terms == "density+u||+T" || collisions.numerical_conserving_terms == "density" + store_moments_in_buffer!(pdf_out,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + end # now parallelise over all dimensions and calculate the # collision operator coefficients and the collision operator # in one loop, noting that we only require data local to @@ -1315,15 +1322,22 @@ function apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions # buffer arrays buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 dummy_vpavperp = scratch_dummy.dummy_vpavperp + # data precalculated by store_moments_in_buffer! + buffer_n = scratch_dummy.buffer_zrs_1 + buffer_upar = scratch_dummy.buffer_zrs_2 + buffer_pressure = scratch_dummy.buffer_zrs_3 mass = 1.0 begin_s_r_z_region() @loop_s_r_z is ir iz begin # get moments of incoming and outgoing distribution functions - n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) - upar_in = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, n_in) - ppar_in = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar_in, mass) - pperp_in = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, mass) - pressure_in = get_pressure(ppar_in,pperp_in) + #n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + #upar_in = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, n_in) + #ppar_in = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar_in, mass) + #pperp_in = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, mass) + #pressure_in = get_pressure(ppar_in,pperp_in) + n_in = buffer_n[iz,ir,is] + upar_in = buffer_upar[iz,ir,is] + pressure_in = buffer_pressure[iz,ir,is] n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) upar_out = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, n_out) @@ -1352,6 +1366,7 @@ function apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] end + return nothing end # function which corrects only for the loss of particles due to numerical error @@ -1365,10 +1380,13 @@ function apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, scratch_dummy, advance, vperp_spectral, vpa_spectral) # buffer array buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 + # data precalculated by store_moments_in_buffer! + buffer_n = scratch_dummy.buffer_zrs_1 begin_s_r_z_region() @loop_s_r_z is ir iz begin # get density moment of incoming and outgoing distribution functions - n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + #n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + n_in = buffer_n[iz,ir,is] n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) @@ -1386,6 +1404,31 @@ function apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] end + return nothing end +function store_moments_in_buffer!(pdf_out,boundary_distributions, + vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # enforce bc prior to calculating the moments + enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, + vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, + scratch_dummy, advance, vperp_spectral, vpa_spectral) + # buffer arrays + density = scratch_dummy.buffer_zrs_1 + upar = scratch_dummy.buffer_zrs_2 + pressure = scratch_dummy.buffer_zrs_3 + # set the mass + mass = 1.0 + + begin_s_r_z_region() + @loop_s_r_z is ir iz begin + density[iz,ir,is] = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + upar[iz,ir,is] = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, density[iz,ir,is]) + ppar = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar[iz,ir,is], mass) + pperp = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, mass) + pressure[iz,ir,is] = get_pressure(ppar,pperp) + end + return nothing +end end diff --git a/src/time_advance.jl b/src/time_advance.jl index 2174199fe..9a6f28e24 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -69,6 +69,10 @@ mutable struct scratch_dummy_arrays buffer_r_2::MPISharedArray{mk_float,1} buffer_r_3::MPISharedArray{mk_float,1} buffer_r_4::MPISharedArray{mk_float,1} + + buffer_zrs_1::MPISharedArray{mk_float,3} + buffer_zrs_2::MPISharedArray{mk_float,3} + buffer_zrs_3::MPISharedArray{mk_float,3} buffer_vpavperpzs_1::MPISharedArray{mk_float,4} buffer_vpavperpzs_2::MPISharedArray{mk_float,4} @@ -512,6 +516,10 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_r_2 = allocate_shared_float(nr) buffer_r_3 = allocate_shared_float(nr) buffer_r_4 = allocate_shared_float(nr) + + buffer_zrs_1 = allocate_shared_float(nz,nr,nspecies_ion) + buffer_zrs_2 = allocate_shared_float(nz,nr,nspecies_ion) + buffer_zrs_3 = allocate_shared_float(nz,nr,nspecies_ion) buffer_vpavperpzs_1 = allocate_shared_float(nvpa,nvperp,nz,nspecies_ion) buffer_vpavperpzs_2 = allocate_shared_float(nvpa,nvperp,nz,nspecies_ion) @@ -553,6 +561,7 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies return scratch_dummy_arrays(dummy_s, dummy_sr,dummy_vpavperp,dummy_zr, buffer_z_1,buffer_z_2,buffer_z_3,buffer_z_4, buffer_r_1,buffer_r_2,buffer_r_3,buffer_r_4, + buffer_zrs_1,buffer_zrs_2,buffer_zrs_3, buffer_vpavperpzs_1,buffer_vpavperpzs_2,buffer_vpavperpzs_3,buffer_vpavperpzs_4,buffer_vpavperpzs_5,buffer_vpavperpzs_6, buffer_vpavperprs_1,buffer_vpavperprs_2,buffer_vpavperprs_3,buffer_vpavperprs_4,buffer_vpavperprs_5,buffer_vpavperprs_6, buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5,buffer_vpavperpzrs_6, From 5d48f106ae536b0184dddf4284e42f5f351a8196 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 5 Oct 2023 10:07:04 +0000 Subject: [PATCH 142/331] Remove commented code. --- src/fokker_planck.jl | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index c4834fce6..5a4fdaebc 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -1330,11 +1330,6 @@ function apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions begin_s_r_z_region() @loop_s_r_z is ir iz begin # get moments of incoming and outgoing distribution functions - #n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) - #upar_in = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, n_in) - #ppar_in = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar_in, mass) - #pperp_in = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, mass) - #pressure_in = get_pressure(ppar_in,pperp_in) n_in = buffer_n[iz,ir,is] upar_in = buffer_upar[iz,ir,is] pressure_in = buffer_pressure[iz,ir,is] @@ -1385,7 +1380,6 @@ function apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, begin_s_r_z_region() @loop_s_r_z is ir iz begin # get density moment of incoming and outgoing distribution functions - #n_in = get_density(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) n_in = buffer_n[iz,ir,is] n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) From dcd36cf4b4c84ad3845044a0ee60f95d2b55caaf Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 6 Oct 2023 16:36:22 +0000 Subject: [PATCH 143/331] Fix broken if statement that ensures that the numerical diffusion is not applied in dimensions of size 1. --- src/numerical_dissipation.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index ed668fedc..dccdf384a 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -49,7 +49,7 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, begin_s_r_z_vperp_region() diffusion_coefficient = num_diss_params.vpa_dissipation_coefficient - if diffusion_coefficient <= 0.0 && !(vpa.n > 1) + if diffusion_coefficient <= 0.0 || vpa.n == 1 return nothing end # if T_spectral <: Bool @@ -118,7 +118,7 @@ function vperp_dissipation!(f_out, f_in, vperp, spectral::T_spectral, dt, begin_s_r_z_vpa_region() diffusion_coefficient = num_diss_params.vperp_dissipation_coefficient - if diffusion_coefficient <= 0.0 && !(vperp.n > 1) + if diffusion_coefficient <= 0.0 || vperp.n == 1 return nothing end @@ -152,7 +152,7 @@ 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 = num_diss_params.z_dissipation_coefficient - if diffusion_coefficient <= 0.0 && !(z.n > 1) + if diffusion_coefficient <= 0.0 || z.n == 1 return nothing end @@ -186,7 +186,7 @@ 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 = num_diss_params.r_dissipation_coefficient - if diffusion_coefficient <= 0.0 && !(r.n > 1) + if diffusion_coefficient <= 0.0 || r.n == 1 return nothing end From 49b257acda5115e82cfee42f1874e5ca8dcbe375 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 6 Oct 2023 17:07:20 +0000 Subject: [PATCH 144/331] Upgrade calculus tests to test both matrix and FFT based differentiation, revealing a possible bug. --- test/calculus_tests.jl | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index 61da11c8e..a913c9531 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -11,14 +11,15 @@ using MPI using Random fd_fake_setup(x) = return false -cheb_option_global = "matrix" # "FFT" +#cheb_option_global = "FFT" # "matrix" # function runtests() @testset "calculus" verbose=use_verbose begin println("calculus tests") @testset "fundamental theorem of calculus" begin - @testset "$discretization $ngrid $nelement" for (discretization, setup_func) ∈ - (("finite_difference", fd_fake_setup), - ("chebyshev_pseudospectral", setup_chebyshev_pseudospectral)), + @testset "$discretization $ngrid $nelement" for (discretization, setup_func, cheb_option) ∈ + (("finite_difference", fd_fake_setup, ""), + ("chebyshev_pseudospectral", setup_chebyshev_pseudospectral, "FFT"), + ("chebyshev_pseudospectral", setup_chebyshev_pseudospectral, "matrix")), ngrid ∈ (5,6,7,8,9,10), nelement ∈ (1, 2, 3, 4, 5) if discretization == "finite_difference" && (ngrid - 1) * nelement % 2 == 1 @@ -37,7 +38,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -434,7 +435,7 @@ function runtests() (5, 31, 8.e-13), (5, 32, 8.e-13), (5, 33, 8.e-13), - ) + ), cheb_option in ("FFT","matrix") # define inputs needed for the test L = 6.0 @@ -442,7 +443,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -633,7 +634,7 @@ function runtests() (5, 31, 8.e-13), (5, 32, 8.e-13), (5, 33, 8.e-13), - ) + ), cheb_option in ("FFT","matrix") # define inputs needed for the test L = 6.0 @@ -641,7 +642,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -680,7 +681,7 @@ function runtests() @testset "Chebyshev pseudospectral derivatives (4 argument), Neumann" verbose=false begin @testset "$nelement $ngrid" for bc ∈ ("constant", "zero"), - nelement ∈ (1:5), ngrid ∈ (3:33) + nelement ∈ (1:5), ngrid ∈ (3:33), cheb_option in ("FFT","matrix") # define inputs needed for the test L = 1.0 @@ -688,7 +689,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -735,7 +736,7 @@ function runtests() @testset "Chebyshev pseudospectral derivatives upwinding (5 argument), Neumann" verbose=false begin @testset "$nelement $ngrid" for bc ∈ ("constant", "zero"), - nelement ∈ (1:5), ngrid ∈ (3:33) + nelement ∈ (1:5), ngrid ∈ (3:33), cheb_option in ("FFT","matrix") # define inputs needed for the test L = 1.0 @@ -743,7 +744,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -953,7 +954,7 @@ function runtests() (5, 31, 8.e-13), (5, 32, 8.e-13), (5, 33, 8.e-13), - ) + ), cheb_option in ("FFT","matrix") # define inputs needed for the test L = 6.0 @@ -961,7 +962,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - cheb_option = cheb_option_global #"FFT" + #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement From ec65cd46a5b2b167165ef17c39addfe0efe85423 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 9 Oct 2023 08:44:53 +0000 Subject: [PATCH 145/331] Attempt to construct script with 2D matrix assembly --- 2D_FEM_assembly_test.jl | 183 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 2D_FEM_assembly_test.jl diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl new file mode 100644 index 000000000..16021314f --- /dev/null +++ b/2D_FEM_assembly_test.jl @@ -0,0 +1,183 @@ +using Printf +using Plots +using LaTeXStrings +using MPI +using Measures + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + import moment_kinetics + using moment_kinetics.input_structs: grid_input, advection_input + using moment_kinetics.coordinates: define_coordinate + using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral + using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral + using moment_kinetics.type_definitions: mk_float, mk_int + + function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.1f ", matrix[i,j]) + end + println("") + end + println("\n") + end + + function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") + end + + + # Array in compound 1D form + + function ic_func(ivpa,ivperp,nvpa) + return ivpa + nvpa*(ivperp-1) + end + function ivperp_func(ic,nvpa) + return floor(Int64,(ic-1)/nvpa) + 1 + end + function ivpa_func(ic,nvpa) + ivpa = ic - nvpa*(ivperp_func(ic,nvpa) - 1) + return ivpa + end + + function ravel_vpavperp_to_c!(pdf_c,pdf_vpavperp,nvpa,nvperp) + for ivperp in 1:nvperp + for ivpa in 1:nvpa + ic = ic_func(ivpa,ivperp,nvpa) + pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] + end + end + return nothing + end + + function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc,nvpa) + for ic in 1:nc + ivpa = ivpa_func(ic,nvpa) + ivperp = ivperp_func(ic,nvpa) + pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] + end + return nothing + end + + function ivpa_global_func(ivpa_local,ielement_vpa,ngrid_vpa) + ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) + return ivpa_global + end + + # define inputs needed for the test + ngrid = 2 #number of points per element + nelement_local_vpa = 4 # number of elements per rank + nelement_global_vpa = nelement_local_vpa # total number of elements + nelement_local_vperp = 4 # number of elements per rank + nelement_global_vperp = nelement_local_vperp # total number of elements + Lvpa = 6.0 #physical box size in reference units + Lvperp = 3.0 #physical box size in reference units + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + #discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) + # create the coordinate struct 'x' + println("made inputs") + println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) + println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa + # and create the plans for the forward and backward fast Chebyshev transforms + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid + #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) + elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + else + # create dummy Bool variable to return in place of the above struct + vpa_spectral = false + #vpa.duniform_dgrid .= 1.0 + end + + if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp + # and create the plans for the forward and backward fast Chebyshev transforms + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid + #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) + elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + else + # create dummy Bool variable to return in place of the above struct + vperp_spectral = false + #vperp.duniform_dgrid .= 1.0 + end + + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + nc_local = vpa.ngrid*vperp.ngrid + MM2D = Array{mk_float,2}(undef,nc_global,nc_global) + MM2D .= 0.0 + print_matrix(MM2D,"MM2D",nc_global,nc_global) + + function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) + # indices of vpa, vperp within the element + ivpa_local = ivpa_func(ic_local,vpa.ngrid) + ivperp_local = ivperp_func(ic_local,vpa.ngrid) + ivpap_local = ivpa_func(icp_local,vpa.ngrid) + ivperpp_local = ivperp_func(icp_local,vpa.ngrid) + # global indices on the grids + ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] + ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] + ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] + ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] + # global compound indices + ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) + icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) + return ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global + end + + for ielement_vperp in 1:1 + for ielement_vpa in 1:vpa.nelement_local + for icp_local in 1:nc_local + for ic_local in 1:nc_local + ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) + # assign mass matrix data + MM2D[ic_global,icp_global] += vpa_spectral.lobatto.M0[ivpa_local,ivpap_local]* + vperp_spectral.radau.M1[ivperp_local,ivperpp_local] + end + end + end + end + for ielement_vperp in 2:vperp.nelement_local + for ielement_vpa in 1:vpa.nelement_local + for icp_local in 1:nc_local + for ic_local in 1:nc_local + ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) + # assign mass matrix data + MM2D[ic_global,icp_global] += vpa_spectral.lobatto.M0[ivpa_local,ivpap_local]* + vperp_spectral.lobatto.M1[ivperp_local,ivperpp_local] + end + end + end + end + print_matrix(MM2D,"MM2D",nc_global,nc_global) +end \ No newline at end of file From 02bd81091aeaa539075cc1e139ebdc1d502616dd Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 9 Oct 2023 10:10:08 +0000 Subject: [PATCH 146/331] Almost working (modulo BC problems) 2D FEM test --- 2D_FEM_assembly_test.jl | 111 +++++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 19 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 16021314f..150e8b290 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -12,8 +12,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral + using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int + using SparseArrays: sparse + using LinearAlgebra: mul!, lu function print_matrix(matrix,name,n,m) println("\n ",name," \n") @@ -74,10 +76,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 2 #number of points per element - nelement_local_vpa = 4 # number of elements per rank + ngrid = 5 #number of points per element + nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 4 # number of elements per rank + nelement_local_vperp = 16 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 6.0 #physical box size in reference units Lvperp = 3.0 #physical box size in reference units @@ -136,7 +138,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ nc_local = vpa.ngrid*vperp.ngrid MM2D = Array{mk_float,2}(undef,nc_global,nc_global) MM2D .= 0.0 - print_matrix(MM2D,"MM2D",nc_global,nc_global) + KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKpar2D .= 0.0 + KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKperp2D .= 0.0 + + #print_matrix(MM2D,"MM2D",nc_global,nc_global) + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) # indices of vpa, vperp within the element @@ -155,29 +167,90 @@ if abspath(PROGRAM_FILE) == @__FILE__ return ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global end - for ielement_vperp in 1:1 + for ielement_vperp in 1:vperp.nelement_local for ielement_vpa in 1:vpa.nelement_local for icp_local in 1:nc_local for ic_local in 1:nc_local ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) + + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") # assign mass matrix data - MM2D[ic_global,icp_global] += vpa_spectral.lobatto.M0[ivpa_local,ivpap_local]* - vperp_spectral.radau.M1[ivperp_local,ivperpp_local] + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local] end end end end - for ielement_vperp in 2:vperp.nelement_local - for ielement_vpa in 1:vpa.nelement_local - for icp_local in 1:nc_local - for ic_local in 1:nc_local - ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) - # assign mass matrix data - MM2D[ic_global,icp_global] += vpa_spectral.lobatto.M0[ivpa_local,ivpap_local]* - vperp_spectral.lobatto.M1[ivperp_local,ivperpp_local] - end - end + #print_matrix(MM2D,"MM2D",nc_global,nc_global) + #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + + # convert these matrices to sparse matrices + + MM2D_sparse = sparse(MM2D) + KKpar2D_sparse = sparse(KKpar2D) + KKperp2D_sparse = sparse(KKperp2D) + + # create LU decomposition for mass matrix inversion + + lu_obj = lu(MM2D_sparse) + + # define a test function + + fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + fc = Array{mk_float,1}(undef,nc_global) + dfc = Array{mk_float,1}(undef,nc_global) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvpa2_exact[ivpa,ivperp] = (4.0*vpa.grid[ivpa]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) end end - print_matrix(MM2D,"MM2D",nc_global,nc_global) + + # boundary conditions + + fvpavperp[vpa.n,:] .= 0.0 + fvpavperp[1,:] .= 0.0 + fvpavperp[:,vperp.n] .= 0.0 + + + #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) + # fill fc with fvpavperp + ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) + #print_vector(fc,"fc",nc_global) + # multiply by KKpar2D and fill dfc + mul!(dfc,KKpar2D_sparse,fc) + # invert mass matrix and fill fc + fc = lu_obj \ dfc + #print_vector(fc,"fc",nc_global) + # unravel + ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) + print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) + print_matrix(d2fvpavperp_dvpa2_exact,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) + + @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) + println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) + + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvpa2_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvpa2_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvpa2_err.pdf") + savefig(outfile) end \ No newline at end of file From a259303b128ea324198f66ae78ce34edb697b8da Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 9 Oct 2023 14:38:35 +0000 Subject: [PATCH 147/331] Make sure that weights are shared memory arrays, correct typo so that logfC does not overwrite a used dummy array. Ensure that a "none" option is supported for conserving terms. --- src/fokker_planck.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 5a4fdaebc..4d8bc4c81 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -70,11 +70,11 @@ function allocate_fokkerplanck_arrays(vperp,vpa) nvpa = vpa.n nvperp = vperp.n - G1_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) - H0_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) - H1_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) - H2_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) - H3_weights = allocate_float(nvpa,nvperp,nvpa,nvperp) + G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) #Rosenbluth_G = allocate_float(nvpa,nvperp) d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) @@ -920,7 +920,7 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,coll d2fdvperpdvpa = scratch_dummy.buffer_vpavperpzrs_3 dfdvperp = scratch_dummy.buffer_vpavperpzrs_4 d2fdvperp2 = scratch_dummy.buffer_vpavperpzrs_5 - logfC = scratch_dummy.buffer_vpavperpzrs_5 + logfC = scratch_dummy.buffer_vpavperpzrs_6 begin_s_r_z_vperp_region() @loop_s_r_z_vperp is ir iz ivperp begin @@ -1010,7 +1010,7 @@ function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,coll apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, scratch_dummy, advance, vperp_spectral, vpa_spectral) - else + elseif !(collisions.numerical_conserving_terms == "none") println("ERROR: collisions.numerical_conserving_terms = ",collisions.numerical_conserving_terms," NOT SUPPORTED") end return nothing From f2774ae8e6dcc8dfe0eab4a19ce7c7b6cb87d5ba Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 11 Oct 2023 17:13:22 +0000 Subject: [PATCH 148/331] Development trying to understand BC problems with mass matrix. --- 2D_FEM_assembly_test.jl | 122 +++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 150e8b290..db04bdabb 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,13 +15,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using SparseArrays: sparse - using LinearAlgebra: mul!, lu + using LinearAlgebra: mul!, lu, cholesky function print_matrix(matrix,name,n,m) println("\n ",name," \n") for i in 1:n for j in 1:m - @printf("%.1f ", matrix[i,j]) + @printf("%.2f ", matrix[i,j]) end println("") end @@ -76,7 +76,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 5 #number of points per element + ngrid = 9 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 16 # number of elements per rank @@ -136,6 +136,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n nc_local = vpa.ngrid*vperp.ngrid + Index2D = Array{mk_int,2}(undef,nc_global,nc_global) MM2D = Array{mk_float,2}(undef,nc_global,nc_global) MM2D .= 0.0 KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) @@ -147,6 +148,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # local dummy arrays MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) @@ -167,31 +169,90 @@ if abspath(PROGRAM_FILE) == @__FILE__ return ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global end + function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + # global indices on the grids + ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] + ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] + ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] + ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] + # global compound indices + ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) + icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) + return ic_global, icp_global + end + for ielement_vperp in 1:vperp.nelement_local for ielement_vpa in 1:vpa.nelement_local - for icp_local in 1:nc_local - for ic_local in 1:nc_local - ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) - - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - # assign mass matrix data - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - KKperp[ivperp_local,ivperpp_local] + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + println("ic: ",ic_global," icp: ",icp_global) + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + # assign mass matrix data + println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local] + # boundary condition possibilities + lower_boundary_vpa = (ielement_vpa == 1 && ivpa_local == 1 && ivpap_local == 1) && false + upper_boundary_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid && ivpap_local == vpa.ngrid) && false + upper_boundary_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid && ivperpp_local == vperp.ngrid)# && false + # lower boundary in vpa, not a boundary in vperp + if lower_boundary_vpa #&& !upper_boundary_vperp + MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* + MMperp[ivperp_local,ivperpp_local] + #MM2D[ic_global,icp_global] = 1.0 + #println("MM2D = 1.0: BC") + end + # if lower_boundary_vpa && upper_boundary_vperp + # MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* + # MMperp_p1[1,1] + #MM2D[ic_global,icp_global] = 1.0 + #println("MM2D = 1.0: BC") + # end + # upper boundary in vpa, not a boundary in vperp + if upper_boundary_vpa #&& !upper_boundary_vperp + MM2D[ic_global,icp_global] += MMpar[1,1]* + MMperp[ivperp_local,ivperpp_local] + #MM2D[ic_global,icp_global] = 1.0 + #println("MM2D = 1.0: BC") + end + # if upper_boundary_vpa && upper_boundary_vperp + # MM2D[ic_global,icp_global] += MMpar[1,1]* + # MMperp_p1[1,1] + #MM2D[ic_global,icp_global] = 1.0 + #println("MM2D = 1.0: BC") + # end + # upper boundary in vperp, not a boundary in vpa + # if upper_boundary_vperp && !(lower_boundary_vpa) && !(upper_boundary_vpa) + # get_QQ_local!(MMperp_p1,ielement_vperp+1,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + # MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + # MMperp_p1[1,1] + # #MM2D[ic_global,icp_global] = 1.0 + # #println("MM2D = 1.0: BC") + # end + end + end end end end end - #print_matrix(MM2D,"MM2D",nc_global,nc_global) - #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - + if nc_global < 30 + print_matrix(MM2D,"MM2D",nc_global,nc_global) + print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + end # convert these matrices to sparse matrices MM2D_sparse = sparse(MM2D) @@ -201,10 +262,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ # create LU decomposition for mass matrix inversion lu_obj = lu(MM2D_sparse) + #cholesky_obj = cholesky(MM2D_sparse) # define a test function fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -227,20 +291,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) # fill fc with fvpavperp ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) + ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) + @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) + println("max(ravel_err)",maximum(fvpavperp_err)) #print_vector(fc,"fc",nc_global) # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_sparse,fc) # invert mass matrix and fill fc fc = lu_obj \ dfc + #fc = cholesky_obj \ dfc #print_vector(fc,"fc",nc_global) # unravel ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) - print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) - print_matrix(d2fvpavperp_dvpa2_exact,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) - + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) + end @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) - + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) + end @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("d2fvpavperp_dvpa2_num.pdf") From e05c65fa9ff954cde30c7e0937b7a9f0c56f9aab Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 12 Oct 2023 09:27:59 +0000 Subject: [PATCH 149/331] Further iterative changes trying to understand the problem. --- 2D_FEM_assembly_test.jl | 120 +++++++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 25 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index db04bdabb..8f8172e39 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -169,16 +169,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ return ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global end - function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + #function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + # # global indices on the grids + # ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] + # ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] + # ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] + # ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] + # # global compound indices + # ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) + # icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) + # return ic_global, icp_global + #end + + function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) # global indices on the grids ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] - ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] - ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] - # global compound indices + # global compound index ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) - return ic_global, icp_global + return ic_global end for ielement_vperp in 1:vperp.nelement_local @@ -187,7 +196,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivperp_local in 1:vperp.ngrid for ivpap_local in 1:vpa.ngrid for ivpa_local in 1:vpa.ngrid - ic_global, icp_global = get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) @@ -205,9 +215,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local] # boundary condition possibilities - lower_boundary_vpa = (ielement_vpa == 1 && ivpa_local == 1 && ivpap_local == 1) && false - upper_boundary_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid && ivpap_local == vpa.ngrid) && false - upper_boundary_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid && ivperpp_local == vperp.ngrid)# && false + lower_boundary_vpa = (ielement_vpa == 1 && ivpa_local == 1 && ivpap_local == 1) + upper_boundary_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid && ivpap_local == vpa.ngrid) + upper_boundary_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid && ivperpp_local == vperp.ngrid) # lower boundary in vpa, not a boundary in vperp if lower_boundary_vpa #&& !upper_boundary_vperp MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* @@ -215,12 +225,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ #MM2D[ic_global,icp_global] = 1.0 #println("MM2D = 1.0: BC") end - # if lower_boundary_vpa && upper_boundary_vperp - # MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* - # MMperp_p1[1,1] + if lower_boundary_vpa && upper_boundary_vperp + MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* + MMperp_p1[1,1] #MM2D[ic_global,icp_global] = 1.0 #println("MM2D = 1.0: BC") - # end + end # upper boundary in vpa, not a boundary in vperp if upper_boundary_vpa #&& !upper_boundary_vperp MM2D[ic_global,icp_global] += MMpar[1,1]* @@ -228,26 +238,59 @@ if abspath(PROGRAM_FILE) == @__FILE__ #MM2D[ic_global,icp_global] = 1.0 #println("MM2D = 1.0: BC") end - # if upper_boundary_vpa && upper_boundary_vperp - # MM2D[ic_global,icp_global] += MMpar[1,1]* - # MMperp_p1[1,1] + if upper_boundary_vpa && upper_boundary_vperp + MM2D[ic_global,icp_global] += MMpar[1,1]* + MMperp_p1[1,1] #MM2D[ic_global,icp_global] = 1.0 #println("MM2D = 1.0: BC") - # end + end # upper boundary in vperp, not a boundary in vpa - # if upper_boundary_vperp && !(lower_boundary_vpa) && !(upper_boundary_vpa) - # get_QQ_local!(MMperp_p1,ielement_vperp+1,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - # MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - # MMperp_p1[1,1] - # #MM2D[ic_global,icp_global] = 1.0 - # #println("MM2D = 1.0: BC") - # end + if upper_boundary_vperp && !(lower_boundary_vpa) && !(upper_boundary_vpa) + get_QQ_local!(MMperp_p1,ielement_vperp+1,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp_p1[1,1] + #MM2D[ic_global,icp_global] = 1.0 + #println("MM2D = 1.0: BC") + end end end end end end end + + function enforce_zero_bc!(fc,vpa,vperp) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + end + if nc_global < 30 print_matrix(MM2D,"MM2D",nc_global,nc_global) print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) @@ -272,12 +315,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) fc = Array{mk_float,1}(undef,nc_global) dfc = Array{mk_float,1}(undef,nc_global) + gc = Array{mk_float,1}(undef,nc_global) + dgc = Array{mk_float,1}(undef,nc_global) for ivperp in 1:vperp.n for ivpa in 1:vpa.n fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) d2fvpavperp_dvpa2_exact[ivpa,ivperp] = (4.0*vpa.grid[ivpa]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvperp2_exact[ivpa,ivperp] = (4.0*vperp.grid[ivperp]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) end end @@ -297,17 +346,25 @@ if abspath(PROGRAM_FILE) == @__FILE__ #print_vector(fc,"fc",nc_global) # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_sparse,fc) + mul!(dgc,KKperp2D_sparse,fc) + # enforce zero bc ? + #enforce_zero_bc!(fc,vpa,vperp) + #enforce_zero_bc!(gc,vpa,vperp) # invert mass matrix and fill fc fc = lu_obj \ dfc + gc = lu_obj \ dgc #fc = cholesky_obj \ dfc #print_vector(fc,"fc",nc_global) # unravel ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) + ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) if nc_global < 30 print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) end @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) + @. d2fvpavperp_dvperp2_err = abs(d2fvpavperp_dvperp2_num - d2fvpavperp_dvperp2_exact) + println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) if nc_global < 30 print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) end @@ -323,4 +380,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("d2fvpavperp_dvpa2_err.pdf") savefig(outfile) + + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvperp2_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvperp2_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2fvpavperp_dvperp2_err.pdf") + savefig(outfile) end \ No newline at end of file From bda5a3c5e5b8f413cdc5a20074171d344aceea5a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 12 Oct 2023 12:16:57 +0000 Subject: [PATCH 150/331] Cleaned version of 2D FEM mass matrix inversion with d2(.)/dvpa2 and d(.)/dvperp2 test differentiations to confirm a correct implementation. --- 2D_FEM_assembly_test.jl | 131 ++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 84 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 8f8172e39..4c55058be 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -76,13 +76,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 9 #number of points per element + ngrid = 17 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 16 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 6.0 #physical box size in reference units - Lvperp = 3.0 #physical box size in reference units + Lvpa = 12.0 #physical box size in reference units + Lvperp = 6.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" @@ -152,35 +152,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ic_local,icp_local) - # indices of vpa, vperp within the element - ivpa_local = ivpa_func(ic_local,vpa.ngrid) - ivperp_local = ivperp_func(ic_local,vpa.ngrid) - ivpap_local = ivpa_func(icp_local,vpa.ngrid) - ivperpp_local = ivperp_func(icp_local,vpa.ngrid) - # global indices on the grids - ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] - ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] - ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] - ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] - # global compound indices - ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) - return ivpa_local, ivperp_local, ivpap_local, ivperpp_local, ic_global, icp_global - end - - #function get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - # # global indices on the grids - # ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] - # ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] - # ivpap_global = vpa.igrid_full[ivpap_local,ielement_vpa] - # ivperpp_global = vperp.igrid_full[ivperpp_local,ielement_vperp] - # # global compound indices - # ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - # icp_global = ic_func(ivpap_global,ivperpp_global,vpa.n) - # return ic_global, icp_global - #end - function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) # global indices on the grids ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] @@ -198,60 +169,52 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivpa_local in 1:vpa.ngrid ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) icp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) - println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) - println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) - println("ic: ",ic_global," icp: ",icp_global) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - # assign mass matrix data - println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + (upper_boundary_row_vperp && ivperp_local == vperp.ngrid && ivpa_local == ivpap_local) + MM2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + end + + # assign K matrices KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local] - # boundary condition possibilities - lower_boundary_vpa = (ielement_vpa == 1 && ivpa_local == 1 && ivpap_local == 1) - upper_boundary_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid && ivpap_local == vpa.ngrid) - upper_boundary_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid && ivperpp_local == vperp.ngrid) - # lower boundary in vpa, not a boundary in vperp - if lower_boundary_vpa #&& !upper_boundary_vperp - MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* - MMperp[ivperp_local,ivperpp_local] - #MM2D[ic_global,icp_global] = 1.0 - #println("MM2D = 1.0: BC") - end - if lower_boundary_vpa && upper_boundary_vperp - MM2D[ic_global,icp_global] += MMpar[vpa.ngrid,vpa.ngrid]* - MMperp_p1[1,1] - #MM2D[ic_global,icp_global] = 1.0 - #println("MM2D = 1.0: BC") - end - # upper boundary in vpa, not a boundary in vperp - if upper_boundary_vpa #&& !upper_boundary_vperp - MM2D[ic_global,icp_global] += MMpar[1,1]* - MMperp[ivperp_local,ivperpp_local] - #MM2D[ic_global,icp_global] = 1.0 - #println("MM2D = 1.0: BC") - end - if upper_boundary_vpa && upper_boundary_vperp - MM2D[ic_global,icp_global] += MMpar[1,1]* - MMperp_p1[1,1] - #MM2D[ic_global,icp_global] = 1.0 - #println("MM2D = 1.0: BC") - end - # upper boundary in vperp, not a boundary in vpa - if upper_boundary_vperp && !(lower_boundary_vpa) && !(upper_boundary_vpa) - get_QQ_local!(MMperp_p1,ielement_vperp+1,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp_p1[1,1] - #MM2D[ic_global,icp_global] = 1.0 - #println("MM2D = 1.0: BC") - end + end end end @@ -332,9 +295,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # boundary conditions - fvpavperp[vpa.n,:] .= 0.0 - fvpavperp[1,:] .= 0.0 - fvpavperp[:,vperp.n] .= 0.0 + #fvpavperp[vpa.n,:] .= 0.0 + #fvpavperp[1,:] .= 0.0 + #fvpavperp[:,vperp.n] .= 0.0 #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) @@ -347,9 +310,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_sparse,fc) mul!(dgc,KKperp2D_sparse,fc) - # enforce zero bc ? - #enforce_zero_bc!(fc,vpa,vperp) - #enforce_zero_bc!(gc,vpa,vperp) + # enforce zero bc + enforce_zero_bc!(fc,vpa,vperp) + enforce_zero_bc!(gc,vpa,vperp) # invert mass matrix and fill fc fc = lu_obj \ dfc gc = lu_obj \ dgc From 97ce249b721404319bdf3704faeb6d7e56efc940 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 12 Oct 2023 13:57:32 +0000 Subject: [PATCH 151/331] Successful Laplacian solve for H_Maxwellian using exact boundary data. --- 2D_FEM_assembly_test.jl | 117 ++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 4c55058be..56b87d179 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -14,6 +14,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int + using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -76,10 +77,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 17 #number of points per element - nelement_local_vpa = 16 # number of elements per rank + ngrid = 9 #number of points per element + nelement_local_vpa = 8 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 16 # number of elements per rank + nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units @@ -143,6 +144,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ KKpar2D .= 0.0 KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) KKperp2D .= 0.0 + # Laplacian matrix + LP2D = Array{mk_float,2}(undef,nc_global,nc_global) + LP2D .= 0.0 #print_matrix(MM2D,"MM2D",nc_global,nc_global) # local dummy arrays @@ -151,6 +155,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) # global indices on the grids @@ -158,7 +163,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] # global compound index ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - return ic_global + return ic_global, ivpa_global, ivperp_global end for ielement_vperp in 1:vperp.nelement_local @@ -167,8 +172,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivperp_local in 1:vperp.ngrid for ivpap_local in 1:vpa.ngrid for ivpa_local in 1:vpa.ngrid - ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) @@ -177,6 +182,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") # boundary condition possibilities lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) @@ -186,27 +192,36 @@ if abspath(PROGRAM_FILE) == @__FILE__ if lower_boundary_row_vpa if ivpap_local == 1 && ivperp_local == ivperpp_local MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - (upper_boundary_row_vperp && ivperp_local == vperp.ngrid && ivpa_local == ivpap_local) - MM2D[ic_global,icp_global] = 1.0 + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 end else # assign mass matrix data - println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] + LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + LLperp[ivperp_local,ivperpp_local]) end # assign K matrices @@ -228,7 +243,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivpa_local = 1 for ielement_vperp in 1:vperp.nelement_local for ivperp_local in 1:vperp.ngrid - ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) fc[ic_global] = 0.0 end end @@ -238,7 +253,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivpa_local = vpa.ngrid for ielement_vperp in 1:vperp.nelement_local for ivperp_local in 1:vperp.ngrid - ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) fc[ic_global] = 0.0 end end @@ -248,12 +263,44 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp_local = vperp.ngrid for ielement_vpa in 1:vpa.nelement_local for ivpa_local in 1:vpa.ngrid - ic_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) fc[ic_global] = 0.0 end end end + function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + end + if nc_global < 30 print_matrix(MM2D,"MM2D",nc_global,nc_global) print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) @@ -264,10 +311,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) KKperp2D_sparse = sparse(KKperp2D) + LP2D_sparse = sparse(LP2D) # create LU decomposition for mass matrix inversion - lu_obj = lu(MM2D_sparse) + lu_obj_MM = lu(MM2D_sparse) + lu_obj_LP = lu(LP2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) # define a test function @@ -314,8 +363,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_zero_bc!(fc,vpa,vperp) enforce_zero_bc!(gc,vpa,vperp) # invert mass matrix and fill fc - fc = lu_obj \ dfc - gc = lu_obj \ dgc + fc = lu_obj_MM \ dfc + gc = lu_obj_MM \ dgc #fc = cholesky_obj \ dfc #print_vector(fc,"fc",nc_global) # unravel @@ -356,4 +405,42 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("d2fvpavperp_dvperp2_err.pdf") savefig(outfile) + + # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test + + F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dens = 1.0 + upar = 0.0 + vth = 1.0 + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + F_M[ivpa,ivperp] = -(4.0/sqrt(pi))*F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + end + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,MM2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact) + fc = lu_obj_LP \ dfc + ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) + @. H_M_err = abs(H_M_num - H_M_exact) + println("maximum(H_M_err): ",maximum(H_M_err)) + @views heatmap(vperp.grid, vpa.grid, H_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("H_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("H_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, H_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("H_M_err.pdf") + savefig(outfile) + + end \ No newline at end of file From 3126e421b6a3263dd278bc611b2375563dd09f9e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 12 Oct 2023 14:17:00 +0000 Subject: [PATCH 152/331] Successful calculation of G via two Laplacian solves using exact boundary data. Addition of test functions to fokker_planck.jl --- 2D_FEM_assembly_test.jl | 30 ++++++++++++++++++++++++++++-- src/fokker_planck.jl | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 56b87d179..16bfa1d34 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -14,7 +14,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int - using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian + using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -412,13 +412,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dens = 1.0 - upar = 0.0 + upar = 1.0 vth = 1.0 for ivperp in 1:vperp.n for ivpa in 1:vpa.n F_M[ivpa,ivperp] = -(4.0/sqrt(pi))*F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -441,6 +445,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("H_M_err.pdf") savefig(outfile) + + @. F_M = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,MM2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact) + fc = lu_obj_LP \ dfc + ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) + @. G_M_err = abs(G_M_num - G_M_exact) + println("maximum(G_M_err): ",maximum(G_M_err)) + @views heatmap(vperp.grid, vpa.grid, G_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("G_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("G_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, G_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("G_M_err.pdf") + savefig(outfile) end \ No newline at end of file diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 4d8bc4c81..49376f91c 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -15,6 +15,7 @@ export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +export H_Maxwellian, G_Maxwellian export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing @@ -1104,6 +1105,39 @@ end # of the Rosenbluth potentials for a shifted Maxwellian # or provide an estimate for collisional coefficients +# G (defined by Del^4 G = -(8/sqrt(pi))*F +# with F = cref^3 pi^(3/2) F_Maxwellian / nref +# the normalised Maxwellian +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end + +# H (defined by Del^2 H = -(4/sqrt(pi))*F +# with F = cref^3 pi^(3/2) F_Maxwellian / nref +# the normalised Maxwellian +function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 + H = 2.0/sqrt(pi) + else + # H_M = erf(eta)/eta + H = erf(eta)/eta + end + return H*dens/vth +end + # 1D derivative functions function dGdeta(eta::mk_float) From dd9b0ee91655e52f7c07987d8d937a8c1c024a7b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 12 Oct 2023 15:54:51 +0000 Subject: [PATCH 153/331] Add test of calculation of d2Gdvpa2 for a Maxwellian distribution. --- 2D_FEM_assembly_test.jl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 16bfa1d34..789c4a741 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,6 +15,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian + using moment_kinetics.fokker_planck: d2Gdvpa2 using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -415,6 +416,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) G_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dens = 1.0 upar = 1.0 vth = 1.0 @@ -423,6 +428,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ F_M[ivpa,ivperp] = -(4.0/sqrt(pi))*F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -467,6 +473,28 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("G_M_err.pdf") savefig(outfile) + + @. F_M = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,KKpar2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact) + fc = lu_obj_LP \ dfc + ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) + @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) + println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvpa2_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvpa2_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvpa2_M_err.pdf") + savefig(outfile) end \ No newline at end of file From 98564f2d2b54ab6626aad1efaeebb687e141a6bd Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 09:32:14 +0000 Subject: [PATCH 154/331] Partially successful implementation of calculation of dGdvperp with exact boundary data. Convergence at the expected rate appears to be obtained for ngrid = 2, but for ngrid > 1 a Gibbs phenomena appears near vperp = 0. --- 2D_FEM_assembly_test.jl | 117 +++++++++++++++++++--- src/gauss_legendre.jl | 208 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 304 insertions(+), 21 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 789c4a741..8f62b7d01 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,7 +15,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2 + using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -78,13 +78,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 9 #number of points per element - nelement_local_vpa = 8 # number of elements per rank + ngrid = 3 #number of points per element + nelement_local_vpa = 100 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 8 # number of elements per rank + nelement_local_vperp = 100 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 12.0 #physical box size in reference units - Lvperp = 6.0 #physical box size in reference units + Lvpa = 6.0 #physical box size in reference units + Lvperp = 3.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" @@ -145,18 +145,29 @@ if abspath(PROGRAM_FILE) == @__FILE__ KKpar2D .= 0.0 KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) KKperp2D .= 0.0 + PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PUperp2D .= 0.0 # Laplacian matrix LP2D = Array{mk_float,2}(undef,nc_global,nc_global) LP2D .= 0.0 + # Modified Laplacian matrix + LV2D = Array{mk_float,2}(undef,nc_global,nc_global) + LV2D .= 0.0 #print_matrix(MM2D,"MM2D",nc_global,nc_global) # local dummy arrays MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) # global indices on the grids @@ -166,6 +177,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) return ic_global, ivpa_global, ivperp_global end + + impose_BC_at_zero_vperp = false for ielement_vperp in 1:vperp.nelement_local for ielement_vpa in 1:vpa.nelement_local @@ -181,12 +194,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("ic: ",ic_global," icp: ",icp_global) get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") # boundary condition possibilities lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) @@ -194,25 +214,41 @@ if abspath(PROGRAM_FILE) == @__FILE__ if ivpap_local == 1 && ivperp_local == ivperpp_local MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp + if ivperpp_local == 1 && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 else MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 end else # assign mass matrix data @@ -223,6 +259,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ MMperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* LLperp[ivperp_local,ivperpp_local]) + LV2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + MNperp[ivperp_local,ivperpp_local])) end # assign K matrices @@ -230,7 +272,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ MMperp[ivperp_local,ivperpp_local] KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local] - + # assign PU matrix + PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] + end end end @@ -270,7 +315,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end - function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc) + function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc;dirichlet_vperp_BC=false) # lower vpa boundary ielement_vpa = 1 ivpa_local = 1 @@ -290,6 +335,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc[ic_global] = f_bc[ivpa_global,ivperp_global] end end + + if dirichlet_vperp_BC + # upper vperp boundary + ielement_vperp = 1 + ivperp_local = 1 + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + end # upper vperp boundary ielement_vperp = vperp.nelement_local @@ -305,19 +362,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ if nc_global < 30 print_matrix(MM2D,"MM2D",nc_global,nc_global) print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + print_matrix(LP2D,"LP",nc_global,nc_global) + print_matrix(LV2D,"LV",nc_global,nc_global) end # convert these matrices to sparse matrices MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) KKperp2D_sparse = sparse(KKperp2D) - LP2D_sparse = sparse(LP2D) + LP2D_sparse = sparse(LP2D) + LV2D_sparse = sparse(LV2D) # create LU decomposition for mass matrix inversion lu_obj_MM = lu(MM2D_sparse) - lu_obj_LP = lu(LP2D_sparse) + lu_obj_LP = lu(LP2D_sparse) + lu_obj_LV = lu(LV2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) # define a test function @@ -419,6 +480,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dens = 1.0 upar = 1.0 @@ -428,13 +492,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ F_M[ivpa,ivperp] = -(4.0/sqrt(pi))*F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact) + enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) @. H_M_err = abs(H_M_num - H_M_exact) @@ -456,7 +521,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact) + enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) @. G_M_err = abs(G_M_num - G_M_exact) @@ -478,7 +543,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,KKpar2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact) + enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) @@ -496,5 +561,27 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("d2Gdvpa2_M_err.pdf") savefig(outfile) + @. F_M = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,PUperp2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + fc = lu_obj_LV \ dfc + ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) + @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) + println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dGdvperp_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dGdvperp_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dGdvperp_M_err.pdf") + savefig(outfile) + end \ No newline at end of file diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 7468c35e6..dc836e0fe 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -20,6 +20,7 @@ export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral export GaussLegendre_weak_product_matrix! export ielement_global_func +export get_QQ_local! using FastGaussQuadrature using LegendrePolynomials: Pl, dnPl @@ -44,6 +45,8 @@ struct gausslegendre_base_info{} M0::Array{mk_float,2} # local mass matrix type 1 M1::Array{mk_float,2} + # local mass matrix type 2 + M2::Array{mk_float,2} # local S (weak derivative) matrix type 0 S0::Array{mk_float,2} # local S (weak derivative) matrix type 1 @@ -52,8 +55,14 @@ struct gausslegendre_base_info{} K0::Array{mk_float,2} # local K (weak second derivative) matrix type 1 K1::Array{mk_float,2} - # local P (weak derivative) matrix type 0 + # local K (weak second derivative) matrix type 2 + K2::Array{mk_float,2} + # local P (weak derivative no integration by parts) matrix type 0 P0::Array{mk_float,2} + # local P (weak derivative no integration by parts) matrix type 1 + P1::Array{mk_float,2} + # local P (weak derivative no integration by parts) matrix type 2 + P2::Array{mk_float,2} # boundary condition differentiation matrix (for vperp grid using radau points) D0::Array{mk_float,1} end @@ -107,6 +116,8 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendre_weak_product_matrix!(M0,coord.ngrid,x,w,coord.L,coord.nelement_global,"M0") M1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(M1,coord.ngrid,x,w,coord.L,coord.nelement_global,"M1") + M2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M2,coord.ngrid,x,w,coord.L,coord.nelement_global,"M2") S0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(S0,coord.ngrid,x,w,coord.L,coord.nelement_global,"S0") S1 = allocate_float(coord.ngrid, coord.ngrid) @@ -115,12 +126,18 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) GaussLegendre_weak_product_matrix!(K0,coord.ngrid,x,w,coord.L,coord.nelement_global,"K0") K1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(K1,coord.ngrid,x,w,coord.L,coord.nelement_global,"K1") + K2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K2,coord.ngrid,x,w,coord.L,coord.nelement_global,"K2") P0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,coord.L,coord.nelement_global,"P0") + P1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P1,coord.ngrid,x,w,coord.L,coord.nelement_global,"P1") + P2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P2,coord.ngrid,x,w,coord.L,coord.nelement_global,"P2") D0 = allocate_float(coord.ngrid) #@. D0 = Dmat[1,:] # values at lower extreme of element GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w,coord.L,coord.nelement_global) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1,K0,K1,K2,P0,P1,P2,D0) end function setup_gausslegendre_pseudospectral_radau(coord) @@ -140,6 +157,8 @@ function setup_gausslegendre_pseudospectral_radau(coord) GaussLegendre_weak_product_matrix!(M0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M0",radau=true) M1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(M1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M1",radau=true) + M2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(M2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M2",radau=true) S0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(S0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S0",radau=true) S1 = allocate_float(coord.ngrid, coord.ngrid) @@ -148,11 +167,17 @@ function setup_gausslegendre_pseudospectral_radau(coord) GaussLegendre_weak_product_matrix!(K0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K0",radau=true) K1 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(K1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K1",radau=true) + K2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(K2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K2",radau=true) P0 = allocate_float(coord.ngrid, coord.ngrid) GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P0",radau=true) + P1 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P1",radau=true) + P2 = allocate_float(coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(P2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P2",radau=true) D0 = allocate_float(coord.ngrid) GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,S0,S1,K0,K1,P0,D0) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1,K0,K1,K2,P0,P1,P2,D0) end """ function for taking the first derivative on Gauss-Legendre points @@ -517,12 +542,16 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op # appropriate inner product of Legendre polys # definition depends on required matrix # for M0: AA = < P_i P_j > - # for M1: AA = < P_i P_j x > + # for M1: AA = < P_i P_j x > + # for M2: AA = < P_i P_j x^2 > # for S0: AA = -< P'_i P_j > # for S1: AA = -< P'_i P_j x > # for K0: AA = -< P'_i P'_j > - # for K1: AA = -< P'_i P'_j x > - # for P0: AA = < P_i P'_j > + # for K1: AA = -< P'_i P'_j x > + # for K2: AA = -< P'_i P'_j x^2 > + # for P0: AA = < P_i P'_j > + # for P1: AA = < P_i P'_j x > + # for P2: AA = < P_i P'_j x^2 > AA = allocate_float(ngrid,ngrid) nquad = 2*ngrid zz, wz = gausslegendre(nquad) @@ -543,6 +572,14 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op end end end + elseif option == "M2" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += (zz[k]^2)*wz[k]*Pl(zz[k],i-1)*Pl(zz[k],j-1) + end + end + end elseif option == "S0" for j in 1:ngrid for i in 1:ngrid @@ -575,6 +612,14 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op end end end + elseif option == "K2" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] -= (zz[k]^2)*wz[k]*dnPl(zz[k],i-1,1)*dnPl(zz[k],j-1,1) + end + end + end elseif option == "P0" for j in 1:ngrid for i in 1:ngrid @@ -583,6 +628,22 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op end end end + elseif option == "P1" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += zz[k]*wz[k]*Pl(zz[k],i-1)*dnPl(zz[k],j-1,1) + end + end + end + elseif option == "P2" + for j in 1:ngrid + for i in 1:ngrid + for k in 1:nquad + AA[i,j] += (zz[k]^2)*wz[k]*Pl(zz[k],i-1)*dnPl(zz[k],j-1,1) + end + end + end end QQ .= 0.0 @@ -944,10 +1005,20 @@ function get_QQ_local!(QQ,ielement, if option == "M" get_MM_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "R" + get_MR_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "N" + get_MN_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "P" + get_PP_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "U" + get_PU_local!(QQ,ielement,lobatto,radau,coord) elseif option == "S" get_SS_local!(QQ,ielement,lobatto,radau,coord) elseif option == "K" get_KK_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "J" + get_KJ_local!(QQ,ielement,lobatto,radau,coord) elseif option == "L" get_LL_local!(QQ,ielement,lobatto,radau,coord) end @@ -1015,6 +1086,33 @@ function get_KK_local!(QQ,ielement, return nothing end +# second derivative matrix with vperp^2 Jacobian factor if +# coord is vperp. Not useful for the vpa coordinate +function get_KJ_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (lobatto.K0*((shift_factor^2)/scale_factor) + + lobatto.K1*shift_factor + + lobatto.K2*scale_factor) + else # radau points + @. QQ = (radau.K0*((shift_factor^2)/scale_factor) + + radau.K1*shift_factor + + radau.K2*scale_factor) + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.K0/scale_factor + end + return nothing +end + function get_LL_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, @@ -1036,4 +1134,102 @@ function get_LL_local!(QQ,ielement, return nothing end +# mass matrix without vperp factor (matrix N) +# only useful for the vperp coordinate +function get_MN_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + #shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = lobatto.M0*scale_factor + else # radau points + @. QQ = radau.M0*scale_factor + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.M0*scale_factor + end + return nothing +end + +# mass matrix with vperp^2 factor (matrix R) +# only useful for the vperp coordinate +function get_MR_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (lobatto.M0*shift_factor^2 + + lobatto.M1*2.0*shift_factor*scale_factor + + lobatto.M2*scale_factor^2)*scale_factor + else # radau points + @. QQ = (radau.M0*shift_factor^2 + + radau.M1*2.0*shift_factor*scale_factor + + radau.M2*scale_factor^2)*scale_factor + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.M0*scale_factor + end + return nothing +end + +# derivative matrix (matrix P, no integration by parts) +# with vperp Jacobian factor if coord is vperp (matrix P) +function get_PP_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = lobatto.P0*shift_factor + lobatto.P1*scale_factor + else # radau points + @. QQ = radau.P0*shift_factor + radau.P1*scale_factor + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.P0 + end + return nothing +end + +# derivative matrix (matrix P, no integration by parts) +# with vperp^2 Jacobian factor if coord is vperp (matrix U) +# not useful for vpa coordinate +function get_PU_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (lobatto.P0*shift_factor^2 + + lobatto.P1*2.0*shift_factor*scale_factor + + lobatto.P2*scale_factor^2) + else # radau points + @. QQ = (radau.P0*shift_factor^2 + + radau.P1*2.0*shift_factor*scale_factor + + radau.P2*scale_factor^2) + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.P0 + end + return nothing +end + + end From 9afc6f099e0a7679f931e5f66b753ccf0d6d7e33 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 09:41:56 +0000 Subject: [PATCH 155/331] Add calculation of d2Gdvperpdvpa by elliptic solve. --- 2D_FEM_assembly_test.jl | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 8f62b7d01..15ff98be2 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,7 +15,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp + using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -78,7 +78,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 3 #number of points per element + ngrid = 2 #number of points per element nelement_local_vpa = 100 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 100 # number of elements per rank @@ -147,6 +147,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ KKperp2D .= 0.0 PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) PUperp2D .= 0.0 + PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPparPUperp2D .= 0.0 # Laplacian matrix LP2D = Array{mk_float,2}(undef,nc_global,nc_global) LP2D .= 0.0 @@ -275,6 +277,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # assign PU matrix PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* PUperp[ivperp_local,ivperpp_local] + PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] end end @@ -483,6 +487,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dens = 1.0 upar = 1.0 @@ -493,7 +500,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -583,5 +591,27 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("dGdvperp_M_err.pdf") savefig(outfile) + @. F_M = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,PPparPUperp2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + fc = lu_obj_LV \ dfc + ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) + @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) + println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperpdvpa_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperpdvpa_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperpdvpa_M_err.pdf") + savefig(outfile) + end \ No newline at end of file From 73f35021018813f2cab74f0be77f9f6bfba4ca94 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 09:49:54 +0000 Subject: [PATCH 156/331] Introduce dummy array for RHS source in elliptic solve. --- 2D_FEM_assembly_test.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 15ff98be2..3a2c6bd0a 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -473,7 +473,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - + + S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -525,8 +526,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("H_M_err.pdf") savefig(outfile) - @. F_M = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @. S_dummy = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) @@ -547,8 +548,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("G_M_err.pdf") savefig(outfile) - @. F_M = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @. S_dummy = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,KKpar2D,fc) enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) @@ -569,8 +570,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("d2Gdvpa2_M_err.pdf") savefig(outfile) - @. F_M = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @. S_dummy = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PUperp2D,fc) enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) @@ -591,8 +592,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("dGdvperp_M_err.pdf") savefig(outfile) - @. F_M = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @. S_dummy = 2.0*H_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PPparPUperp2D,fc) enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) @@ -613,5 +614,4 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("d2Gdvperpdvpa_M_err.pdf") savefig(outfile) - end \ No newline at end of file From db30aefd23bd54ac93f24a1548af64b97e0a87da Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 09:57:31 +0000 Subject: [PATCH 157/331] Added calculation of dHdvpa by elliptic solve using exact boundary data. --- 2D_FEM_assembly_test.jl | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 3a2c6bd0a..1e1bfeabd 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,7 +15,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa + using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, dHdvpa using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -149,6 +149,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ PUperp2D .= 0.0 PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) PPparPUperp2D .= 0.0 + PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPpar2D .= 0.0 # Laplacian matrix LP2D = Array{mk_float,2}(undef,nc_global,nc_global) LP2D .= 0.0 @@ -279,6 +281,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ PUperp[ivperp_local,ivperpp_local] PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* PUperp[ivperp_local,ivperpp_local] + PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] end end @@ -491,7 +495,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperpdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - + dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dens = 1.0 upar = 1.0 vth = 1.0 @@ -502,7 +509,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -524,6 +532,27 @@ if abspath(PROGRAM_FILE) == @__FILE__ @views heatmap(vperp.grid, vpa.grid, H_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("H_M_err.pdf") + savefig(outfile) + + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,PPpar2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + fc = lu_obj_LP \ dfc + ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) + @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) + println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvpa_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvpa_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvpa_M_err.pdf") savefig(outfile) @. S_dummy = 2.0*H_M_num From ea7aaaf3514f5abf25a7a50d1888054d5e21b39e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 10:01:55 +0000 Subject: [PATCH 158/331] Added calculation of dHdvperp by elliptic solve using exact boundary data. --- 2D_FEM_assembly_test.jl | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 1e1bfeabd..f2e9e6f7b 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -15,7 +15,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, dHdvpa + using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -498,6 +498,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dens = 1.0 upar = 1.0 @@ -510,7 +513,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -553,6 +557,27 @@ if abspath(PROGRAM_FILE) == @__FILE__ @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) outfile = string("dHdvpa_M_err.pdf") + savefig(outfile) + + ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,PUperp2D,fc) + enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + fc = lu_obj_LV \ dfc + ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) + @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) + println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvperp_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvperp_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("dHdvperp_M_err.pdf") savefig(outfile) @. S_dummy = 2.0*H_M_num From fe533a18f1984d9081c9672e740989a63ad79bac Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 10:35:18 +0000 Subject: [PATCH 159/331] Move calculatiof 1D elemental matrices to outermost part of assembly loop. --- 2D_FEM_assembly_test.jl | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index f2e9e6f7b..2b9b9c827 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -78,10 +78,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 2 #number of points per element - nelement_local_vpa = 100 # number of elements per rank + ngrid = 17 #number of points per element + nelement_local_vpa = 4 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 100 # number of elements per rank + nelement_local_vperp = 4 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 6.0 #physical box size in reference units Lvperp = 3.0 #physical box size in reference units @@ -185,7 +185,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ impose_BC_at_zero_vperp = false for ielement_vperp in 1:vperp.nelement_local - for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + for ivperpp_local in 1:vperp.ngrid for ivperp_local in 1:vperp.ngrid for ivpap_local in 1:vpa.ngrid @@ -196,17 +209,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) #println("ic: ",ic_global," icp: ",icp_global) - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") - get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") - get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") - get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") - get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") - get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") # boundary condition possibilities lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) From f2eec0219ee866c52e77faa178bb8b7565d459bd Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 10:55:55 +0000 Subject: [PATCH 160/331] Fix bug in KJ matrix for elliptic solve of dGdvperp, dGdvperpdvpa, dHdvperp. These elliptic solves now pass the test. --- src/gauss_legendre.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index dc836e0fe..5a7fd0086 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -1100,11 +1100,11 @@ function get_KJ_local!(QQ,ielement, # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (lobatto.K0*((shift_factor^2)/scale_factor) + - lobatto.K1*shift_factor + + lobatto.K1*2.0*shift_factor + lobatto.K2*scale_factor) else # radau points @. QQ = (radau.K0*((shift_factor^2)/scale_factor) + - radau.K1*shift_factor + + radau.K1*2.0*shift_factor + radau.K2*scale_factor) end else # assume integrals of form int^infty_-infty (.) d vpa From b5a9a889e4d6b79cdc80df0af8b7711c8918f903 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 10:57:09 +0000 Subject: [PATCH 161/331] Diagnostic of 1D element matrices in comments. --- 2D_FEM_assembly_test.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 2b9b9c827..6a6965438 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -193,11 +193,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) + #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) for ielement_vpa in 1:vpa.nelement_local get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) + #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) + #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) for ivperpp_local in 1:vperp.ngrid for ivperp_local in 1:vperp.ngrid From f8d366f378ae494d6a13924026343b4c41c5839d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 11:11:57 +0000 Subject: [PATCH 162/331] output timing data --- 2D_FEM_assembly_test.jl | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 6a6965438..f3b1481af 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -3,6 +3,7 @@ using Plots using LaTeXStrings using MPI using Measures +using Dates if abspath(PROGRAM_FILE) == @__FILE__ using Pkg @@ -183,6 +184,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end impose_BC_at_zero_vperp = false + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) for ielement_vperp in 1:vperp.nelement_local get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") @@ -303,6 +305,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end + println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) function enforce_zero_bc!(fc,vpa,vperp) # lower vpa boundary @@ -388,6 +391,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ print_matrix(LV2D,"LV",nc_global,nc_global) end # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) @@ -396,11 +400,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ LV2D_sparse = sparse(LV2D) # create LU decomposition for mass matrix inversion + println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) lu_obj_MM = lu(MM2D_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) + println("finish LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) # define a test function @@ -529,7 +535,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) end - end + end + + println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) @@ -537,6 +545,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) @. H_M_err = abs(H_M_num - H_M_exact) + println("finish H calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(H_M_err): ",maximum(H_M_err)) @views heatmap(vperp.grid, vpa.grid, H_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -550,7 +559,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("H_M_err.pdf") savefig(outfile) - + + println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PPpar2D,fc) @@ -558,6 +568,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) + println("finish dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -572,6 +583,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = string("dHdvpa_M_err.pdf") savefig(outfile) + println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PUperp2D,fc) @@ -579,6 +591,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) + println("finish dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -592,7 +605,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("dHdvperp_M_err.pdf") savefig(outfile) - + + println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -601,6 +615,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) @. G_M_err = abs(G_M_num - G_M_exact) + println("finish G calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(G_M_err): ",maximum(G_M_err)) @views heatmap(vperp.grid, vpa.grid, G_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -614,7 +629,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("G_M_err.pdf") savefig(outfile) - + + println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -623,6 +639,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) + println("finish d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -636,7 +653,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("d2Gdvpa2_M_err.pdf") savefig(outfile) - + + println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -645,6 +663,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) + println("finish dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) @@ -658,7 +677,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("dGdvperp_M_err.pdf") savefig(outfile) - + + println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -667,6 +687,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) + println("finish d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, windowsize = (360,240), margin = 15pt) From 0cfb33c3f42587c472ec492ed71c87533c4fc0d8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 11:38:48 +0000 Subject: [PATCH 163/331] Added calculation of d2Gdvperp2 = 2 H - d2Gdvpa2 - (1/vperp)dGdvperp, using weak method to deal with (1/vperp) factor. --- 2D_FEM_assembly_test.jl | 42 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index f3b1481af..17581c6e2 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -16,7 +16,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp + using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -152,6 +152,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ PPparPUperp2D .= 0.0 PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) PPpar2D .= 0.0 + MMparMNperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + MMparMNperp2D .= 0.0 # Laplacian matrix LP2D = Array{mk_float,2}(undef,nc_global,nc_global) LP2D .= 0.0 @@ -298,7 +300,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ PUperp[ivperp_local,ivperpp_local] PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] - + # assign RHS mass matrix for d2Gdvperp2 + MMparMNperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MNperp[ivperp_local,ivperpp_local] end end end @@ -508,6 +512,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -530,6 +537,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2(dens,upar,vth,vpa,vperp,ivpa,ivperp) dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) @@ -701,5 +709,35 @@ if abspath(PROGRAM_FILE) == @__FILE__ windowsize = (360,240), margin = 15pt) outfile = string("d2Gdvperpdvpa_M_err.pdf") savefig(outfile) + + # use relation 2H = del2 G to compute d2Gdpverp2 + println("begin d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @. S_dummy = -dGdvperp_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) + #enforce_zero_bc!(fc,vpa,vperp) + mul!(dfc,MMparMNperp2D,fc) + @. S_dummy = 2.0*H_M_num - d2Gdvpa2_M_num + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) + mul!(dgc,MM2D,fc) + dfc += dgc + enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + fc = lu_obj_MM \ dfc + ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) + #@. d2Gdvperp2_M_num += 2.0*H_M_num - d2Gdvpa2_M_num + @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) + println("finish d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperp2_M_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperp2_M_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("d2Gdvperp2_M_err.pdf") + savefig(outfile) end \ No newline at end of file From ed2c660bb0f4ceb32f2f8b71bc706d529699ef50 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 16 Oct 2023 13:12:48 +0000 Subject: [PATCH 164/331] Refactor plotting output to use a generic funciton. --- 2D_FEM_assembly_test.jl | 167 +++++++++------------------------------- 1 file changed, 38 insertions(+), 129 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 17581c6e2..6bbdccd22 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -39,7 +39,22 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("") println("\n") end - + + function plot_test_data(func_exact,func_num,func_err,func_name,vpa,vperp) + @views heatmap(vperp.grid, vpa.grid, func_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, func_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, func_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_err.pdf") + savefig(outfile) + return nothing + end # Array in compound 1D form @@ -79,10 +94,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # define inputs needed for the test - ngrid = 17 #number of points per element + plot_test_output = true + ngrid = 9 #number of points per element nelement_local_vpa = 4 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 4 # number of elements per rank + nelement_local_vperp = 2 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 6.0 #physical box size in reference units Lvperp = 3.0 #physical box size in reference units @@ -472,33 +488,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) if nc_global < 30 print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) + end + if plot_test_output + plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) + plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) end - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvpa2_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvpa2_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvpa2_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvpa2_err.pdf") - savefig(outfile) - - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvperp2_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvperp2_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2fvpavperp_dvperp2_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2fvpavperp_dvperp2_err.pdf") - savefig(outfile) - # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -555,18 +549,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. H_M_err = abs(H_M_num - H_M_exact) println("finish H calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(H_M_err): ",maximum(H_M_err)) - @views heatmap(vperp.grid, vpa.grid, H_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("H_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("H_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("H_M_err.pdf") - savefig(outfile) println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) @@ -578,19 +560,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) println("finish dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvpa_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvpa_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvpa_M_err.pdf") - savefig(outfile) - + println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -601,18 +571,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) println("finish dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvperp_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvperp_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dHdvperp_M_err.pdf") - savefig(outfile) println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num @@ -625,18 +583,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. G_M_err = abs(G_M_num - G_M_exact) println("finish G calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(G_M_err): ",maximum(G_M_err)) - @views heatmap(vperp.grid, vpa.grid, G_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("G_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("G_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("G_M_err.pdf") - savefig(outfile) println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num @@ -649,19 +595,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) println("finish d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvpa2_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvpa2_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvpa2_M_err.pdf") - savefig(outfile) - + println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) @@ -673,19 +607,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) println("finish dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dGdvperp_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dGdvperp_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("dGdvperp_M_err.pdf") - savefig(outfile) - + println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) @@ -697,19 +619,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) println("finish d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperpdvpa_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperpdvpa_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperpdvpa_M_err.pdf") - savefig(outfile) - + # use relation 2H = del2 G to compute d2Gdpverp2 println("begin d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) @. S_dummy = -dGdvperp_M_num @@ -727,17 +637,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) println("finish d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperp2_M_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperp2_M_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_M_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("d2Gdvperp2_M_err.pdf") - savefig(outfile) + + if plot_test_output + plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + end end \ No newline at end of file From 83164f356aae26ace9e648ce4aefca684194463f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 17 Oct 2023 08:09:59 +0000 Subject: [PATCH 165/331] Refactor the routines for extracting the boundary condition data. --- 2D_FEM_assembly_test.jl | 135 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 6bbdccd22..f5bc67b4d 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -92,9 +92,77 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) return ivpa_global end + + struct vpa_vperp_boundary_data + lower_boundary_vpa::Array{mk_float,1} + upper_boundary_vpa::Array{mk_float,1} + upper_boundary_vperp::Array{mk_float,1} + end + + struct rosenbluth_potential_boundary_data + H_data::vpa_vperp_boundary_data + dHdvpa_data::vpa_vperp_boundary_data + dHdvperp_data::vpa_vperp_boundary_data + G_data::vpa_vperp_boundary_data + dGdvperp_data::vpa_vperp_boundary_data + d2Gdvperp2_data::vpa_vperp_boundary_data + d2Gdvperpdvpa_data::vpa_vperp_boundary_data + d2Gdvpa2_data::vpa_vperp_boundary_data + end + + function allocate_boundary_data(vpa,vperp) + lower_boundary_vpa = Array{mk_float,1}(undef,vperp.n) + upper_boundary_vpa = Array{mk_float,1}(undef,vperp.n) + upper_boundary_vperp = Array{mk_float,1}(undef,vpa.n) + return vpa_vperp_boundary_data(lower_boundary_vpa, + upper_boundary_vpa,upper_boundary_vperp) + end + function assign_exact_boundary_data!(func_data::vpa_vperp_boundary_data, + func_exact,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + for ivperp in 1:nvperp + func_data.lower_boundary_vpa[ivperp] = func_exact[1,ivperp] + func_data.upper_boundary_vpa[ivperp] = func_exact[nvpa,ivperp] + end + for ivpa in 1:nvpa + func_data.upper_boundary_vperp[ivpa] = func_exact[ivpa,nvperp] + end + return nothing + end + + function allocate_rosenbluth_potential_boundary_data(vpa,vperp) + H_data = allocate_boundary_data(vpa,vperp) + dHdvpa_data = allocate_boundary_data(vpa,vperp) + dHdvperp_data = allocate_boundary_data(vpa,vperp) + G_data = allocate_boundary_data(vpa,vperp) + dGdvperp_data = allocate_boundary_data(vpa,vperp) + d2Gdvperp2_data = allocate_boundary_data(vpa,vperp) + d2Gdvperpdvpa_data = allocate_boundary_data(vpa,vperp) + d2Gdvpa2_data = allocate_boundary_data(vpa,vperp) + return rosenbluth_potential_boundary_data(H_data,dHdvpa_data, + dHdvperp_data,G_data,dGdvperp_data,d2Gdvperp2_data, + d2Gdvperpdvpa_data,d2Gdvpa2_data) + end + + function calculate_rosenbluth_potential_boundary_data_exact!(rpbd::rosenbluth_potential_boundary_data, + H_exact,dHdvpa_exact,dHdvperp_exact,G_exact,dGdvperp_exact, + d2Gdvperp2_exact,d2Gdvperpdvpa_exact,d2Gdvpa2_exact, + vpa,vperp) + assign_exact_boundary_data!(rpbd.H_data,H_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dHdvpa_data,dHdvpa_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dHdvperp_data,dHdvperp_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.G_data,G_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dGdvperp_data,dGdvperp_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvperp2_data,d2Gdvperp2_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvperpdvpa_data,d2Gdvperpdvpa_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvpa2_data,d2Gdvpa2_exact,vpa,vperp) + return nothing + end + # define inputs needed for the test - plot_test_output = true + plot_test_output = false#true ngrid = 9 #number of points per element nelement_local_vpa = 4 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements @@ -403,6 +471,39 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end + function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.lower_boundary_vpa[ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vpa[ivperp_global] + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vperp[ivpa_global] + end + end + return nothing + end + if nc_global < 30 print_matrix(MM2D,"MM2D",nc_global,nc_global) print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) @@ -538,12 +639,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) end end - + # calculate the Rosenbluth potential boundary data (rpbd) + rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) + calculate_rosenbluth_potential_boundary_data_exact!(rpbd, + H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, + dGdvperp_M_exact,d2Gdvperp2_M_exact, + d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) + println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.H_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) @. H_M_err = abs(H_M_num - H_M_exact) @@ -554,7 +662,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PPpar2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvpa_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) @@ -565,7 +674,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PUperp2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvperp_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) @@ -577,7 +687,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.G_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) @. G_M_err = abs(G_M_num - G_M_exact) @@ -589,7 +700,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,KKpar2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvpa2_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) @@ -601,7 +713,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PUperp2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dGdvperp_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) @@ -613,7 +726,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PPparPUperp2D,fc) - enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperpdvpa_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) @@ -630,7 +744,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) mul!(dgc,MM2D,fc) dfc += dgc - enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) + enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) fc = lu_obj_MM \ dfc ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) #@. d2Gdvperp2_M_num += 2.0*H_M_num - d2Gdvpa2_M_num From d2875912cca5b0ac603ca15e1a42551c4599ba1b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 17 Oct 2023 09:49:32 +0000 Subject: [PATCH 166/331] Attempt (so far unsuccessful) to use numerical integration to provide accurate boundary data for the elliptic solvers. --- 2D_FEM_assembly_test.jl | 591 +++++++++++++++++++++++++--------------- src/fokker_planck.jl | 2 +- 2 files changed, 371 insertions(+), 222 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index f5bc67b4d..67dc1f524 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -4,21 +4,25 @@ using LaTeXStrings using MPI using Measures using Dates +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral +using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian +using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp +using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct +using moment_kinetics.calculus: derivative! +using moment_kinetics.communication +using moment_kinetics.looping +using SparseArrays: sparse +using LinearAlgebra: mul!, lu, cholesky if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") - import moment_kinetics - using moment_kinetics.input_structs: grid_input, advection_input - using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! - using moment_kinetics.type_definitions: mk_float, mk_int - using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian - using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp - using SparseArrays: sparse - using LinearAlgebra: mul!, lu, cholesky function print_matrix(matrix,name,n,m) println("\n ",name," \n") @@ -160,13 +164,227 @@ if abspath(PROGRAM_FILE) == @__FILE__ assign_exact_boundary_data!(rpbd.d2Gdvpa2_data,d2Gdvpa2_exact,vpa,vperp) return nothing end - + + + function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, + weight,func_input,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + for ivperp in 1:nvperp + func_data.lower_boundary_vpa[ivperp] = 0.0 + func_data.upper_boundary_vpa[ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.lower_boundary_vpa[ivperp] += weight[ivpap,ivperpp,1,ivperp]*func_input[ivpap,ivperpp] + func_data.upper_boundary_vpa[ivperp] += weight[ivpap,ivperpp,nvperp,ivperp]*func_input[ivpap,ivperpp] + end + end + end + for ivpa in 1:nvpa + func_data.upper_boundary_vperp[ivpa] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.upper_boundary_vperp[ivpa] += weight[ivpap,ivperpp,ivpa,nvperp]*func_input[ivpap,ivperpp] + end + end + end + return nothing + end + + function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, + fkpl::fokkerplanck_arrays_struct,pdf) + # get derivatives of pdf + dfdvperp = fkpl.dfdvperp + dfdvpa = fkpl.dfdvpa + d2fdvperpdvpa = fkpl.d2fdvperpdvpa + for ivpa in 1:vpa.n + @views derivative!(vperp.scratch, pdf[ivpa,:], vperp, vperp_spectral) + @. dfdvperp[ivpa,:] = vperp.scratch + end + for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) + @. dfdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) + @. d2fdvperpdvpa[:,ivperp] = vpa.scratch + end + + # carry out the numerical integration + calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) + calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) + calculate_boundary_data!(rpbd.dHdvperp_data,fkpl.H1_weights,dfdvperp,vpa,vperp) + # NOT supported! calculate_boundary_data!(rpbd.G_data,G_weights,pdf,vpa,vperp) + calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvperp2_data,fkpl.H2_weights,dfdvperp,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvperpdvpa_data,fkpl.G1_weights,d2fdvperpdvpa,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvpa2_data,fkpl.H3_weights,dfdvpa,vpa,vperp) + + return nothing + end + + function test_rosenbluth_potential_boundary_data(rpbd::rosenbluth_potential_boundary_data, + rpbd_exact::rosenbluth_potential_boundary_data,vpa,vperp) + + error_buffer_vpa = Array{mk_float,1}(undef,vpa.n) + error_buffer_vperp_1 = Array{mk_float,1}(undef,vperp.n) + error_buffer_vperp_2 = Array{mk_float,1}(undef,vperp.n) + test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + #test_boundary_data(rpbd.G_data,rpbd_exact.G_data,vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + + return nothing + end + + function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2) + nvpa = vpa.n + nvperp = vperp.n + for ivperp in 1:nvperp + buffer_vperp_1 = abs(func.lower_boundary_vpa[ivperp] - func_exact.lower_boundary_vpa[ivperp]) + buffer_vperp_2 = abs(func.upper_boundary_vpa[ivperp] - func_exact.upper_boundary_vpa[ivperp]) + end + for ivpa in 1:nvpa + buffer_vpa = abs(func.upper_boundary_vperp[ivpa] - func_exact.upper_boundary_vperp[ivpa]) + end + @serial_region begin + max_lower_vpa_err = maximum(buffer_vperp_1) + max_upper_vpa_err = maximum(buffer_vperp_2) + max_upper_vperp_err = maximum(buffer_vpa) + println(string(func_name*" boundary data:")) + println("max(lower_vpa_err) = ",max_lower_vpa_err) + println("max(upper_vpa_err) = ",max_upper_vpa_err) + println("max(upper_vperp_err) = ",max_upper_vperp_err) + end + return nothing + end + + function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # global indices on the grids + ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] + ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] + # global compound index + ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) + return ic_global, ivpa_global, ivperp_global + end + function enforce_zero_bc!(fc,vpa,vperp) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + end + + function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc;dirichlet_vperp_BC=false) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + if dirichlet_vperp_BC + # upper vperp boundary + ielement_vperp = 1 + ivperp_local = 1 + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + end + + function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.lower_boundary_vpa[ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vpa[ivperp_global] + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vperp[ivpa_global] + end + end + return nothing + end + + # define inputs needed for the test plot_test_output = false#true - ngrid = 9 #number of points per element - nelement_local_vpa = 4 # number of elements per rank + ngrid = 3 #number of points per element + nelement_local_vpa = 32 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 2 # number of elements per rank + nelement_local_vperp = 16 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 6.0 #physical box size in reference units Lvperp = 3.0 #physical box size in reference units @@ -219,6 +437,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ vperp_spectral = false #vperp.duniform_dgrid .= 1.0 end + # Set up MPI + initialize_comms!() + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + begin_serial_region() + # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n @@ -259,19 +486,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - # global indices on the grids - ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] - ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] - # global compound index - ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - return ic_global, ivpa_global, ivperp_global - end - + impose_BC_at_zero_vperp = false - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - + @serial_region begin + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end for ielement_vperp in 1:vperp.nelement_local get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") @@ -393,142 +612,36 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end - println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - - function enforce_zero_bc!(fc,vpa,vperp) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - end - - function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc;dirichlet_vperp_BC=false) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - - if dirichlet_vperp_BC - # upper vperp boundary - ielement_vperp = 1 - ivperp_local = 1 - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - end - - function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.lower_boundary_vpa[ivperp_global] - end - end + @serial_region begin + println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.upper_boundary_vpa[ivperp_global] - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.upper_boundary_vperp[ivpa_global] - end + if nc_global < 30 + print_matrix(MM2D,"MM2D",nc_global,nc_global) + print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + print_matrix(LP2D,"LP",nc_global,nc_global) + print_matrix(LV2D,"LV",nc_global,nc_global) end - return nothing + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end - - if nc_global < 30 - print_matrix(MM2D,"MM2D",nc_global,nc_global) - print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - print_matrix(LP2D,"LP",nc_global,nc_global) - print_matrix(LV2D,"LV",nc_global,nc_global) - end - # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) KKperp2D_sparse = sparse(KKperp2D) LP2D_sparse = sparse(LP2D) LV2D_sparse = sparse(LV2D) - # create LU decomposition for mass matrix inversion - println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - + @serial_region begin + # create LU decomposition for mass matrix inversion + println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end lu_obj_MM = lu(MM2D_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) - println("finish LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - + @serial_region begin + println("finish LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end # define a test function fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -564,7 +677,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) - println("max(ravel_err)",maximum(fvpavperp_err)) + @serial_region begin + println("max(ravel_err)",maximum(fvpavperp_err)) + end #print_vector(fc,"fc",nc_global) # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_sparse,fc) @@ -580,24 +695,29 @@ if abspath(PROGRAM_FILE) == @__FILE__ # unravel ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) - if nc_global < 30 - print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) - end - @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) - println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) - @. d2fvpavperp_dvperp2_err = abs(d2fvpavperp_dvperp2_num - d2fvpavperp_dvperp2_exact) - println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) - if nc_global < 30 - print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) - end - if plot_test_output - plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) - plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) + @serial_region begin + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) + end + @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) + println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) + @. d2fvpavperp_dvperp2_err = abs(d2fvpavperp_dvperp2_num - d2fvpavperp_dvperp2_exact) + println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) + end + if plot_test_output + plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) + plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) + end end # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -628,7 +748,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vth = 1.0 for ivperp in 1:vperp.n for ivpa in 1:vpa.n - F_M[ivpa,ivperp] = -(4.0/sqrt(pi))*F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) @@ -640,49 +760,70 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end # calculate the Rosenbluth potential boundary data (rpbd) + rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) - calculate_rosenbluth_potential_boundary_data_exact!(rpbd, + # use known test function to provide exact data + calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, dGdvperp_M_exact,d2Gdvperp2_M_exact, d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) + # use numerical integration to find the boundary data + # initialise the weights + fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + begin_serial_region() + # do the numerical integration at the boundaries (N.B. G not supported) + calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M) + # test the boundary data calculation + test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) - println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @serial_region begin + println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + @. S_dummy = -(4.0/sqrt(pi))*F_M + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,MM2D,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.H_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) - @. H_M_err = abs(H_M_num - H_M_exact) - println("finish H calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(H_M_err): ",maximum(H_M_err)) - - println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @serial_region begin + @. H_M_err = abs(H_M_num - H_M_exact) + println("finish H calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(H_M_err): ",maximum(H_M_err)) + + println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + @. S_dummy = -(4.0/sqrt(pi))*F_M + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PPpar2D,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvpa_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) - @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) - println("finish dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) - - println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - ravel_vpavperp_to_c!(fc,F_M,vpa.n,vperp.n) + @serial_region begin + @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) + println("finish dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) + + println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + @. S_dummy = -(4.0/sqrt(pi))*F_M + ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) mul!(dfc,PUperp2D,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvperp_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) - @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) - println("finish dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) - - println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @serial_region begin + @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) + println("finish dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) + + println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -691,11 +832,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.G_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) - @. G_M_err = abs(G_M_num - G_M_exact) - println("finish G calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(G_M_err): ",maximum(G_M_err)) - - println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @serial_region begin + @. G_M_err = abs(G_M_num - G_M_exact) + println("finish G calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(G_M_err): ",maximum(G_M_err)) + + println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -704,11 +847,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvpa2_data) fc = lu_obj_LP \ dfc ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) - @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) - println("finish d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) - - println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @serial_region begin + @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) + println("finish d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) + + println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -717,11 +862,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dGdvperp_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) - @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) - println("finish dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) - - println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @serial_region begin + @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) + println("finish dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) + + println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -730,12 +877,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperpdvpa_data) fc = lu_obj_LV \ dfc ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) - @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) - println("finish d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) - - # use relation 2H = del2 G to compute d2Gdpverp2 - println("begin d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + @serial_region begin + @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) + println("finish d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) + + # use relation 2H = del2 G to compute d2Gdpverp2 + println("begin d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end @. S_dummy = -dGdvperp_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -748,20 +897,20 @@ if abspath(PROGRAM_FILE) == @__FILE__ enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) fc = lu_obj_MM \ dfc ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) - #@. d2Gdvperp2_M_num += 2.0*H_M_num - d2Gdvpa2_M_num - @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) - println("finish d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) - - if plot_test_output - plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - end - + @serial_region begin + @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) + println("finish d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) + + if plot_test_output + plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + end + end end \ No newline at end of file diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 49376f91c..83700ff8a 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -4,7 +4,7 @@ module for including the Full-F Fokker-Planck Collision Operator module fokker_planck -export init_fokker_planck_collisions +export init_fokker_planck_collisions, fokkerplanck_arrays_struct export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients From bd5938789a6c0a794ad6dbebb98a54dc73b6243e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 17 Oct 2023 14:13:59 +0000 Subject: [PATCH 167/331] Bugfix for calculation of boundary data by integration -- typo in selection of appropriate weights. Correct calculation of d2fdvperpdvpa to ensure d2Gdvperpdvpa is computed correctly. --- 2D_FEM_assembly_test.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 67dc1f524..c375f0c5e 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -176,7 +176,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivperpp in 1:nvperp for ivpap in 1:nvpa func_data.lower_boundary_vpa[ivperp] += weight[ivpap,ivperpp,1,ivperp]*func_input[ivpap,ivperpp] - func_data.upper_boundary_vpa[ivperp] += weight[ivpap,ivperpp,nvperp,ivperp]*func_input[ivpap,ivperpp] + func_data.upper_boundary_vpa[ivperp] += weight[ivpap,ivperpp,nvpa,ivperp]*func_input[ivpap,ivperpp] end end end @@ -204,7 +204,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ for ivperp in 1:vperp.n @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) @. dfdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) + @views derivative!(vpa.scratch, dfdvperp[:,ivperp], vpa, vpa_spectral) @. d2fdvperpdvpa[:,ivperp] = vpa.scratch end @@ -381,13 +381,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = false#true - ngrid = 3 #number of points per element - nelement_local_vpa = 32 # number of elements per rank + ngrid = 5 #number of points per element + nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 16 # number of elements per rank + nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 6.0 #physical box size in reference units - Lvperp = 3.0 #physical box size in reference units + Lvpa = 12.0 #physical box size in reference units + Lvperp = 6.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" From 656f3a9f8d2c8f9de3d2f3a0b18f8402aedbaa64 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 18 Oct 2023 07:32:40 +0000 Subject: [PATCH 168/331] Begin implementing boundary weight calculation. Refactor weight calculation routine to accept array of dimension nvpa*nvperp -- only the ivpa,ivperp slice is passed in to the integration routines to allow the same functions to be used for both integration weights of 3 (boundary only) and 4 (entire volume) dimensions. --- src/fokker_planck.jl | 228 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 183 insertions(+), 45 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 83700ff8a..071238b95 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -103,6 +103,50 @@ function allocate_fokkerplanck_arrays(vperp,vpa) end +""" +a struct to contain the integration weights for the boundary points +in the (vpa,vperp) domain +""" +struct boundary_integration_weights_struct + lower_vpa_boundary::MPISharedArray{mk_float,3} + upper_vpa_boundary::MPISharedArray{mk_float,3} + upper_vperp_boundary::MPISharedArray{mk_float,3} +end + +""" +a struct used for calculating the integration weights for the +boundary of the velocity space domain in (vpa,vperp) coordinates +""" +struct fokkerplanck_boundary_data_arrays_struct + G0_weights::boundary_integration_weights_struct + G1_weights::boundary_integration_weights_struct + H0_weights::boundary_integration_weights_struct + H1_weights::boundary_integration_weights_struct + H2_weights::boundary_integration_weights_struct + H3_weights::boundary_integration_weights_struct +end + + +function allocate_boundary_integration_weight(vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + lower_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) + upper_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) + upper_vperp_boundary = allocate_shared_float(nvpa,nvperp,nvpa) + return boundary_integration_weights_struct() +end + +function allocate_boundary_integration_weights(vpa,vperp) + G0_weights = allocate_boundary_integration_weight(vpa,vperp) + G1_weights = allocate_boundary_integration_weight(vpa,vperp) + H0_weights = allocate_boundary_integration_weight(vpa,vperp) + H1_weights = allocate_boundary_integration_weight(vpa,vperp) + H2_weights = allocate_boundary_integration_weight(vpa,vperp) + H3_weights = allocate_boundary_integration_weight(vpa,vperp) + return fokkerplanck_boundary_data_arrays_struct(G0_weights, + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights) +end + # initialise the elliptic integral factor arrays # note the definitions of ellipe & ellipk # `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` @@ -147,6 +191,22 @@ function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_in end +""" +function that initialises the arrays needed for Fokker Planck collisions +using numerical integration to compute the Rosenbluth potentials only +at the boundary and using an elliptic solve to obtain the potentials +in the rest of the velocity space domain. +""" + +function init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=false) + bwgt = allocate_boundary_integration_weights(vpa,vperp) + if vperp.n > 1 && precompute_weights + @views init_Rosenbluth_potential_boundary_integration_weights!(bwgt.G0_weights, bwgt.G1_weights, bwgt.H0_weights, bwgt.H1_weights, + bwgt.H2_weights, bwgt.H3_weights, vpa, vperp) + end + return fka +end + """ function that initialises the arrays needed for Fokker Planck collisions """ @@ -168,18 +228,14 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) end - nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid - nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid - ngrid = max(ngrid_vpa,ngrid_vperp) - # get Gauss-Legendre points and weights on (-1,1) + ngrid = max(vpa.ngrid,vperp.ngrid) nquad = 2*ngrid x_legendre, w_legendre = gausslegendre(nquad) #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries nlaguerre = nquad x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) @@ -191,14 +247,7 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] - ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) - ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) - igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] - ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) - ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) vperp_val = vperp.grid[ivperp] vpa_val = vpa.grid[ivpa] @@ -216,12 +265,14 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 end end # loop over elements and grid points within elements on primed coordinate - loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vperp_vpa_elements!(G1_weights[:,:,ivpa,ivperp], + H0_weights[:,:,ivpa,ivperp],H1_weights[:,:,ivpa,ivperp], + H2_weights[:,:,ivpa,ivperp],H3_weights[:,:,ivpa,ivperp], vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) end @@ -231,6 +282,93 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 return nothing end +""" +function for getting the indices used to choose the integration +quadrature +""" +function get_element_limit_indices(ivpa,ivperp,vpa,vperp) + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + return igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, + igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi +end +""" +function that precomputes the required integration weights +only along the velocity space boundaries +""" +function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp) + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + ngrid = max(ngrid_vpa,ngrid_vperp) + + # get Gauss-Legendre points and weights on (-1,1) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # precalculate weights, integrating over Lagrange polynomials + # first compute weights along vpa boundaries + begin_vperp_region() + ivpa = 1 # + @loop_vperp ivperp begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + G1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H2_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H3_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + end + + # now compute the weights along the vperp boundary + @serial_region begin + println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + return nothing +end + function get_imin_imax(coord,iel) j = iel if j > 1 @@ -399,7 +537,7 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids + vpa_val, vperp_val) # values and indices for unprimed (field) grids for igrid_vperp in 1:vperp.ngrid for igrid_vpa in 1:vpa.ngrid # get grid index for point on full grid @@ -432,40 +570,40 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - #(G_weights[ivpap,ivperpp,ivpa,ivperp] += + #(G_weights[ivpap,ivperpp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (G1_weights[ivpap,ivperpp,ivpa,ivperp] += + (G1_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(G2_weights[ivpap,ivperpp,ivpa,ivperp] += + #(G2_weights[ivpap,ivperpp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(G3_weights[ivpap,ivperpp,ivpa,ivperp] += + #(G3_weights[ivpap,ivperpp] += # lagrange_poly_vpa*lagrange_poly_vperp* # G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H0_weights[ivpap,ivperpp,ivpa,ivperp] += + (H0_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H1_weights[ivpap,ivperpp,ivpa,ivperp] += + (H1_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H2_weights[ivpap,ivperpp,ivpa,ivperp] += + (H2_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpap,ivperpp,ivpa,ivperp] += + (H3_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* H_elliptic_integral_factor*(vpa_val - x_kvpa)* x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - #(n_weights[ivpap,ivperpp,ivpa,ivperp] += + #(n_weights[ivpap,ivperpp] += # lagrange_poly_vpa*lagrange_poly_vperp* # x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) end @@ -480,7 +618,7 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) @@ -490,11 +628,11 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi @@ -504,11 +642,11 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local @@ -516,11 +654,11 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end return nothing @@ -531,17 +669,17 @@ function loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) for ielement_vpap in 1:vpa.nelement_local # do integration over part of the domain with no divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end return nothing @@ -552,19 +690,19 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) for ielement_vperpp in 1:ielement_vperp_low-1 vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi @@ -573,12 +711,12 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) end for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local @@ -586,12 +724,12 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end return nothing end @@ -601,18 +739,18 @@ function loop_over_vperp_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_w vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) for ielement_vperpp in 1:vperp.nelement_local vperp_nodes = get_nodes(vperp,ielement_vperpp) vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre, - vpa_val, vperp_val, ivpa, ivperp) + vpa_val, vperp_val) end return nothing end From 760ee9be28e1115e2698b7eb954d4a5541917c48 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 18 Oct 2023 08:05:46 +0000 Subject: [PATCH 169/331] Restore integration weights for the Rosenbluth potential G. --- 2D_FEM_assembly_test.jl | 12 +++++------ src/fokker_planck.jl | 46 +++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index c375f0c5e..29a7211b9 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -212,7 +212,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) calculate_boundary_data!(rpbd.dHdvperp_data,fkpl.H1_weights,dfdvperp,vpa,vperp) - # NOT supported! calculate_boundary_data!(rpbd.G_data,G_weights,pdf,vpa,vperp) + calculate_boundary_data!(rpbd.G_data,fkpl.G0_weights,pdf,vpa,vperp) calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) calculate_boundary_data!(rpbd.d2Gdvperp2_data,fkpl.H2_weights,dfdvperp,vpa,vperp) calculate_boundary_data!(rpbd.d2Gdvperpdvpa_data,fkpl.G1_weights,d2fdvperpdvpa,vpa,vperp) @@ -230,7 +230,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - #test_boundary_data(rpbd.G_data,rpbd_exact.G_data,vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) @@ -381,13 +381,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = false#true - ngrid = 5 #number of points per element + ngrid = 3 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 12.0 #physical box size in reference units - Lvperp = 6.0 #physical box size in reference units + Lvpa = 6.0 #physical box size in reference units + Lvperp = 3.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" @@ -775,7 +775,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M) # test the boundary data calculation test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) - + #rpbd = rpbd_exact @serial_region begin println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 071238b95..a296ad90e 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -38,6 +38,7 @@ for the Fokker-Planck collision operator """ struct fokkerplanck_arrays_struct + G0_weights::MPISharedArray{mk_float,4} G1_weights::MPISharedArray{mk_float,4} H0_weights::MPISharedArray{mk_float,4} H1_weights::MPISharedArray{mk_float,4} @@ -71,6 +72,7 @@ function allocate_fokkerplanck_arrays(vperp,vpa) nvpa = vpa.n nvperp = vperp.n + G0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) @@ -95,7 +97,7 @@ function allocate_fokkerplanck_arrays(vperp,vpa) dfdvperp = allocate_shared_float(nvpa,nvperp) d2fdvperp2 = allocate_shared_float(nvpa,nvperp) - return fokkerplanck_arrays_struct(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + return fokkerplanck_arrays_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, Cssp_result_vpavperp, dfdvpa, d2fdvpa2, @@ -214,7 +216,7 @@ function that initialises the arrays needed for Fokker Planck collisions function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false) fka = allocate_fokkerplanck_arrays(vperp,vpa) if vperp.n > 1 && precompute_weights - @views init_Rosenbluth_potential_integration_weights!(fka.G1_weights, fka.H0_weights, fka.H1_weights, + @views init_Rosenbluth_potential_integration_weights!(fka.G0_weights, fka.G1_weights, fka.H0_weights, fka.H1_weights, fka.H2_weights, fka.H3_weights, vperp, vpa) end return fka @@ -223,7 +225,7 @@ end """ function that precomputes the required integration weights """ -function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) +function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) @serial_region begin println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -253,7 +255,7 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 vpa_val = vpa.grid[ivpa] for ivperpp in 1:vperp.n for ivpap in 1:vpa.n - # G_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + G0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 G1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 @@ -265,7 +267,7 @@ function init_Rosenbluth_potential_integration_weights!(G1_weights,H0_weights,H1 end end # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G1_weights[:,:,ivpa,ivperp], + @views loop_over_vperp_vpa_elements!(G0_weights[:,:,ivpa,ivperp],G1_weights[:,:,ivpa,ivperp], H0_weights[:,:,ivpa,ivperp],H1_weights[:,:,ivpa,ivperp], H2_weights[:,:,ivpa,ivperp],H3_weights[:,:,ivpa,ivperp], vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids @@ -533,7 +535,7 @@ function nel_hi(ielement,nelement) return 1- floor(mk_int, ielement/nelement) end -function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, +function local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids @@ -560,7 +562,7 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, #if mm_test > 1.0 # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) #end - #G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) @@ -570,9 +572,9 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - #(G_weights[ivpap,ivperpp] += - # lagrange_poly_vpa*lagrange_poly_vperp* - # G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (G0_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) (G1_weights[ivpap,ivperpp] += lagrange_poly_vpa*lagrange_poly_vperp* @@ -613,7 +615,7 @@ function local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights, return nothing end -function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, +function loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -628,7 +630,7 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -642,7 +644,7 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -654,7 +656,7 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -664,7 +666,7 @@ function loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_ return nothing end -function loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, +function loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -675,7 +677,7 @@ function loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, x_vpa, w_vpa, x_vperp, w_vperp, @@ -685,7 +687,7 @@ function loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights return nothing end -function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, +function loop_over_vperp_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -697,7 +699,7 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -711,7 +713,7 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - @views loop_over_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperpp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -724,7 +726,7 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -734,7 +736,7 @@ function loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weigh return nothing end -function loop_over_vperp_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, +function loop_over_vperp_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids @@ -745,7 +747,7 @@ function loop_over_vperp_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_w vperp_max = vperp_nodes[end] vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids From 3e516952aafb32f08b9bd2f81a77ee4de4143fef Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 18 Oct 2023 08:39:17 +0000 Subject: [PATCH 170/331] Refactor quadrature setup. --- src/fokker_planck.jl | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index a296ad90e..2cdee57e9 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -226,20 +226,8 @@ end function that precomputes the required integration weights """ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) - @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # get Gauss-Legendre points and weights on (-1,1) - ngrid = max(vpa.ngrid,vperp.ngrid) - nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) - #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries - nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) @serial_region begin println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -284,6 +272,31 @@ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0 return nothing end +""" +function for getting the basic quadratures used for the +numerical integration of the Lagrange polynomials and the +Green's function. +""" +function setup_basic_quadratures(vpa,vperp) + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # get Gauss-Legendre points and weights on (-1,1) + ngrid = max(vpa.ngrid,vperp.ngrid) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + + return x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre +end + + """ function for getting the indices used to choose the integration quadrature From 518211b976b2c3b5e44fd4bfce8f0adad126a7e5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 18 Oct 2023 10:06:34 +0000 Subject: [PATCH 171/331] Working version of the elliptic solvers for the Rosenbluth potentials, using boundary data provided by the Green's functions and numerical integration. Parallelisation is used to speed up the calculation of the boundary data. --- 2D_FEM_assembly_test.jl | 82 +++++++++++++++++++++----- src/fokker_planck.jl | 127 +++++++++++++++++++++++++++++++--------- 2 files changed, 165 insertions(+), 44 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 29a7211b9..791306371 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -12,9 +12,11 @@ using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp -using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct +using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct, fokkerplanck_boundary_data_arrays_struct +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new, boundary_integration_weights_struct using moment_kinetics.calculus: derivative! using moment_kinetics.communication +using moment_kinetics.communication: MPISharedArray using moment_kinetics.looping using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky @@ -98,9 +100,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ end struct vpa_vperp_boundary_data - lower_boundary_vpa::Array{mk_float,1} - upper_boundary_vpa::Array{mk_float,1} - upper_boundary_vperp::Array{mk_float,1} + lower_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vperp::MPISharedArray{mk_float,1} end struct rosenbluth_potential_boundary_data @@ -167,10 +169,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, - weight,func_input,vpa,vperp) + weight::MPISharedArray{mk_float,4},func_input,vpa,vperp) nvpa = vpa.n nvperp = vperp.n - for ivperp in 1:nvperp + #for ivperp in 1:nvperp + begin_vperp_region() + @loop_vperp ivperp begin func_data.lower_boundary_vpa[ivperp] = 0.0 func_data.upper_boundary_vpa[ivperp] = 0.0 for ivperpp in 1:nvperp @@ -180,7 +184,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end - for ivpa in 1:nvpa + #for ivpa in 1:nvpa + begin_vpa_region() + @loop_vpa ivpa begin func_data.upper_boundary_vperp[ivpa] = 0.0 for ivperpp in 1:nvperp for ivpap in 1:nvpa @@ -188,26 +194,65 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end + # return to serial parallelisation + begin_serial_region() + return nothing + end + + function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, + weight::boundary_integration_weights_struct, + func_input,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + #for ivperp in 1:nvperp + begin_vperp_region() + @loop_vperp ivperp begin + func_data.lower_boundary_vpa[ivperp] = 0.0 + func_data.upper_boundary_vpa[ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.lower_boundary_vpa[ivperp] += weight.lower_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] + func_data.upper_boundary_vpa[ivperp] += weight.upper_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] + end + end + end + #for ivpa in 1:nvpa + begin_vpa_region() + @loop_vpa ivpa begin + func_data.upper_boundary_vperp[ivpa] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.upper_boundary_vperp[ivpa] += weight.upper_vperp_boundary[ivpap,ivperpp,ivpa]*func_input[ivpap,ivperpp] + end + end + end + # return to serial parallelisation + begin_serial_region() return nothing end function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, - fkpl::fokkerplanck_arrays_struct,pdf) + fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf) # get derivatives of pdf dfdvperp = fkpl.dfdvperp dfdvpa = fkpl.dfdvpa d2fdvperpdvpa = fkpl.d2fdvperpdvpa - for ivpa in 1:vpa.n + #for ivpa in 1:vpa.n + begin_vpa_region() + @loop_vpa ivpa begin @views derivative!(vperp.scratch, pdf[ivpa,:], vperp, vperp_spectral) @. dfdvperp[ivpa,:] = vperp.scratch end - for ivperp in 1:vperp.n + begin_vperp_region() + @loop_vperp ivperp begin + #for ivperp in 1:vperp.n @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) @. dfdvpa[:,ivperp] = vpa.scratch @views derivative!(vpa.scratch, dfdvperp[:,ivperp], vpa, vpa_spectral) @. d2fdvperpdvpa[:,ivperp] = vpa.scratch end - + # ensure data is synchronized + begin_serial_region() # carry out the numerical integration calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) @@ -381,13 +426,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = false#true - ngrid = 3 #number of points per element + ngrid = 9 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 6.0 #physical box size in reference units - Lvperp = 3.0 #physical box size in reference units + Lvpa = 12.0 #physical box size in reference units + Lvperp = 6.0 #physical box size in reference units bc = "" #not required to take a particular value, not used # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" @@ -769,10 +814,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) # use numerical integration to find the boundary data # initialise the weights - fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + #fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) begin_serial_region() # do the numerical integration at the boundaries (N.B. G not supported) + @serial_region begin + println("begin boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M) + @serial_region begin + println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end # test the boundary data calculation test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) #rpbd = rpbd_exact diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 2cdee57e9..fe0727732 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -5,6 +5,7 @@ module fokker_planck export init_fokker_planck_collisions, fokkerplanck_arrays_struct +export init_fokker_planck_collisions_new export explicit_fokker_planck_collisions! export calculate_Rosenbluth_potentials! export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients @@ -16,7 +17,7 @@ export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian export H_Maxwellian, G_Maxwellian - +export boundary_integration_weights_struct, fokkerplanck_boundary_data_arrays_struct export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing export symmetric_matrix_inverse @@ -126,6 +127,9 @@ struct fokkerplanck_boundary_data_arrays_struct H1_weights::boundary_integration_weights_struct H2_weights::boundary_integration_weights_struct H3_weights::boundary_integration_weights_struct + dfdvpa::MPISharedArray{mk_float,2} + d2fdvperpdvpa::MPISharedArray{mk_float,2} + dfdvperp::MPISharedArray{mk_float,2} end @@ -135,7 +139,8 @@ function allocate_boundary_integration_weight(vpa,vperp) lower_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) upper_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) upper_vperp_boundary = allocate_shared_float(nvpa,nvperp,nvpa) - return boundary_integration_weights_struct() + return boundary_integration_weights_struct(lower_vpa_boundary, + upper_vpa_boundary, upper_vperp_boundary) end function allocate_boundary_integration_weights(vpa,vperp) @@ -145,8 +150,14 @@ function allocate_boundary_integration_weights(vpa,vperp) H1_weights = allocate_boundary_integration_weight(vpa,vperp) H2_weights = allocate_boundary_integration_weight(vpa,vperp) H3_weights = allocate_boundary_integration_weight(vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + dfdvpa = allocate_shared_float(nvpa,nvperp) + d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) + dfdvperp = allocate_shared_float(nvpa,nvperp) return fokkerplanck_boundary_data_arrays_struct(G0_weights, - G1_weights,H0_weights,H1_weights,H2_weights,H3_weights) + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + dfdvpa,d2fdvperpdvpa,dfdvperp) end # initialise the elliptic integral factor arrays @@ -206,7 +217,7 @@ function init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=false) @views init_Rosenbluth_potential_boundary_integration_weights!(bwgt.G0_weights, bwgt.G1_weights, bwgt.H0_weights, bwgt.H1_weights, bwgt.H2_weights, bwgt.H3_weights, vpa, vperp) end - return fka + return bwgt end """ @@ -322,33 +333,17 @@ only along the velocity space boundaries """ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp) - @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) - end - - nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid - nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid - ngrid = max(ngrid_vpa,ngrid_vperp) - - # get Gauss-Legendre points and weights on (-1,1) - nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) - #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries - nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) @serial_region begin - println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end # precalculate weights, integrating over Lagrange polynomials - # first compute weights along vpa boundaries + # first compute weights along lower vpa boundary begin_vperp_region() - ivpa = 1 # + ivpa = 1 # lower_vpa_boundary @loop_vperp ivperp begin #limits where checks required to determine which divergence-safe grid is needed igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) @@ -369,17 +364,91 @@ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, end end # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + @views loop_over_vperp_vpa_elements!(G0_weights.lower_vpa_boundary[:,:,ivperp], + G1_weights.lower_vpa_boundary[:,:,ivperp], + H0_weights.lower_vpa_boundary[:,:,ivperp], + H1_weights.lower_vpa_boundary[:,:,ivperp], + H2_weights.lower_vpa_boundary[:,:,ivperp], + H3_weights.lower_vpa_boundary[:,:,ivperp], vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) + igrid_vpa, igrid_vperp, vpa_val, vperp_val) end - - # now compute the weights along the vperp boundary + # second compute weights along upper vpa boundary + ivpa = vpa.n # upper_vpa_boundary + @loop_vperp ivperp begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + G1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H2_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H3_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights.upper_vpa_boundary[:,:,ivperp], + G1_weights.upper_vpa_boundary[:,:,ivperp], + H0_weights.upper_vpa_boundary[:,:,ivperp], + H1_weights.upper_vpa_boundary[:,:,ivperp], + H2_weights.upper_vpa_boundary[:,:,ivperp], + H3_weights.upper_vpa_boundary[:,:,ivperp], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + # finally compute weight along upper vperp boundary + begin_vpa_region() + ivperp = vperp.n # upper_vperp_boundary + @loop_vpa ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + G1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H2_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H3_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights.upper_vperp_boundary[:,:,ivpa], + G1_weights.upper_vperp_boundary[:,:,ivpa], + H0_weights.upper_vperp_boundary[:,:,ivpa], + H1_weights.upper_vperp_boundary[:,:,ivpa], + H2_weights.upper_vperp_boundary[:,:,ivpa], + H3_weights.upper_vperp_boundary[:,:,ivpa], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + # return the parallelisation status to serial + begin_serial_region() @serial_region begin - println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end return nothing end From cf84ed8774302480cfcb87323eb2f58110160c0b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 18 Oct 2023 16:42:48 +0000 Subject: [PATCH 172/331] First attempt to implement the collision operator using a finite element method with a serial assembly for the RHS vector. Testing the self collision operator on a Maxwellian suggests that the operator is implemented correctly. The fact that the RHS vector is assembled in serial means that the operator is very slow to evaluate (~ 60s for present resolutions, in contrast to the <~ 1s evaluation for each of the Rosenbluth coefficients for the operator). --- 2D_FEM_assembly_test.jl | 101 +++++++++++++- src/gauss_legendre.jl | 292 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 387 insertions(+), 6 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 791306371..9d647eb0b 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -425,7 +425,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test - plot_test_output = false#true + plot_test_output = true ngrid = 9 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements @@ -760,6 +760,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -802,6 +805,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + C_M_exact[ivpa,ivperp] = 0.0 end end # calculate the Rosenbluth potential boundary data (rpbd) @@ -965,4 +969,97 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) end end -end \ No newline at end of file + + rhsc = Array{mk_float,1}(undef,nc_global) + + function assemble_explicit_collision_operator_rhs!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa,d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp) + # assemble RHS of collision operator + @. rhsc = 0.0 + YY0perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) + YY1perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) + YY2perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) + YY3perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) + YY0par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) + YY1par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) + YY2par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) + YY3par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) + #kvpa = 0 + #kvperp = 0 + # loop over elements + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(YY0perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") + get_QQ_local!(YY1perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") + get_QQ_local!(YY2perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") + get_QQ_local!(YY3perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") + + #ivperp_min = vperp.imin[ielement_vperp] - kvperp + #ivperp_max = vperp.imax[ielement_vperp] + + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(YY0par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") + get_QQ_local!(YY1par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") + get_QQ_local!(YY2par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") + get_QQ_local!(YY3par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + + #ivpa_min = vpa.imin[ielement_vpa] - kvpa + #ivpa_max = vpa.imax[ielement_vpa] + # loop over field positions in each element + for ivperp_local in 1:vperp.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + #println(kvpap," ",kvperpp," ",jvpap," ",jvperpp) + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + + #kvpa = 1 + end + #kvperp = 1 + end + # correct for minus sign due to integration by parts + # and multiply by the normalised collision frequency + rhsc *= -nussp + return nothing + end + + @serial_region begin + println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + ms = 1.0 + msp = 1.0 + nussp = 1.0 + assemble_explicit_collision_operator_rhs!(rhsc,F_M,d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp) + enforce_zero_bc!(rhsc,vpa,vperp) + # invert mass matrix and fill fc + fc = lu_obj_MM \ rhsc + ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) + @serial_region begin + @. C_M_err = abs(C_M_num - C_M_exact) + println("finish C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("maximum(C_M_err): ",maximum(C_M_err)) + plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + end + +end diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 5a7fd0086..c17875260 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -65,6 +65,22 @@ struct gausslegendre_base_info{} P2::Array{mk_float,2} # boundary condition differentiation matrix (for vperp grid using radau points) D0::Array{mk_float,1} + # local nonlinear diffusion matrix Y00 + Y00::Array{mk_float,3} + # local nonlinear diffusion matrix Y01 + Y01::Array{mk_float,3} + # local nonlinear diffusion matrix Y10 + Y10::Array{mk_float,3} + # local nonlinear diffusion matrix Y11 + Y11::Array{mk_float,3} + # local nonlinear diffusion matrix Y20 + Y20::Array{mk_float,3} + # local nonlinear diffusion matrix Y21 + Y21::Array{mk_float,3} + # local nonlinear diffusion matrix Y30 + Y30::Array{mk_float,3} + # local nonlinear diffusion matrix Y31 + Y31::Array{mk_float,3} end struct gausslegendre_info{} @@ -137,7 +153,25 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) D0 = allocate_float(coord.ngrid) #@. D0 = Dmat[1,:] # values at lower extreme of element GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w,coord.L,coord.nelement_global) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1,K0,K1,K2,P0,P1,P2,D0) + Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y00") + Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y01") + Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y10") + Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y11") + Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y20") + Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y21") + Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y30") + Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y31") + + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1, + K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end function setup_gausslegendre_pseudospectral_radau(coord) @@ -177,7 +211,24 @@ function setup_gausslegendre_pseudospectral_radau(coord) GaussLegendre_weak_product_matrix!(P2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P2",radau=true) D0 = allocate_float(coord.ngrid) GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1,K0,K1,K2,P0,P1,P2,D0) + Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y00",radau=true) + Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y01",radau=true) + Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y10",radau=true) + Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y11",radau=true) + Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y20",radau=true) + Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y21",radau=true) + Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y30",radau=true) + Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y31",radau=true) + return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1, + K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end """ function for taking the first derivative on Gauss-Legendre points @@ -526,8 +577,9 @@ end """ assign abitrary weak inner product matrix Q on a 1D line with Jacobian = 1 +matrix Q acts on a single vector x such that y = Q * x is also a vector """ -function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,option;radau=false) +function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,2},ngrid,x,wgts,L,nelement_global,option;radau=false) # coefficient in expansion of # lagrange polys in terms of Legendre polys gamma = allocate_float(ngrid) @@ -669,6 +721,136 @@ function GaussLegendre_weak_product_matrix!(QQ,ngrid,x,wgts,L,nelement_global,op return nothing end +""" +assign abitrary weak inner product matrix Q on a 1D line with Jacobian = 1 +matrix Q acts on two vectors x1 and x2 such that the quadratic form +y = x1 * Q * x2 is also a vector +""" +function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,3},ngrid,x,wgts,L,nelement_global,option;radau=false) + # coefficient in expansion of + # lagrange polys in terms of Legendre polys + gamma = allocate_float(ngrid) + for i in 1:ngrid-1 + gamma[i] = Legendre_h_n(i-1) + end + if radau + gamma[ngrid] = Legendre_h_n(ngrid-1) + else + gamma[ngrid] = 2.0/(ngrid - 1) + end + # appropriate inner product of Legendre polys + # definition depends on required matrix + # for Y00: AA = < P_i P_j P_k > + # for Y01: AA = < P_i P_j P_k x > + # for Y10: AA = < P_i P_j P'_k > + # for Y11: AA = < P_i P_j P'_k x > + # for Y20: AA = < P_i P'_j P'_k > + # for Y21: AA = < P_i P'_j P'_k x > + # for Y31: AA = < P_i P'_j P_k x > + # for Y30: AA = < P_i P'_j P_k > + AA = allocate_float(ngrid,ngrid,ngrid) + nquad = 2*ngrid + zz, wz = gausslegendre(nquad) + @. AA = 0.0 + if option == "Y00" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += wz[q]*Pl(zz[q],i-1)*Pl(zz[q],j-1)*Pl(zz[q],k-1) + end + end + end + end + elseif option == "Y01" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += zz[q]*wz[q]*Pl(zz[q],i-1)*Pl(zz[q],j-1)*Pl(zz[q],k-1) + end + end + end + end + elseif option == "Y10" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += wz[q]*Pl(zz[q],i-1)*Pl(zz[q],j-1)*dnPl(zz[q],k-1,1) + end + end + end + end + elseif option == "Y11" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += zz[q]*wz[q]*Pl(zz[q],i-1)*Pl(zz[q],j-1)*dnPl(zz[q],k-1,1) + end + end + end + end + elseif option == "Y20" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += wz[q]*Pl(zz[q],i-1)*dnPl(zz[q],j-1,1)*dnPl(zz[q],k-1,1) + end + end + end + end + elseif option == "Y21" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += zz[q]*wz[q]*Pl(zz[q],i-1)*dnPl(zz[q],j-1,1)*dnPl(zz[q],k-1,1) + end + end + end + end + elseif option == "Y31" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += zz[q]*wz[q]*Pl(zz[q],i-1)*dnPl(zz[q],j-1,1)*Pl(zz[q],k-1) + end + end + end + end + elseif option == "Y30" + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for q in 1:nquad + AA[i,j,k] += wz[q]*Pl(zz[q],i-1)*dnPl(zz[q],j-1,1)*Pl(zz[q],k-1) + end + end + end + end + end + + QQ .= 0.0 + for k in 1:ngrid + for j in 1:ngrid + for i in 1:ngrid + for l in 1:ngrid + for m in 1:ngrid + for n in 1:ngrid + QQ[i,j,k] += wgts[i]*wgts[j]*wgts[k]*Pl(x[i],n-1)*Pl(x[j],m-1)*Pl(x[k],l-1)*AA[n,m,l]/(gamma[n]*gamma[m]*gamma[l]) + end + end + end + end + end + end + return nothing +end + """ assign mass matrix M1mn = < lm|x|ln > on a 1D line with Jacobian = 1 """ @@ -998,7 +1180,7 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, return nothing end -function get_QQ_local!(QQ,ielement, +function get_QQ_local!(QQ::Array{mk_float,2},ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, coord,option) @@ -1231,5 +1413,107 @@ function get_PU_local!(QQ,ielement, return nothing end +""" +construction function for nonlinear diffusion matrices, only +used in the assembly of the collision operator +""" + +function get_QQ_local!(QQ::Array{mk_float,3},ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord,option) + + if option == "YY0" # mass-like matrix + get_YY0_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "YY1" # first-derivative-like matrix + get_YY1_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "YY2" # second-derivative-like matrix + get_YY2_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "YY3" # first-derivative-like matrix + get_YY3_local!(QQ,ielement,lobatto,radau,coord) + end + return nothing +end + +function get_YY0_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (shift_factor*lobatto.Y00 + scale_factor*lobatto.Y01)*scale_factor + else # radau points + @. QQ = (shift_factor*radau.Y00 + scale_factor*radau.Y01)*scale_factor + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.Y00*scale_factor + end + return nothing +end + +function get_YY1_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = shift_factor*lobatto.Y10 + scale_factor*lobatto.Y11 + else # radau points + @. QQ = shift_factor*radau.Y10 + scale_factor*radau.Y11 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.Y10 + end + return nothing +end + +function get_YY2_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = (shift_factor/scale_factor)*lobatto.Y20 + lobatto.Y21 + else # radau points + @. QQ = (shift_factor/scale_factor)*radau.Y20 + radau.Y21 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.Y20/scale_factor + end + return nothing +end + +function get_YY3_local!(QQ,ielement, + lobatto::gausslegendre_base_info, + radau::gausslegendre_base_info, + coord) + + scale_factor = scale_factor_func(coord.L,coord.nelement_global) + shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp + # extra scale and shift factors required because of vperp in integral + if ielement > 1 || coord.irank > 0 # lobatto points + @. QQ = shift_factor*lobatto.Y30 + scale_factor*lobatto.Y31 + else # radau points + @. QQ = shift_factor*radau.Y30 + scale_factor*radau.Y31 + end + else # assume integrals of form int^infty_-infty (.) d vpa + @. QQ = lobatto.Y30 + end + return nothing +end + end From c8c8c3c11ee3a59701566dba9cae8e1a5bd15f56 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 19 Oct 2023 07:22:25 +0000 Subject: [PATCH 173/331] Added C[Fs_M,Fsp_M] test to ensure that C produces the correct value when the collision operator should be nonzero. Fix @. typo in multiplication of nussp. The dominant error in C arise from near vperp = 0. --- 2D_FEM_assembly_test.jl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 9d647eb0b..2dcec43c3 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -10,7 +10,7 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian +using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian, Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct, fokkerplanck_boundary_data_arrays_struct using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new, boundary_integration_weights_struct @@ -759,6 +759,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) + Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -791,11 +792,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + denss, upars, vths = 1.0, -1.0, 2.0/3.0 dens = 1.0 upar = 1.0 vth = 1.0 + ms = 1.0 + msp = 1.0 + nussp = 1.0 for ivperp in 1:vperp.n for ivpa in 1:vpa.n + Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) @@ -805,7 +811,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) - C_M_exact[ivpa,ivperp] = 0.0 + C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + dens,upar,vth,msp, + nussp,vpa,vperp,ivpa,ivperp) end end # calculate the Rosenbluth potential boundary data (rpbd) @@ -1040,17 +1048,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # correct for minus sign due to integration by parts # and multiply by the normalised collision frequency - rhsc *= -nussp + @. rhsc *= -nussp return nothing end @serial_region begin println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - ms = 1.0 - msp = 1.0 - nussp = 1.0 - assemble_explicit_collision_operator_rhs!(rhsc,F_M,d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp) + assemble_explicit_collision_operator_rhs!(rhsc,Fs_M,d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp) enforce_zero_bc!(rhsc,vpa,vperp) # invert mass matrix and fill fc fc = lu_obj_MM \ rhsc From 2acd477126730434c09519ba8cc27946693fb252 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 19 Oct 2023 08:39:05 +0000 Subject: [PATCH 174/331] Implement df/dvperp = 0 as a boundary condition in a version of the 2D mass matrix MM2DZG. This provides a small performance enhancement for calculating d2f/dvperp2 but provides no benefit for improving the accuracy of the collision operator. The feature may nonetheless be desirable from the point of view of stability of the evolving simulation. --- 2D_FEM_assembly_test.jl | 112 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 11 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 2dcec43c3..e49048ae2 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -314,7 +314,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) return ic_global, ivpa_global, ivperp_global end - function enforce_zero_bc!(fc,vpa,vperp) + function enforce_zero_bc!(fc,vpa,vperp;impose_BC_at_zero_vperp=false) # lower vpa boundary ielement_vpa = 1 ivpa_local = 1 @@ -335,6 +335,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end + if impose_BC_at_zero_vperp + # lower vperp boundary + ielement_vperp = 1 + ivperp_local = 1 + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + end + # upper vperp boundary ielement_vperp = vperp.nelement_local ivperp_local = vperp.ngrid @@ -425,7 +437,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test - plot_test_output = true + plot_test_output = true + impose_zero_gradient_BC = false#true ngrid = 9 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements @@ -498,6 +511,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ Index2D = Array{mk_int,2}(undef,nc_global,nc_global) MM2D = Array{mk_float,2}(undef,nc_global,nc_global) MM2D .= 0.0 + MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) + MM2DZG .= 0.0 KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) KKpar2D .= 0.0 KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) @@ -657,11 +672,69 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp + if ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + end + end + end + end + end + end + end @serial_region begin println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) if nc_global < 30 print_matrix(MM2D,"MM2D",nc_global,nc_global) + print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) print_matrix(LP2D,"LP",nc_global,nc_global) @@ -671,6 +744,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end MM2D_sparse = sparse(MM2D) + MM2DZG_sparse = sparse(MM2DZG) KKpar2D_sparse = sparse(KKpar2D) KKperp2D_sparse = sparse(KKperp2D) LP2D_sparse = sparse(LP2D) @@ -681,6 +755,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) end lu_obj_MM = lu(MM2D_sparse) + lu_obj_MMZG = lu(MM2DZG_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) @@ -729,12 +804,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_sparse,fc) mul!(dgc,KKperp2D_sparse,fc) - # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp) - enforce_zero_bc!(gc,vpa,vperp) - # invert mass matrix and fill fc - fc = lu_obj_MM \ dfc - gc = lu_obj_MM \ dgc + if impose_zero_gradient_BC + # enforce zero bc + enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) + enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ dfc + gc = lu_obj_MMZG \ dgc + else + # enforce zero bc + enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) + enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ dfc + gc = lu_obj_MMZG \ dgc + end #fc = cholesky_obj \ dfc #print_vector(fc,"fc",nc_global) # unravel @@ -1056,9 +1140,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) end assemble_explicit_collision_operator_rhs!(rhsc,Fs_M,d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp) - enforce_zero_bc!(rhsc,vpa,vperp) - # invert mass matrix and fill fc - fc = lu_obj_MM \ rhsc + if impose_zero_gradient_BC + enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ rhsc + else + enforce_zero_bc!(rhsc,vpa,vperp) + # invert mass matrix and fill fc + fc = lu_obj_MM \ rhsc + end ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) @serial_region begin @. C_M_err = abs(C_M_num - C_M_exact) From 006c21548b8c0089c277e96c309e285408469ab9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 19 Oct 2023 10:27:28 +0000 Subject: [PATCH 175/331] Refactor RHS assembly routine to use precalculated YY* arrays. --- 2D_FEM_assembly_test.jl | 100 +++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index e49048ae2..7b4aec669 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -439,10 +439,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = true impose_zero_gradient_BC = false#true - ngrid = 9 #number of points per element - nelement_local_vpa = 16 # number of elements per rank + ngrid = 3 #number of points per element + nelement_local_vpa = 64 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 8 # number of elements per rank + nelement_local_vperp = 32 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units @@ -1063,35 +1063,87 @@ if abspath(PROGRAM_FILE) == @__FILE__ end rhsc = Array{mk_float,1}(undef,nc_global) + + struct YY_collision_operator_arrays + # let phi_j(vperp) be the jth Lagrange basis function, + # and phi'_j(vperp) the first derivative of the Lagrange basis function + # on the iel^th element. Then, the arrays are defined as follows. + # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp + YY0perp::Array{mk_float,4} + # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp + YY1perp::Array{mk_float,4} + # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp + YY2perp::Array{mk_float,4} + # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp + YY3perp::Array{mk_float,4} + # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa + YY0par::Array{mk_float,4} + # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa + YY1par::Array{mk_float,4} + # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa + YY2par::Array{mk_float,4} + # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa + YY3par::Array{mk_float,4} + end + + function calculate_YY_arrays(vpa,vperp) + YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY3perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY0par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY1par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY2par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY3par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + + for ielement_vperp in 1:vperp.nelement_local + @views get_QQ_local!(YY0perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") + @views get_QQ_local!(YY1perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") + @views get_QQ_local!(YY2perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") + @views get_QQ_local!(YY3perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") + end + for ielement_vpa in 1:vpa.nelement_local + @views get_QQ_local!(YY0par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") + @views get_QQ_local!(YY1par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") + @views get_QQ_local!(YY2par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") + @views get_QQ_local!(YY3par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + end - function assemble_explicit_collision_operator_rhs!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa,d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp) + return YY_collision_operator_arrays(YY0perp,YY1perp,YY2perp,YY3perp, + YY0par,YY1par,YY2par,YY3par) + end + + function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral, + YY_arrays::YY_collision_operator_arrays) # assemble RHS of collision operator @. rhsc = 0.0 - YY0perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) - YY1perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) - YY2perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) - YY3perp = Array{mk_float,3}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid) - YY0par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) - YY1par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) - YY2par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) - YY3par = Array{mk_float,3}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid) + #kvpa = 0 #kvperp = 0 # loop over elements for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(YY0perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") - get_QQ_local!(YY1perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") - get_QQ_local!(YY2perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") - get_QQ_local!(YY3perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") - + #get_QQ_local!(YY0perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") + #get_QQ_local!(YY1perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") + #get_QQ_local!(YY2perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") + #get_QQ_local!(YY3perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") + YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] #ivperp_min = vperp.imin[ielement_vperp] - kvperp #ivperp_max = vperp.imax[ielement_vperp] for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(YY0par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") - get_QQ_local!(YY1par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") - get_QQ_local!(YY2par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") - get_QQ_local!(YY3par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + #get_QQ_local!(YY0par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") + #get_QQ_local!(YY1par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") + #get_QQ_local!(YY2par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") + #get_QQ_local!(YY3par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] #ivpa_min = vpa.imin[ielement_vpa] - kvpa #ivpa_max = vpa.imax[ielement_vpa] @@ -1139,7 +1191,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - assemble_explicit_collision_operator_rhs!(rhsc,Fs_M,d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp) + YY_arrays = calculate_YY_arrays(vpa,vperp) + assemble_explicit_collision_operator_rhs_serial!(rhsc,Fs_M, + d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, + dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) if impose_zero_gradient_BC enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) # invert mass matrix and fill fc From c45ec9b8d091d108a3f0ec6c345663357b73b46c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 19 Oct 2023 11:56:32 +0000 Subject: [PATCH 176/331] Added capability to parallelise the RHS assembly step for the collision operator. To exploit the shared-memory parallelism we have to rewrite the loop over elements to a loop over collocation points, with special behaviour at collocation points that happen to be shared element boundaries. This is acheived without conditional statements inside the loops. --- 2D_FEM_assembly_test.jl | 138 ++++++++++++++++++++++++++++++---------- src/fokker_planck.jl | 1 + src/gauss_legendre.jl | 8 ++- 3 files changed, 111 insertions(+), 36 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 7b4aec669..4a8b95222 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -14,6 +14,7 @@ using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian, C using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct, fokkerplanck_boundary_data_arrays_struct using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new, boundary_integration_weights_struct +using moment_kinetics.fokker_planck: get_element_limit_indices using moment_kinetics.calculus: derivative! using moment_kinetics.communication using moment_kinetics.communication: MPISharedArray @@ -438,11 +439,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = true - impose_zero_gradient_BC = false#true + impose_zero_gradient_BC = false#true + test_parallelism = true ngrid = 3 #number of points per element - nelement_local_vpa = 64 # number of elements per rank + nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 32 # number of elements per rank + nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units @@ -760,7 +762,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ lu_obj_LV = lu(LV2D_sparse) #cholesky_obj = cholesky(MM2D_sparse) @serial_region begin - println("finish LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) end # define a test function @@ -937,7 +939,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) @serial_region begin @. H_M_err = abs(H_M_num - H_M_exact) - println("finish H calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished H calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(H_M_err): ",maximum(H_M_err)) println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -952,7 +954,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) @serial_region begin @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) - println("finish dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -967,7 +969,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) @serial_region begin @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) - println("finish dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -982,7 +984,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) @serial_region begin @. G_M_err = abs(G_M_num - G_M_exact) - println("finish G calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished G calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(G_M_err): ",maximum(G_M_err)) println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -997,7 +999,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) @serial_region begin @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) - println("finish d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -1012,7 +1014,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) @serial_region begin @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) - println("finish dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -1027,7 +1029,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) @serial_region begin @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) - println("finish d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) # use relation 2H = del2 G to compute d2Gdpverp2 @@ -1047,7 +1049,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) @serial_region begin @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) - println("finish d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) if plot_test_output @@ -1063,6 +1065,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ end rhsc = Array{mk_float,1}(undef,nc_global) + rhsc_check = Array{mk_float,1}(undef,nc_global) + rhsc_err = Array{mk_float,1}(undef,nc_global) + rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) struct YY_collision_operator_arrays # let phi_j(vperp) be the jth Lagrange basis function, @@ -1120,33 +1125,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ # assemble RHS of collision operator @. rhsc = 0.0 - #kvpa = 0 - #kvperp = 0 # loop over elements for ielement_vperp in 1:vperp.nelement_local - #get_QQ_local!(YY0perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") - #get_QQ_local!(YY1perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") - #get_QQ_local!(YY2perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") - #get_QQ_local!(YY3perp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - #ivperp_min = vperp.imin[ielement_vperp] - kvperp - #ivperp_max = vperp.imax[ielement_vperp] for ielement_vpa in 1:vpa.nelement_local - #get_QQ_local!(YY0par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") - #get_QQ_local!(YY1par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") - #get_QQ_local!(YY2par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") - #get_QQ_local!(YY3par,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] - #ivpa_min = vpa.imin[ielement_vpa] - kvpa - #ivpa_max = vpa.imax[ielement_vpa] # loop over field positions in each element for ivperp_local in 1:vperp.ngrid for ivpa_local in 1:vpa.ngrid @@ -1161,7 +1152,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ pdfjj = pdfs[jvpap,jvperpp] for kvpap_local in 1:vpa.ngrid kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - #println(kvpap," ",kvperpp," ",jvpap," ",jvperpp) # first three lines represent parallel flux terms # second three lines represent perpendicular flux terms rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + @@ -1176,11 +1166,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end end - end - - #kvpa = 1 + end end - #kvperp = 1 end # correct for minus sign due to integration by parts # and multiply by the normalised collision frequency @@ -1188,14 +1175,97 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral, + YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + rhsvpavperp[ivpa,ivperp] = 0.0 + end + # loop over collocation points to benefit from shared-memory parallelism + ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid + @loop_vperp_vpa ivperp_global ivpa_global begin + igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) + # loop over elements belonging to this collocation point + for ielement_vperp in ielement_vperp_low:ielement_vperp_hi + # correct local ivperp in the case that we on a boundary point + ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) + YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in ielement_vpa_low:ielement_vpa_hi + # correct local ivpa in the case that we on a boundary point + ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) + YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + # ravel to compound index + begin_serial_region() + ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) + return nothing + end + @serial_region begin - println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) end YY_arrays = calculate_YY_arrays(vpa,vperp) - assemble_explicit_collision_operator_rhs_serial!(rhsc,Fs_M, + @serial_region begin + println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + if test_parallelism + assemble_explicit_collision_operator_rhs_serial!(rhsc_check,Fs_M, + d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, + dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) + @serial_region begin + println("finished C RHS assembly (serial) ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,Fs_M, d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) + @serial_region begin + println("finished C RHS assembly (parallel) ", Dates.format(now(), dateformat"H:MM:SS")) + end + if test_parallelism + @serial_region begin + @. rhsc_err = abs(rhsc - rhsc_check) + println("maximum(rhsc_err) (test parallelisation): ",maximum(rhsc_err)) + end + end if impose_zero_gradient_BC enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) # invert mass matrix and fill fc @@ -1208,7 +1278,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) @serial_region begin @. C_M_err = abs(C_M_num - C_M_exact) - println("finish C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) println("maximum(C_M_err): ",maximum(C_M_err)) plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index fe0727732..f9e8cec45 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -19,6 +19,7 @@ export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian export H_Maxwellian, G_Maxwellian export boundary_integration_weights_struct, fokkerplanck_boundary_data_arrays_struct export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions +export get_element_limit_indices # testing export symmetric_matrix_inverse diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index c17875260..e68baaab1 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -1418,8 +1418,12 @@ construction function for nonlinear diffusion matrices, only used in the assembly of the collision operator """ -function get_QQ_local!(QQ::Array{mk_float,3},ielement, - lobatto::gausslegendre_base_info, +function get_QQ_local!(QQ::Union{Array{mk_float,3}, + SubArray{Float64, 3, Array{Float64, 4}, + Tuple{Base.Slice{Base.OneTo{Int64}}, + Base.Slice{Base.OneTo{Int64}}, + Base.Slice{Base.OneTo{Int64}}, Int64}, true}}, + ielement,lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, coord,option) From 228e6086e916deb40c816001bc7e5b5818578887 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 19 Oct 2023 13:04:35 +0000 Subject: [PATCH 177/331] Added ability to switch from testing C[Fs,Fsp] to testing C[Fs,Fs]. Printout of moments of C are provided to indicate how well the operator conserves density (and u|| and p for C[Fs,Fs]). --- 2D_FEM_assembly_test.jl | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 4a8b95222..7aaab067e 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -16,6 +16,7 @@ using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new, boundary_integration_weights_struct using moment_kinetics.fokker_planck: get_element_limit_indices using moment_kinetics.calculus: derivative! +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.communication using moment_kinetics.communication: MPISharedArray using moment_kinetics.looping @@ -440,11 +441,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test plot_test_output = true impose_zero_gradient_BC = false#true - test_parallelism = true - ngrid = 3 #number of points per element - nelement_local_vpa = 16 # number of elements per rank + test_parallelism = false#true + test_self_operator = false#true + ngrid = 9 #number of points per element + nelement_local_vpa = 8 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 8 # number of elements per rank + nelement_local_vperp = 4 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units @@ -878,10 +880,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ dHdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - denss, upars, vths = 1.0, -1.0, 2.0/3.0 - dens = 1.0 - upar = 1.0 - vth = 1.0 + if test_self_operator + dens, upar, vth = 1.0, 1.0, 1.0 + denss, upars, vths = dens, upar, vth + else + denss, upars, vths = 1.0, -1.0, 2.0/3.0 + dens, upar, vth = 1.0, 1.0, 1.0 + end ms = 1.0 msp = 1.0 nussp = 1.0 @@ -1282,5 +1287,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("maximum(C_M_err): ",maximum(C_M_err)) plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) end - + if test_self_operator + delta_n = get_density(C_M_num, vpa, vperp) + delta_upar = get_upar(C_M_num, vpa, vperp, dens) + delta_ppar = get_ppar(C_M_num, vpa, vperp, upar, msp) + delta_pperp = get_pperp(C_M_num, vpa, vperp, msp) + delta_pressure = get_pressure(delta_ppar,delta_pperp) + @serial_region begin + println("delta_n: ", delta_n) + println("delta_upar: ", delta_upar) + println("delta_pressure: ", delta_pressure) + end + else + delta_n = get_density(C_M_num, vpa, vperp) + @serial_region begin + println("delta_n: ", delta_n) + end + end end From 843b94dd6fdf0a197c9fe76a1da6f07229202203 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 20 Oct 2023 08:27:06 +0000 Subject: [PATCH 178/331] Refactor calculation of mass and other matrices into functions. Only sparse matrix objects are used in the script to allow for a replacement of the dense arrays with a direct sparse matrix construction. --- 2D_FEM_assembly_test.jl | 500 +++++++++++++++++++++------------------- 1 file changed, 264 insertions(+), 236 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 7aaab067e..051d79a35 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -444,9 +444,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ test_parallelism = false#true test_self_operator = false#true ngrid = 9 #number of points per element - nelement_local_vpa = 8 # number of elements per rank + nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 4 # number of elements per rank + nelement_local_vperp = 8 # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements Lvpa = 12.0 #physical box size in reference units Lvperp = 6.0 #physical box size in reference units @@ -506,254 +506,282 @@ if abspath(PROGRAM_FILE) == @__FILE__ s=1, sn=1, r=1, z=1, vperp=vperp.n, vpa=vpa.n, vzeta=1, vr=1, vz=1) - begin_serial_region() - - - # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n - nc_local = vpa.ngrid*vperp.ngrid - Index2D = Array{mk_int,2}(undef,nc_global,nc_global) - MM2D = Array{mk_float,2}(undef,nc_global,nc_global) - MM2D .= 0.0 - MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) - MM2DZG .= 0.0 - KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) - KKpar2D .= 0.0 - KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - KKperp2D .= 0.0 - PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - PUperp2D .= 0.0 - PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - PPparPUperp2D .= 0.0 - PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) - PPpar2D .= 0.0 - MMparMNperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - MMparMNperp2D .= 0.0 - # Laplacian matrix - LP2D = Array{mk_float,2}(undef,nc_global,nc_global) - LP2D .= 0.0 - # Modified Laplacian matrix - LV2D = Array{mk_float,2}(undef,nc_global,nc_global) - LV2D .= 0.0 + begin_serial_region() - #print_matrix(MM2D,"MM2D",nc_global,nc_global) - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - impose_BC_at_zero_vperp = false - @serial_region begin - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") - get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") - get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") - get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") - get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") - #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) - #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) - - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") - #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) - #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) - #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) - - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) - #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) - #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) - #println("ic: ",ic_global," icp: ",icp_global) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - + function assemble_matrix_operators_dirichlet_bc(vpa,vperp) + nc_global = vpa.n*vperp.n + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + MM2D = Array{mk_float,2}(undef,nc_global,nc_global) + MM2D .= 0.0 + KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKpar2D .= 0.0 + KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKperp2D .= 0.0 + PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PUperp2D .= 0.0 + PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPparPUperp2D .= 0.0 + PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPpar2D .= 0.0 + MMparMNperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + MMparMNperp2D .= 0.0 + # Laplacian matrix + LP2D = Array{mk_float,2}(undef,nc_global,nc_global) + LP2D .= 0.0 + # Modified Laplacian matrix + LV2D = Array{mk_float,2}(undef,nc_global,nc_global) + LV2D .= 0.0 + + #print_matrix(MM2D,"MM2D",nc_global,nc_global) + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + impose_BC_at_zero_vperp = false + @serial_region begin + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) + #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) + + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) + #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) + #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) + + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp - if ivperpp_local == 1 && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp + if ivperpp_local == 1 && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + LLperp[ivperp_local,ivperpp_local]) + LV2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + MNperp[ivperp_local,ivperpp_local])) end - else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + + # assign K matrices + KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local] + # assign PU matrix + PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] + PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] + PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] - LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - LLperp[ivperp_local,ivperpp_local]) - LV2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* - MRperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - (KJperp[ivperp_local,ivperpp_local] - - PPperp[ivperp_local,ivperpp_local] - - MNperp[ivperp_local,ivperpp_local])) + # assign RHS mass matrix for d2Gdvperp2 + MMparMNperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MNperp[ivperp_local,ivperpp_local] end - - # assign K matrices - KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - KKperp[ivperp_local,ivperpp_local] - # assign PU matrix - PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local] - PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local] - PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - # assign RHS mass matrix for d2Gdvperp2 - MMparMNperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MNperp[ivperp_local,ivperpp_local] end end end end end + @serial_region begin + println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + + if nc_global < 30 + print_matrix(MM2D,"MM2D",nc_global,nc_global) + print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + print_matrix(LP2D,"LP",nc_global,nc_global) + print_matrix(LV2D,"LV",nc_global,nc_global) + end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + MM2D_sparse = sparse(MM2D) + KKpar2D_sparse = sparse(KKpar2D) + KKperp2D_sparse = sparse(KKperp2D) + LP2D_sparse = sparse(LP2D) + LV2D_sparse = sparse(LV2D) + PUperp2D_sparse = sparse(PUperp2D) + PPparPUperp2D_sparse = sparse(PPparPUperp2D) + PPpar2D_sparse = sparse(PPpar2D) + MMparMNperp2D_sparse = sparse(MMparMNperp2D) + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, + LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + PPpar2D_sparse, MMparMNperp2D_sparse end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) - #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) - #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) - #println("ic: ",ic_global," icp: ",icp_global) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - + + function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp) + nc_global = vpa.n*vperp.n + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) + MM2DZG .= 0.0 + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + @serial_region begin + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp - if ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp + if ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] end - else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] end end end end end end - end - @serial_region begin - println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - - if nc_global < 30 - print_matrix(MM2D,"MM2D",nc_global,nc_global) - print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) - print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - print_matrix(LP2D,"LP",nc_global,nc_global) - print_matrix(LV2D,"LV",nc_global,nc_global) + @serial_region begin + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if nc_global < 30 + print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) + end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end - # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + MM2DZG_sparse = sparse(MM2DZG) + return MM2DZG_sparse end - MM2D_sparse = sparse(MM2D) - MM2DZG_sparse = sparse(MM2DZG) - KKpar2D_sparse = sparse(KKpar2D) - KKperp2D_sparse = sparse(KKperp2D) - LP2D_sparse = sparse(LP2D) - LV2D_sparse = sparse(LV2D) + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp) @serial_region begin # create LU decomposition for mass matrix inversion println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -937,7 +965,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = -(4.0/sqrt(pi))*F_M ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MM2D,fc) + mul!(dfc,MM2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.H_data) fc = lu_obj_LP \ dfc @@ -952,7 +980,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = -(4.0/sqrt(pi))*F_M ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PPpar2D,fc) + mul!(dfc,PPpar2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvpa_data) fc = lu_obj_LP \ dfc @@ -967,7 +995,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = -(4.0/sqrt(pi))*F_M ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PUperp2D,fc) + mul!(dfc,PUperp2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvperp_data) fc = lu_obj_LV \ dfc @@ -982,7 +1010,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MM2D,fc) + mul!(dfc,MM2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.G_data) fc = lu_obj_LP \ dfc @@ -997,7 +1025,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,KKpar2D,fc) + mul!(dfc,KKpar2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvpa2_data) fc = lu_obj_LP \ dfc @@ -1012,7 +1040,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PUperp2D,fc) + mul!(dfc,PUperp2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dGdvperp_data) fc = lu_obj_LV \ dfc @@ -1027,7 +1055,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = 2.0*H_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PPparPUperp2D,fc) + mul!(dfc,PPparPUperp2D_sparse,fc) #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperpdvpa_data) fc = lu_obj_LV \ dfc @@ -1043,10 +1071,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. S_dummy = -dGdvperp_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MMparMNperp2D,fc) + mul!(dfc,MMparMNperp2D_sparse,fc) @. S_dummy = 2.0*H_M_num - d2Gdvpa2_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - mul!(dgc,MM2D,fc) + mul!(dgc,MM2D_sparse,fc) dfc += dgc #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) From 9aa049a7f08f58f65b79b4ff618ea3bcefc553d9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 20 Oct 2023 14:00:51 +0000 Subject: [PATCH 179/331] Added routine for initialising the mass (and other) matrices which creates the required sparse matrices without allocating dense arrays. --- 2D_FEM_assembly_test.jl | 383 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 370 insertions(+), 13 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 051d79a35..bb0a371f7 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -100,7 +100,62 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) return ivpa_global end - + + # function that returns the sparse matrix index + # used to directly construct the nonzero entries + # of a 2D assembled sparse matrix + function icsc_func(ivpa_local::mk_int,ivpap_local::mk_int, + ielement_vpa::mk_int, + ngrid_vpa::mk_int,nelement_vpa::mk_int, + ivperp_local::mk_int,ivperpp_local::mk_int, + ielement_vperp::mk_int, + ngrid_vperp::mk_int,nelement_vperp::mk_int) + ntot_vpa = (nelement_vpa - 1)*(ngrid_vpa^2 - 1) + ngrid_vpa^2 + ntot_vperp = (nelement_vperp - 1)*(ngrid_vperp^2 - 1) + ngrid_vperp^2 + + icsc_vpa = ((ivpap_local - 1) + (ivpa_local - 1)*ngrid_vpa + + (ielement_vpa - 1)*(ngrid_vpa^2 - 1)) + icsc_vperp = ((ivperpp_local - 1) + (ivperp_local - 1)*ngrid_vperp + + (ielement_vperp - 1)*(ngrid_vperp^2 - 1)) + icsc = 1 + icsc_vpa + ntot_vpa*icsc_vperp + return icsc + end + + struct sparse_matrix_constructor + # the Ith row + II::Array{mk_float,1} + # the Jth column + JJ::Array{mk_float,1} + # the data S[I,J] + SS::Array{mk_float,1} + end + + function allocate_sparse_matrix_constructor(nsparse) + II = Array{mk_int,1}(undef,nsparse) + @. II = 0 + JJ = Array{mk_int,1}(undef,nsparse) + @. JJ = 0 + SS = Array{mk_float,1}(undef,nsparse) + @. SS = 0.0 + return sparse_matrix_constructor(II,JJ,SS) + end + + function assign_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) + data.II[icsc] = ii + data.JJ[icsc] = jj + data.SS[icsc] = ss + return nothing + end + function assemble_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) + data.II[icsc] = ii + data.JJ[icsc] = jj + data.SS[icsc] += ss + return nothing + end + + function create_sparse_matrix(data::sparse_matrix_constructor) + return sparse(data.II,data.JJ,data.SS) + end struct vpa_vperp_boundary_data lower_boundary_vpa::MPISharedArray{mk_float,1} upper_boundary_vpa::MPISharedArray{mk_float,1} @@ -442,7 +497,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_test_output = true impose_zero_gradient_BC = false#true test_parallelism = false#true - test_self_operator = false#true + test_self_operator = true + test_dense_construction = false#true ngrid = 9 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements @@ -509,7 +565,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ nc_global = vpa.n*vperp.n begin_serial_region() - function assemble_matrix_operators_dirichlet_bc(vpa,vperp) + function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) nc_global = vpa.n*vperp.n # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n @@ -677,12 +733,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - if nc_global < 30 + if nc_global < 60 print_matrix(MM2D,"MM2D",nc_global,nc_global) - print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - print_matrix(LP2D,"LP",nc_global,nc_global) - print_matrix(LV2D,"LV",nc_global,nc_global) + #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + #print_matrix(LP2D,"LP",nc_global,nc_global) + #print_matrix(LV2D,"LV",nc_global,nc_global) end # convert these matrices to sparse matrices println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) @@ -701,7 +757,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ PPpar2D_sparse, MMparMNperp2D_sparse end - function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp) + function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) nc_global = vpa.n*vperp.n # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n @@ -778,10 +834,311 @@ if abspath(PROGRAM_FILE) == @__FILE__ return MM2DZG_sparse end - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, - PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp) + function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 + ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 + nsparse = ntot_vpa*ntot_vperp + ngrid_vpa = vpa.ngrid + nelement_vpa = vpa.nelement_local + ngrid_vperp = vperp.ngrid + nelement_vperp = vperp.nelement_local + + MM2D = allocate_sparse_matrix_constructor(nsparse) + KKpar2D = allocate_sparse_matrix_constructor(nsparse) + KKperp2D = allocate_sparse_matrix_constructor(nsparse) + PUperp2D = allocate_sparse_matrix_constructor(nsparse) + PPparPUperp2D = allocate_sparse_matrix_constructor(nsparse) + PPpar2D = allocate_sparse_matrix_constructor(nsparse) + MMparMNperp2D = allocate_sparse_matrix_constructor(nsparse) + # Laplacian matrix + LP2D = allocate_sparse_matrix_constructor(nsparse) + # Modified Laplacian matrix + LV2D = allocate_sparse_matrix_constructor(nsparse) + + # local dummy arrays + MMpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + MMperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MNperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + KJperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PPperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PUperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PPpar = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + + impose_BC_at_zero_vperp = false + @serial_region begin + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:nelement_vperp + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) + #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) + + for ielement_vpa in 1:nelement_vpa + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) + #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) + #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) + + for ivperpp_local in 1:ngrid_vperp + for ivperp_local in 1:ngrid_vperp + for ivpap_local in 1:ngrid_vpa + for ivpa_local in 1:ngrid_vpa + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, + ngrid_vpa,nelement_vpa, + ivperp_local,ivperpp_local, + ielement_vperp, + ngrid_vperp,nelement_vperp) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp + if ivperpp_local == 1 && ivpa_local == ivpap_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + else + # assign mass matrix data + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + LLperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(LV2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + MNperp[ivperp_local,ivperpp_local]))) + end + + # assign K matrices + assemble_constructor_data!(KKpar2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(KKperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local])) + # assign PU matrix + assemble_constructor_data!(PUperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(PPparPUperp2D,icsc,ic_global,icp_global, + (PPpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(PPpar2D,icsc,ic_global,icp_global, + (PPpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + # assign RHS mass matrix for d2Gdvperp2 + assemble_constructor_data!(MMparMNperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MNperp[ivperp_local,ivperpp_local])) + end + end + end + end + end + end + MM2D_sparse = create_sparse_matrix(MM2D) + KKpar2D_sparse = create_sparse_matrix(KKpar2D) + KKperp2D_sparse = create_sparse_matrix(KKperp2D) + LP2D_sparse = create_sparse_matrix(LP2D) + LV2D_sparse = create_sparse_matrix(LV2D) + PUperp2D_sparse = create_sparse_matrix(PUperp2D) + PPparPUperp2D_sparse = create_sparse_matrix(PPparPUperp2D) + PPpar2D_sparse = create_sparse_matrix(PPpar2D) + MMparMNperp2D_sparse = create_sparse_matrix(MMparMNperp2D) + @serial_region begin + println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) + + if nc_global < 60 + println("MM2D_sparse \n",MM2D_sparse) + print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) + # print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + # print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + # print_matrix(LP2D,"LP",nc_global,nc_global) + # print_matrix(LV2D,"LV",nc_global,nc_global) + end + # convert these matrices to sparse matrices + #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, + LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + PPpar2D_sparse, MMparMNperp2D_sparse + end + + function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 + ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 + nsparse = ntot_vpa*ntot_vperp + ngrid_vpa = vpa.ngrid + nelement_vpa = vpa.nelement_local + ngrid_vperp = vperp.ngrid + nelement_vperp = vperp.nelement_local + + MM2DZG = allocate_sparse_matrix_constructor(nsparse) + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + @serial_region begin + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, + ngrid_vpa,nelement_vpa, + ivperp_local,ivperpp_local, + ielement_vperp, + ngrid_vperp,nelement_vperp) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp + if ivpa_local == ivpap_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,vperp_spectral.radau.D0[ivperpp_local]) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + else + # assign mass matrix data + assemble_constructor_data!(MM2DZG,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + end + end + end + end + end + end + end + @serial_region begin + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + #if nc_global < 30 + # print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) + #end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + MM2DZG_sparse = create_sparse_matrix(MM2DZG) + return MM2DZG_sparse + end + + if test_dense_construction + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) + else + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + end + #MM2D_sparse @serial_region begin # create LU decomposition for mass matrix inversion println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) From aaed80892ea5e1c777b6bfb01da19d6df52ea5e1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 20 Oct 2023 17:27:51 +0000 Subject: [PATCH 180/331] Refactor order of operations and definitions to make it easer to estimate the length of time per collision operator evaluation. --- 2D_FEM_assembly_test.jl | 388 +++++++++++++++++++++------------------- 1 file changed, 206 insertions(+), 182 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index bb0a371f7..c8de5266c 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -84,7 +84,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] end end - return nothing + return nothing + end + + function ravel_vpavperp_to_c_parallel!(pdf_c,pdf_vpavperp,nvpa) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,nvpa) + pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] + end + return nothing end function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc,nvpa) @@ -93,7 +102,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ ivperp = ivperp_func(ic,nvpa) pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] end - return nothing + return nothing + end + + function ravel_c_to_vpavperp_parallel!(pdf_vpavperp,pdf_c,nvpa) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,nvpa) + pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] + end + return nothing end function ivpa_global_func(ivpa_local,ielement_vpa,ngrid_vpa) @@ -494,12 +512,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test - plot_test_output = true + plot_test_output = false#true impose_zero_gradient_BC = false#true test_parallelism = false#true test_self_operator = true test_dense_construction = false#true - ngrid = 9 #number of points per element + ngrid = 3 #number of points per element nelement_local_vpa = 16 # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = 8 # number of elements per rank @@ -1127,6 +1145,175 @@ if abspath(PROGRAM_FILE) == @__FILE__ return MM2DZG_sparse end + struct YY_collision_operator_arrays + # let phi_j(vperp) be the jth Lagrange basis function, + # and phi'_j(vperp) the first derivative of the Lagrange basis function + # on the iel^th element. Then, the arrays are defined as follows. + # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp + YY0perp::Array{mk_float,4} + # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp + YY1perp::Array{mk_float,4} + # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp + YY2perp::Array{mk_float,4} + # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp + YY3perp::Array{mk_float,4} + # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa + YY0par::Array{mk_float,4} + # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa + YY1par::Array{mk_float,4} + # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa + YY2par::Array{mk_float,4} + # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa + YY3par::Array{mk_float,4} + end + + function calculate_YY_arrays(vpa,vperp) + YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY3perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY0par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY1par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY2par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY3par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + + for ielement_vperp in 1:vperp.nelement_local + @views get_QQ_local!(YY0perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") + @views get_QQ_local!(YY1perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") + @views get_QQ_local!(YY2perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") + @views get_QQ_local!(YY3perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") + end + for ielement_vpa in 1:vpa.nelement_local + @views get_QQ_local!(YY0par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") + @views get_QQ_local!(YY1par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") + @views get_QQ_local!(YY2par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") + @views get_QQ_local!(YY3par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + end + + return YY_collision_operator_arrays(YY0perp,YY1perp,YY2perp,YY3perp, + YY0par,YY1par,YY2par,YY3par) + end + + function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral, + YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + @. rhsc = 0.0 + + # loop over elements + for ielement_vperp in 1:vperp.nelement_local + YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in 1:vpa.nelement_local + YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # loop over field positions in each element + for ivperp_local in 1:vperp.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + end + # correct for minus sign due to integration by parts + # and multiply by the normalised collision frequency + @. rhsc *= -nussp + return nothing + end + + function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,vpa_spectral,vperp_spectral, + YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + rhsvpavperp[ivpa,ivperp] = 0.0 + end + # loop over collocation points to benefit from shared-memory parallelism + ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid + @loop_vperp_vpa ivperp_global ivpa_global begin + igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) + # loop over elements belonging to this collocation point + for ielement_vperp in ielement_vperp_low:ielement_vperp_hi + # correct local ivperp in the case that we on a boundary point + ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) + @views YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + @views YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + @views YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + @views YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in ielement_vpa_low:ielement_vpa_hi + # correct local ivpa in the case that we on a boundary point + ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) + @views YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + @views YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + @views YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + @views YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + # ravel to compound index + #begin_serial_region() + #ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) + ravel_vpavperp_to_c_parallel!(rhsc,rhsvpavperp,vpa.n) + return nothing + end + if test_dense_construction MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, @@ -1304,6 +1491,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ # initialise the weights #fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) + # initialise any arrays needed later for the collision operator + rhsc = MPISharedArray{mk_float,1}(undef,nc_global) + rhsc_check = Array{mk_float,1}(undef,nc_global) + rhsc_err = Array{mk_float,1}(undef,nc_global) + rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + @serial_region begin + println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + YY_arrays = calculate_YY_arrays(vpa,vperp) + begin_serial_region() # do the numerical integration at the boundaries (N.B. G not supported) @serial_region begin @@ -1454,183 +1651,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end - rhsc = Array{mk_float,1}(undef,nc_global) - rhsc_check = Array{mk_float,1}(undef,nc_global) - rhsc_err = Array{mk_float,1}(undef,nc_global) - rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - - struct YY_collision_operator_arrays - # let phi_j(vperp) be the jth Lagrange basis function, - # and phi'_j(vperp) the first derivative of the Lagrange basis function - # on the iel^th element. Then, the arrays are defined as follows. - # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp - YY0perp::Array{mk_float,4} - # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp - YY1perp::Array{mk_float,4} - # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp - YY2perp::Array{mk_float,4} - # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp - YY3perp::Array{mk_float,4} - # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa - YY0par::Array{mk_float,4} - # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa - YY1par::Array{mk_float,4} - # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa - YY2par::Array{mk_float,4} - # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa - YY3par::Array{mk_float,4} - end - - function calculate_YY_arrays(vpa,vperp) - YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY3perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY0par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY1par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY2par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY3par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - - for ielement_vperp in 1:vperp.nelement_local - @views get_QQ_local!(YY0perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") - @views get_QQ_local!(YY1perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") - @views get_QQ_local!(YY2perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") - @views get_QQ_local!(YY3perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") - end - for ielement_vpa in 1:vpa.nelement_local - @views get_QQ_local!(YY0par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") - @views get_QQ_local!(YY1par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") - @views get_QQ_local!(YY2par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") - @views get_QQ_local!(YY3par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") - end - - return YY_collision_operator_arrays(YY0perp,YY1perp,YY2perp,YY3perp, - YY0par,YY1par,YY2par,YY3par) - end - - function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral, - YY_arrays::YY_collision_operator_arrays) - # assemble RHS of collision operator - @. rhsc = 0.0 - - # loop over elements - for ielement_vperp in 1:vperp.nelement_local - YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] - YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] - YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] - YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - - for ielement_vpa in 1:vpa.nelement_local - YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] - YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] - YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] - YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] - - # loop over field positions in each element - for ivperp_local in 1:vperp.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - # carry out the matrix sum on each 2D element - for jvperpp_local in 1:vperp.ngrid - jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] - for kvperpp_local in 1:vperp.ngrid - kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] - for jvpap_local in 1:vpa.ngrid - jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] - pdfjj = pdfs[jvpap,jvperpp] - for kvpap_local in 1:vpa.ngrid - kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - # first three lines represent parallel flux terms - # second three lines represent perpendicular flux terms - rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + - YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - - 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + - # end parallel flux, start of perpendicular flux - YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + - YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - - 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) - end - end - end - end - end - end - end - end - # correct for minus sign due to integration by parts - # and multiply by the normalised collision frequency - @. rhsc *= -nussp - return nothing - end - - function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral, - YY_arrays::YY_collision_operator_arrays) - # assemble RHS of collision operator - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - rhsvpavperp[ivpa,ivperp] = 0.0 - end - # loop over collocation points to benefit from shared-memory parallelism - ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid - @loop_vperp_vpa ivperp_global ivpa_global begin - igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) - # loop over elements belonging to this collocation point - for ielement_vperp in ielement_vperp_low:ielement_vperp_hi - # correct local ivperp in the case that we on a boundary point - ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) - YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] - YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] - YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] - YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - - for ielement_vpa in ielement_vpa_low:ielement_vpa_hi - # correct local ivpa in the case that we on a boundary point - ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) - YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] - YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] - YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] - YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] - - # carry out the matrix sum on each 2D element - for jvperpp_local in 1:vperp.ngrid - jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] - for kvperpp_local in 1:vperp.ngrid - kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] - for jvpap_local in 1:vpa.ngrid - jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] - pdfjj = pdfs[jvpap,jvperpp] - for kvpap_local in 1:vpa.ngrid - kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - # first three lines represent parallel flux terms - # second three lines represent perpendicular flux terms - rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + - YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - - 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + - # end parallel flux, start of perpendicular flux - YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + - YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - - 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) - end - end - end - end - end - end - end - # ravel to compound index - begin_serial_region() - ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) - return nothing - end - - @serial_region begin - println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - YY_arrays = calculate_YY_arrays(vpa,vperp) @serial_region begin println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -1647,6 +1667,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) + # switch back to serial region before matrix inverse + begin_serial_region() @serial_region begin println("finished C RHS assembly (parallel) ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -1665,7 +1687,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ # invert mass matrix and fill fc fc = lu_obj_MM \ rhsc end - ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) + #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) + ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) + begin_serial_region() @serial_region begin @. C_M_err = abs(C_M_num - C_M_exact) println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) From dd81b704f2eae52f9ab8545275040ea056047a3b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 21 Oct 2023 11:16:40 +0000 Subject: [PATCH 181/331] Add an elliptic solver function to permit easy parallelisation of assignment operations used in the elliptic solver steps. --- 2D_FEM_assembly_test.jl | 84 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index c8de5266c..61dd16cb3 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -28,7 +28,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ Pkg.activate(".") - function print_matrix(matrix,name,n,m) + function print_matrix(matrix,name::String,n::mk_int,m::mk_int) println("\n ",name," \n") for i in 1:n for j in 1:m @@ -39,7 +39,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("\n") end - function print_vector(vector,name,m) + function print_vector(vector,name::String,m::mk_int) println("\n ",name," \n") for j in 1:m @printf("%.3f ", vector[j]) @@ -66,18 +66,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ # Array in compound 1D form - function ic_func(ivpa,ivperp,nvpa) + function ic_func(ivpa::mk_int,ivperp::mk_int,nvpa::mk_int) return ivpa + nvpa*(ivperp-1) end - function ivperp_func(ic,nvpa) + function ivperp_func(ic::mk_int,nvpa::mk_int) return floor(Int64,(ic-1)/nvpa) + 1 end - function ivpa_func(ic,nvpa) + function ivpa_func(ic::mk_int,nvpa::mk_int) ivpa = ic - nvpa*(ivperp_func(ic,nvpa) - 1) return ivpa end - function ravel_vpavperp_to_c!(pdf_c,pdf_vpavperp,nvpa,nvperp) + function ravel_vpavperp_to_c!(pdf_c,pdf_vpavperp,nvpa::mk_int,nvperp::mk_int) for ivperp in 1:nvperp for ivpa in 1:nvpa ic = ic_func(ivpa,ivperp,nvpa) @@ -87,7 +87,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end - function ravel_vpavperp_to_c_parallel!(pdf_c,pdf_vpavperp,nvpa) + function ravel_vpavperp_to_c_parallel!(pdf_c,pdf_vpavperp,nvpa::mk_int) begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin ic = ic_func(ivpa,ivperp,nvpa) @@ -96,7 +96,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end - function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc,nvpa) + function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc::mk_int,nvpa::mk_int) for ic in 1:nc ivpa = ivpa_func(ic,nvpa) ivperp = ivperp_func(ic,nvpa) @@ -105,7 +105,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end - function ravel_c_to_vpavperp_parallel!(pdf_vpavperp,pdf_c,nvpa) + function ravel_c_to_vpavperp_parallel!(pdf_vpavperp,pdf_c,nvpa::mk_int) begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin ic = ic_func(ivpa,ivperp,nvpa) @@ -114,7 +114,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end - function ivpa_global_func(ivpa_local,ielement_vpa,ngrid_vpa) + function ivpa_global_func(ivpa_local::mk_int,ielement_vpa::mk_int,ngrid_vpa::mk_int) ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) return ivpa_global end @@ -148,7 +148,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ SS::Array{mk_float,1} end - function allocate_sparse_matrix_constructor(nsparse) + function allocate_sparse_matrix_constructor(nsparse::mk_int) II = Array{mk_int,1}(undef,nsparse) @. II = 0 JJ = Array{mk_int,1}(undef,nsparse) @@ -1418,38 +1418,38 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - S_dummy = Array{mk_float,2}(undef,vpa.n,vperp.n) + S_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - C_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + #dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + #dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + #d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - H_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - G_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvpa2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperp2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperp2_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperp2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dGdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperpdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvpa_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvperp_M_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) if test_self_operator @@ -1493,6 +1493,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) # initialise any arrays needed later for the collision operator rhsc = MPISharedArray{mk_float,1}(undef,nc_global) + sc = MPISharedArray{mk_float,1}(undef,nc_global) rhsc_check = Array{mk_float,1}(undef,nc_global) rhsc_err = Array{mk_float,1}(undef,nc_global) rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -1516,14 +1517,33 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - @. S_dummy = -(4.0/sqrt(pi))*F_M - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MM2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,H_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.H_data) - fc = lu_obj_LP \ dfc - ravel_c_to_vpavperp!(H_M_num,fc,nc_global,vpa.n) + + function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc,matrix_rhs,sc) + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) + # solve the linear system + sc = lu_object_lhs \ rhsc + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) + return nothing + end + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*F_M[ivpa,ivperp] + + end + elliptic_solve!(H_M_num,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + begin_serial_region() @serial_region begin @. H_M_err = abs(H_M_num - H_M_exact) println("finished H calculation ", Dates.format(now(), dateformat"H:MM:SS")) From c393dd5d7be8043a08a6e3b4466c6074e2d790f1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 21 Oct 2023 12:00:56 +0000 Subject: [PATCH 182/331] Refactor to reduce repeated code blocks. --- 2D_FEM_assembly_test.jl | 169 ++++++++++++---------------------------- 1 file changed, 51 insertions(+), 118 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 61dd16cb3..eba82acad 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -64,6 +64,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + function print_test_data(func_exact,func_num,func_err,func_name) + @. func_err = abs(func_num - func_exact) + println("maximum("*func_name*"_err): ",maximum(func_err)) + return nothing + end # Array in compound 1D form function ic_func(ivpa::mk_int,ivperp::mk_int,nvpa::mk_int) @@ -1511,11 +1516,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - # test the boundary data calculation - test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) #rpbd = rpbd_exact @serial_region begin - println("begin H calculation ", Dates.format(now(), dateformat"H:MM:SS")) + println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, @@ -1543,105 +1546,26 @@ if abspath(PROGRAM_FILE) == @__FILE__ end elliptic_solve!(H_M_num,S_dummy,rpbd.H_data, lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - begin_serial_region() - @serial_region begin - @. H_M_err = abs(H_M_num - H_M_exact) - println("finished H calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(H_M_err): ",maximum(H_M_err)) - - println("begin dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = -(4.0/sqrt(pi))*F_M - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PPpar2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvpa_data) - fc = lu_obj_LP \ dfc - ravel_c_to_vpavperp!(dHdvpa_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. dHdvpa_M_err = abs(dHdvpa_M_num - dHdvpa_M_exact) - println("finished dHdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dHdvpa_M_err): ",maximum(dHdvpa_M_err)) - - println("begin dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = -(4.0/sqrt(pi))*F_M - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PUperp2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,dHdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dHdvperp_data) - fc = lu_obj_LV \ dfc - ravel_c_to_vpavperp!(dHdvperp_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. dHdvperp_M_err = abs(dHdvperp_M_num - dHdvperp_M_exact) - println("finished dHdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dHdvperp_M_err): ",maximum(dHdvperp_M_err)) - - println("begin G calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MM2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,G_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.G_data) - fc = lu_obj_LP \ dfc - ravel_c_to_vpavperp!(G_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. G_M_err = abs(G_M_num - G_M_exact) - println("finished G calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(G_M_err): ",maximum(G_M_err)) - - println("begin d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,KKpar2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvpa2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvpa2_data) - fc = lu_obj_LP \ dfc - ravel_c_to_vpavperp!(d2Gdvpa2_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. d2Gdvpa2_M_err = abs(d2Gdvpa2_M_num - d2Gdvpa2_M_exact) - println("finished d2Gdvpa2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvpa2_M_err): ",maximum(d2Gdvpa2_M_err)) - - println("begin dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PUperp2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,dGdvperp_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.dGdvperp_data) - fc = lu_obj_LV \ dfc - ravel_c_to_vpavperp!(dGdvperp_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. dGdvperp_M_err = abs(dGdvperp_M_num - dGdvperp_M_exact) - println("finished dGdvperp calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(dGdvperp_M_err): ",maximum(dGdvperp_M_err)) - - println("begin d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - @. S_dummy = 2.0*H_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,PPparPUperp2D_sparse,fc) - #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperpdvpa_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperpdvpa_data) - fc = lu_obj_LV \ dfc - ravel_c_to_vpavperp!(d2Gdvperpdvpa_M_num,fc,nc_global,vpa.n) - @serial_region begin - @. d2Gdvperpdvpa_M_err = abs(d2Gdvperpdvpa_M_num - d2Gdvperpdvpa_M_exact) - println("finished d2Gdvperpdvpa calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvperpdvpa_M_err): ",maximum(d2Gdvperpdvpa_M_err)) - - # use relation 2H = del2 G to compute d2Gdpverp2 - println("begin d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) + elliptic_solve!(dHdvpa_M_num,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp_M_num,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] + end + elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2_M_num,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp_M_num,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa_M_num,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_serial_region() @. S_dummy = -dGdvperp_M_num ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) @@ -1655,20 +1579,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ fc = lu_obj_MM \ dfc ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) @serial_region begin - @. d2Gdvperp2_M_err = abs(d2Gdvperp2_M_num - d2Gdvperp2_M_exact) - println("finished d2Gdvperp2 calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(d2Gdvperp2_M_err): ",maximum(d2Gdvperp2_M_err)) - - if plot_test_output - plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - end + println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end @serial_region begin @@ -1711,10 +1622,32 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) begin_serial_region() @serial_region begin - @. C_M_err = abs(C_M_num - C_M_exact) println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) - println("maximum(C_M_err): ",maximum(C_M_err)) - plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + + # test the boundary data calculation + test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) + + print_test_data(H_M_exact,H_M_num,H_M_err,"H_M") + print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M") + print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M") + print_test_data(G_M_exact,G_M_num,G_M_err,"G_M") + print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M") + print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M") + print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M") + print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M") + print_test_data(C_M_exact,C_M_num,C_M_err,"C_M") + + if plot_test_output + plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + end end if test_self_operator delta_n = get_density(C_M_num, vpa, vperp) From 615a76dda4552fca615c4ebdbe16a84501154b2e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 21 Oct 2023 17:36:40 +0000 Subject: [PATCH 183/331] Provide an L2 norm of the errors in a version of the print_test_data function. Refactor algebraic solve for d2Gdvperp2 so that it uses a modified form of the elliptic_solve function. --- 2D_FEM_assembly_test.jl | 104 +++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index eba82acad..f7ac9dd24 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -66,8 +66,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ function print_test_data(func_exact,func_num,func_err,func_name) @. func_err = abs(func_num - func_exact) - println("maximum("*func_name*"_err): ",maximum(func_err)) - return nothing + max_err = maximum(func_err) + println("maximum("*func_name*"_err): ",max_err) + return max_err + end + + function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp) + @. func_err = abs(func_num - func_exact) + max_err = maximum(func_err) + @. func_err = func_err^2 + L2norm = get_density(func_err,vpa,vperp) + println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) + return max_err, L2norm end # Array in compound 1D form @@ -517,7 +527,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test - plot_test_output = false#true + plot_test_output = true impose_zero_gradient_BC = false#true test_parallelism = false#true test_self_operator = true @@ -1424,6 +1434,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test S_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + Q_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -1498,7 +1509,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) # initialise any arrays needed later for the collision operator rhsc = MPISharedArray{mk_float,1}(undef,nc_global) + rhqc = MPISharedArray{mk_float,1}(undef,nc_global) sc = MPISharedArray{mk_float,1}(undef,nc_global) + qc = MPISharedArray{mk_float,1}(undef,nc_global) rhsc_check = Array{mk_float,1}(undef,nc_global) rhsc_err = Array{mk_float,1}(undef,nc_global) rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -1521,6 +1534,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end + # Elliptic solve function. + # field: the solution + # source: the source function on the RHS + # boundary data: the known values of field at infinity + # lu_object_lhs: the object for the differential operator that defines field + # matrix_rhs: the weak matrix acting on the source vector + # rhsc, sc: dummy arrays in the compound index (assumed MPISharedArray or SubArray type) + # vpa, vperp: coordinate structs function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) # get data into the compound index format @@ -1538,6 +1559,32 @@ if abspath(PROGRAM_FILE) == @__FILE__ ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) return nothing end + # same as above but source is made of two different terms + # with different weak matrices + function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) + ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) + + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc_1,matrix_rhs_1,sc_1) + mul!(rhsc_2,matrix_rhs_2,sc_2) + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,vpa.n) + rhsc_1[ic] += rhsc_2[ic] + end + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) + # solve the linear system + sc_1 = lu_object_lhs \ rhsc_1 + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) + return nothing + end begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -1565,19 +1612,30 @@ if abspath(PROGRAM_FILE) == @__FILE__ elliptic_solve!(d2Gdvperpdvpa_M_num,S_dummy,rpbd.d2Gdvperpdvpa_data, lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - begin_serial_region() - @. S_dummy = -dGdvperp_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - d2Gdvpa2_M_num[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp_M_num[ivpa,ivperp] + end + # use the elliptic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + elliptic_solve!(d2Gdvperp2_M_num,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + #begin_serial_region() + #@. S_dummy = -dGdvperp_M_num + #ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) #enforce_zero_bc!(fc,vpa,vperp) - mul!(dfc,MMparMNperp2D_sparse,fc) - @. S_dummy = 2.0*H_M_num - d2Gdvpa2_M_num - ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - mul!(dgc,MM2D_sparse,fc) - dfc += dgc + #mul!(dfc,MMparMNperp2D_sparse,fc) + #@. S_dummy = 2.0*H_M_num - d2Gdvpa2_M_num + #ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) + #mul!(dgc,MM2D_sparse,fc) + #dfc += dgc #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) - fc = lu_obj_MM \ dfc - ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) + #enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) + #fc = lu_obj_MM \ dfc + #ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) @serial_region begin println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -1627,15 +1685,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ # test the boundary data calculation test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) - print_test_data(H_M_exact,H_M_num,H_M_err,"H_M") - print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M") - print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M") - print_test_data(G_M_exact,G_M_num,G_M_err,"G_M") - print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M") - print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M") - print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M") - print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M") - print_test_data(C_M_exact,C_M_num,C_M_err,"C_M") + H_M_max_err, H_M_L2_err = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + dHdvpa_M_max_err, dHdvpa_M_L2_err = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + dHdvperp_M_max_err, dHdvperp_M_L2_err = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + G_M_max_err, G_M_L2_err = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + d2Gdvpa2_M_max_err, d2Gdvpa2_M_L2_err = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + dGdvperp_M_max_err, dGdvperp_M_L2_err = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + d2Gdvperpdvpa_M_max_err, d2Gdvperpdvpa_M_L2_err = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + d2Gdvperp2_M_max_err, d2Gdvperp2_M_L2_err = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + C_M_max_err, C_M_L2_err = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) if plot_test_output plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) From ef0481dd8e4647888c4a28836d4c014c7a8ae7ce Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 09:04:47 +0000 Subject: [PATCH 184/331] Added structs for passing around the resulting errors from the tests. --- 2D_FEM_assembly_test.jl | 70 +++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index f7ac9dd24..03f1c3655 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -75,10 +75,54 @@ if abspath(PROGRAM_FILE) == @__FILE__ @. func_err = abs(func_num - func_exact) max_err = maximum(func_err) @. func_err = func_err^2 + # compute the numerator L2norm = get_density(func_err,vpa,vperp) + # compute the denominator + @. func_err = 1.0 + L2norm /= get_density(func_err,vpa,vperp) println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) return max_err, L2norm end + + mutable struct error_data + max::mk_float + L2::mk_float + end + + mutable struct moments_error_data + delta_density::mk_float + delta_upar::mk_float + delta_pressure::mk_float + end + + struct fkpl_error_data + C_M::error_data + H_M::error_data + dHdvpa_M::error_data + dHdvperp_M::error_data + G_M::error_data + dGdvperp_M::error_data + d2Gdvpa2_M::error_data + d2Gdvperpdvpa_M::error_data + d2Gdvperp2_M::error_data + moments::moments_error_data + end + + function allocate_error_data() + C_M = error_data(0.0,0.0) + H_M = error_data(0.0,0.0) + dHdvpa_M = error_data(0.0,0.0) + dHdvperp_M = error_data(0.0,0.0) + G_M = error_data(0.0,0.0) + dGdvperp_M = error_data(0.0,0.0) + d2Gdvpa2_M = error_data(0.0,0.0) + d2Gdvperpdvpa_M = error_data(0.0,0.0) + d2Gdvperp2_M = error_data(0.0,0.0) + moments = moments_error_data(0.0,0.0,0.0) + return fkpl_error_data(C_M,H_M,dHdvpa_M,dHdvperp_M, + G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvpa2_M, + moments) + end # Array in compound 1D form function ic_func(ivpa::mk_int,ivperp::mk_int,nvpa::mk_int) @@ -527,7 +571,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ # define inputs needed for the test - plot_test_output = true + plot_test_output = false#true impose_zero_gradient_BC = false#true test_parallelism = false#true test_self_operator = true @@ -1679,21 +1723,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) begin_serial_region() + fkerr = allocate_error_data() + println(fkerr) @serial_region begin println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) # test the boundary data calculation test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) - H_M_max_err, H_M_L2_err = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - dHdvpa_M_max_err, dHdvpa_M_L2_err = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - dHdvperp_M_max_err, dHdvperp_M_L2_err = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - G_M_max_err, G_M_L2_err = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - d2Gdvpa2_M_max_err, d2Gdvpa2_M_L2_err = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - dGdvperp_M_max_err, dGdvperp_M_L2_err = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - d2Gdvperpdvpa_M_max_err, d2Gdvperpdvpa_M_L2_err = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - d2Gdvperp2_M_max_err, d2Gdvperp2_M_L2_err = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - C_M_max_err, C_M_L2_err = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + fkerr.G_M.max, fkerr.G_M.L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + fkerr.d2Gdvpa2_M.max, fkerr.d2Gdvpa2_M.L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + fkerr.dGdvperp_M.max, fkerr.dGdvperp_M.L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) if plot_test_output plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) @@ -1718,10 +1764,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("delta_upar: ", delta_upar) println("delta_pressure: ", delta_pressure) end + fkerr.moments.delta_density = delta_n + fkerr.moments.delta_upar = delta_upar + fkerr.moments.delta_pressure = delta_pressure else delta_n = get_density(C_M_num, vpa, vperp) @serial_region begin println("delta_n: ", delta_n) end + fkerr.moments.delta_density = delta_n end end From cea525e43c286d5b1ae40cf02be3b7e768ccd16d Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 09:11:40 +0000 Subject: [PATCH 185/331] Remembered sqrt() in L2 norm definition and reorder functions so that body of script is in one location. --- 2D_FEM_assembly_test.jl | 274 ++++++++++++++++++++-------------------- 1 file changed, 134 insertions(+), 140 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 03f1c3655..c8d665253 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -76,10 +76,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ max_err = maximum(func_err) @. func_err = func_err^2 # compute the numerator - L2norm = get_density(func_err,vpa,vperp) + num = get_density(func_err,vpa,vperp) # compute the denominator @. func_err = 1.0 - L2norm /= get_density(func_err,vpa,vperp) + denom = get_density(func_err,vpa,vperp) + L2norm = sqrt(num/denom) println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) return max_err, L2norm end @@ -569,79 +570,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end - - # define inputs needed for the test - plot_test_output = false#true - impose_zero_gradient_BC = false#true - test_parallelism = false#true - test_self_operator = true - test_dense_construction = false#true - ngrid = 3 #number of points per element - nelement_local_vpa = 16 # number of elements per rank - nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 8 # number of elements per rank - nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 12.0 #physical box size in reference units - Lvperp = 6.0 #physical box size in reference units - bc = "" #not required to take a particular value, not used - # fd_option and adv_input not actually used so given values unimportant - #discretization = "chebyshev_pseudospectral" - discretization = "gausslegendre_pseudospectral" - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, - nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, - nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) - # create the coordinate struct 'x' - println("made inputs") - println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) - println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa - # and create the plans for the forward and backward fast Chebyshev transforms - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid - #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) - elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - else - # create dummy Bool variable to return in place of the above struct - vpa_spectral = false - #vpa.duniform_dgrid .= 1.0 - end - - if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp - # and create the plans for the forward and backward fast Chebyshev transforms - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid - #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) - elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - else - # create dummy Bool variable to return in place of the above struct - vperp_spectral = false - #vperp.duniform_dgrid .= 1.0 - end - # Set up MPI - initialize_comms!() - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=1, sn=1, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - nc_global = vpa.n*vperp.n - begin_serial_region() - function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) nc_global = vpa.n*vperp.n # Assemble a 2D mass matrix in the global compound coordinate @@ -1373,6 +1301,135 @@ if abspath(PROGRAM_FILE) == @__FILE__ return nothing end + + # Elliptic solve function. + # field: the solution + # source: the source function on the RHS + # boundary data: the known values of field at infinity + # lu_object_lhs: the object for the differential operator that defines field + # matrix_rhs: the weak matrix acting on the source vector + # rhsc, sc: dummy arrays in the compound index (assumed MPISharedArray or SubArray type) + # vpa, vperp: coordinate structs + function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc,matrix_rhs,sc) + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) + # solve the linear system + sc = lu_object_lhs \ rhsc + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) + return nothing + end + # same as above but source is made of two different terms + # with different weak matrices + function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) + ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) + + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc_1,matrix_rhs_1,sc_1) + mul!(rhsc_2,matrix_rhs_2,sc_2) + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,vpa.n) + rhsc_1[ic] += rhsc_2[ic] + end + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) + # solve the linear system + sc_1 = lu_object_lhs \ rhsc_1 + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) + return nothing + end + + + + # define inputs needed for the test + plot_test_output = false#true + impose_zero_gradient_BC = false#true + test_parallelism = false#true + test_self_operator = true + test_dense_construction = false#true + ngrid = 3 #number of points per element + nelement_local_vpa = 16 # number of elements per rank + nelement_global_vpa = nelement_local_vpa # total number of elements + nelement_local_vperp = 8 # number of elements per rank + nelement_global_vperp = nelement_local_vperp # total number of elements + Lvpa = 12.0 #physical box size in reference units + Lvperp = 6.0 #physical box size in reference units + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + #discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) + # create the coordinate struct 'x' + println("made inputs") + println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) + println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa + # and create the plans for the forward and backward fast Chebyshev transforms + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid + #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) + elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + else + # create dummy Bool variable to return in place of the above struct + vpa_spectral = false + #vpa.duniform_dgrid .= 1.0 + end + + if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp + # and create the plans for the forward and backward fast Chebyshev transforms + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid + #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) + elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + else + # create dummy Bool variable to return in place of the above struct + vperp_spectral = false + #vperp.duniform_dgrid .= 1.0 + end + # Set up MPI + initialize_comms!() + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + nc_global = vpa.n*vperp.n + begin_serial_region() + + + if test_dense_construction MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, @@ -1578,58 +1635,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end - # Elliptic solve function. - # field: the solution - # source: the source function on the RHS - # boundary data: the known values of field at infinity - # lu_object_lhs: the object for the differential operator that defines field - # matrix_rhs: the weak matrix acting on the source vector - # rhsc, sc: dummy arrays in the compound index (assumed MPISharedArray or SubArray type) - # vpa, vperp: coordinate structs - function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, - lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) - # get data into the compound index format - begin_vperp_vpa_region() - ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) - # assemble the rhs of the weak system - begin_serial_region() - mul!(rhsc,matrix_rhs,sc) - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) - # solve the linear system - sc = lu_object_lhs \ rhsc - # get data into the vpa vperp indices format - begin_vperp_vpa_region() - ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) - return nothing - end - # same as above but source is made of two different terms - # with different weak matrices - function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, - lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) - # get data into the compound index format - begin_vperp_vpa_region() - ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) - ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) - - # assemble the rhs of the weak system - begin_serial_region() - mul!(rhsc_1,matrix_rhs_1,sc_1) - mul!(rhsc_2,matrix_rhs_2,sc_2) - @loop_vperp_vpa ivperp ivpa begin - ic = ic_func(ivpa,ivperp,vpa.n) - rhsc_1[ic] += rhsc_2[ic] - end - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) - # solve the linear system - sc_1 = lu_object_lhs \ rhsc_1 - # get data into the vpa vperp indices format - begin_vperp_vpa_region() - ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) - return nothing - end - begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*F_M[ivpa,ivperp] @@ -1667,19 +1672,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ elliptic_solve!(d2Gdvperp2_M_num,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, rhsc,rhqc,sc,qc,vpa,vperp) - #begin_serial_region() - #@. S_dummy = -dGdvperp_M_num - #ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #enforce_zero_bc!(fc,vpa,vperp) - #mul!(dfc,MMparMNperp2D_sparse,fc) - #@. S_dummy = 2.0*H_M_num - d2Gdvpa2_M_num - #ravel_vpavperp_to_c!(fc,S_dummy,vpa.n,vperp.n) - #mul!(dgc,MM2D_sparse,fc) - #dfc += dgc - #enforce_dirichlet_bc!(dfc,vpa,vperp,d2Gdvperp2_M_exact,dirichlet_vperp_BC=impose_BC_at_zero_vperp) - #enforce_dirichlet_bc!(dfc,vpa,vperp,rpbd.d2Gdvperp2_data) - #fc = lu_obj_MM \ dfc - #ravel_c_to_vpavperp!(d2Gdvperp2_M_num,fc,nc_global,vpa.n) @serial_region begin println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -1774,4 +1766,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ end fkerr.moments.delta_density = delta_n end + + return fkerr end From 3c6b7959961b49f5f9e4c74291aa5e3c9c25939f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 09:55:28 +0000 Subject: [PATCH 186/331] Refactor the script to produce plots of the maximum and L2 norm errors as a function of input nelement. --- 2D_FEM_assembly_test.jl | 958 +++++++++++++++++++++++----------------- 1 file changed, 557 insertions(+), 401 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index c8d665253..a6755e064 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -367,7 +367,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ end function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, - fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf) + fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral) # get derivatives of pdf dfdvperp = fkpl.dfdvperp dfdvpa = fkpl.dfdvpa @@ -1154,7 +1154,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ YY3par::Array{mk_float,4} end - function calculate_YY_arrays(vpa,vperp) + function calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) @@ -1183,8 +1183,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral, - YY_arrays::YY_collision_operator_arrays) + vpa,vperp,YY_arrays::YY_collision_operator_arrays) # assemble RHS of collision operator @. rhsc = 0.0 @@ -1240,8 +1239,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral, - YY_arrays::YY_collision_operator_arrays) + vpa,vperp,YY_arrays::YY_collision_operator_arrays) # assemble RHS of collision operator begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -1355,417 +1353,575 @@ if abspath(PROGRAM_FILE) == @__FILE__ end - - # define inputs needed for the test - plot_test_output = false#true - impose_zero_gradient_BC = false#true - test_parallelism = false#true - test_self_operator = true - test_dense_construction = false#true - ngrid = 3 #number of points per element - nelement_local_vpa = 16 # number of elements per rank - nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = 8 # number of elements per rank - nelement_global_vperp = nelement_local_vperp # total number of elements - Lvpa = 12.0 #physical box size in reference units - Lvperp = 6.0 #physical box size in reference units - bc = "" #not required to take a particular value, not used - # fd_option and adv_input not actually used so given values unimportant - #discretization = "chebyshev_pseudospectral" - discretization = "gausslegendre_pseudospectral" - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, - nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, - nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) - # create the coordinate struct 'x' - println("made inputs") - println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) - println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa - # and create the plans for the forward and backward fast Chebyshev transforms - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid - #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) - elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - else - # create dummy Bool variable to return in place of the above struct - vpa_spectral = false - #vpa.duniform_dgrid .= 1.0 - end + function test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp; + Lvpa=12.0,Lvperp=6.0,plot_test_output=false,impose_zero_gradient_BC=false, + test_parallelism=false,test_self_operator=true, + test_dense_construction=false,standalone=false) + # define inputs needed for the test + #plot_test_output = false#true + #impose_zero_gradient_BC = false#true + #test_parallelism = false#true + #test_self_operator = true + #test_dense_construction = false#true + #ngrid = 3 #number of points per element + nelement_local_vpa = nelement_vpa # number of elements per rank + nelement_global_vpa = nelement_local_vpa # total number of elements + nelement_local_vperp = nelement_vpa # number of elements per rank + nelement_global_vperp = nelement_local_vperp # total number of elements + #Lvpa = 12.0 #physical box size in reference units + #Lvperp = 6.0 #physical box size in reference units + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + #discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) + vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) + # create the coordinate struct 'x' + println("made inputs") + println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) + println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) + vpa = define_coordinate(vpa_input) + vperp = define_coordinate(vperp_input) + if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa + # and create the plans for the forward and backward fast Chebyshev transforms + vpa_spectral = setup_chebyshev_pseudospectral(vpa) + # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid + #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) + elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 + vpa_spectral = setup_gausslegendre_pseudospectral(vpa) + else + # create dummy Bool variable to return in place of the above struct + vpa_spectral = false + #vpa.duniform_dgrid .= 1.0 + end - if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp - # and create the plans for the forward and backward fast Chebyshev transforms - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid - #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) - elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - else - # create dummy Bool variable to return in place of the above struct - vperp_spectral = false - #vperp.duniform_dgrid .= 1.0 - end - # Set up MPI - initialize_comms!() - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=1, sn=1, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - nc_global = vpa.n*vperp.n - begin_serial_region() - - - - if test_dense_construction - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, - PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) - else - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, - PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - end - #MM2D_sparse - @serial_region begin - # create LU decomposition for mass matrix inversion - println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - end - lu_obj_MM = lu(MM2D_sparse) - lu_obj_MMZG = lu(MM2DZG_sparse) - lu_obj_LP = lu(LP2D_sparse) - lu_obj_LV = lu(LV2D_sparse) - #cholesky_obj = cholesky(MM2D_sparse) - @serial_region begin - println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - end - # define a test function - - fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) - fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) - fvpavperp_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) - fc = Array{mk_float,1}(undef,nc_global) - dfc = Array{mk_float,1}(undef,nc_global) - gc = Array{mk_float,1}(undef,nc_global) - dgc = Array{mk_float,1}(undef,nc_global) - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) - d2fvpavperp_dvpa2_exact[ivpa,ivperp] = (4.0*vpa.grid[ivpa]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) - d2fvpavperp_dvperp2_exact[ivpa,ivperp] = (4.0*vperp.grid[ivperp]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 + # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp + # and create the plans for the forward and backward fast Chebyshev transforms + vperp_spectral = setup_chebyshev_pseudospectral(vperp) + # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid + #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) + elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 + vperp_spectral = setup_gausslegendre_pseudospectral(vperp) + else + # create dummy Bool variable to return in place of the above struct + vperp_spectral = false + #vperp.duniform_dgrid .= 1.0 end - end - - # boundary conditions - - #fvpavperp[vpa.n,:] .= 0.0 - #fvpavperp[1,:] .= 0.0 - #fvpavperp[:,vperp.n] .= 0.0 - - - #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) - # fill fc with fvpavperp - ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) - ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) - @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) - @serial_region begin - println("max(ravel_err)",maximum(fvpavperp_err)) - end - #print_vector(fc,"fc",nc_global) - # multiply by KKpar2D and fill dfc - mul!(dfc,KKpar2D_sparse,fc) - mul!(dgc,KKperp2D_sparse,fc) - if impose_zero_gradient_BC - # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) - enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ dfc - gc = lu_obj_MMZG \ dgc - else - # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) - enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ dfc - gc = lu_obj_MMZG \ dgc - end - #fc = cholesky_obj \ dfc - #print_vector(fc,"fc",nc_global) - # unravel - ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) - ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) - @serial_region begin - if nc_global < 30 - print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) - end - @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) - println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) - @. d2fvpavperp_dvperp2_err = abs(d2fvpavperp_dvperp2_num - d2fvpavperp_dvperp2_exact) - println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) - if nc_global < 30 - print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + nc_global = vpa.n*vperp.n + begin_serial_region() + + + + if test_dense_construction + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) + else + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + end + #MM2D_sparse + @serial_region begin + # create LU decomposition for mass matrix inversion + println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end + lu_obj_MM = lu(MM2D_sparse) + lu_obj_MMZG = lu(MM2DZG_sparse) + lu_obj_LP = lu(LP2D_sparse) + lu_obj_LV = lu(LV2D_sparse) + #cholesky_obj = cholesky(MM2D_sparse) + @serial_region begin + println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end + # define a test function + + fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + fc = Array{mk_float,1}(undef,nc_global) + dfc = Array{mk_float,1}(undef,nc_global) + gc = Array{mk_float,1}(undef,nc_global) + dgc = Array{mk_float,1}(undef,nc_global) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvpa2_exact[ivpa,ivperp] = (4.0*vpa.grid[ivpa]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvperp2_exact[ivpa,ivperp] = (4.0*vperp.grid[ivperp]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + end + end + + # boundary conditions + + #fvpavperp[vpa.n,:] .= 0.0 + #fvpavperp[1,:] .= 0.0 + #fvpavperp[:,vperp.n] .= 0.0 + + + #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) + # fill fc with fvpavperp + ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) + ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) + @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) + @serial_region begin + println("max(ravel_err)",maximum(fvpavperp_err)) + end + #print_vector(fc,"fc",nc_global) + # multiply by KKpar2D and fill dfc + mul!(dfc,KKpar2D_sparse,fc) + mul!(dgc,KKperp2D_sparse,fc) + if impose_zero_gradient_BC + # enforce zero bc + enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) + enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ dfc + gc = lu_obj_MMZG \ dgc + else + # enforce zero bc + enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) + enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ dfc + gc = lu_obj_MMZG \ dgc + end + #fc = cholesky_obj \ dfc + #print_vector(fc,"fc",nc_global) + # unravel + ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) + ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) + @serial_region begin + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_num,"d2fvpavperp_dvpa2_num",vpa.n,vperp.n) + end + @. d2fvpavperp_dvpa2_err = abs(d2fvpavperp_dvpa2_num - d2fvpavperp_dvpa2_exact) + println("maximum(d2fvpavperp_dvpa2_err): ",maximum(d2fvpavperp_dvpa2_err)) + @. d2fvpavperp_dvperp2_err = abs(d2fvpavperp_dvperp2_num - d2fvpavperp_dvperp2_exact) + println("maximum(d2fvpavperp_dvperp2_err): ",maximum(d2fvpavperp_dvperp2_err)) + if nc_global < 30 + print_matrix(d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2_err",vpa.n,vperp.n) + end + if plot_test_output + plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) + plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) + end + end + # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test + + S_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + Q_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + C_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + #dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + #dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + #d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + H_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + G_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + + if test_self_operator + dens, upar, vth = 1.0, 1.0, 1.0 + denss, upars, vths = dens, upar, vth + else + denss, upars, vths = 1.0, -1.0, 2.0/3.0 + dens, upar, vth = 1.0, 1.0, 1.0 + end + ms = 1.0 + msp = 1.0 + nussp = 1.0 + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + dens,upar,vth,msp, + nussp,vpa,vperp,ivpa,ivperp) + end end - if plot_test_output - plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) - plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) + # calculate the Rosenbluth potential boundary data (rpbd) + rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) + rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) + # use known test function to provide exact data + calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, + H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, + dGdvperp_M_exact,d2Gdvperp2_M_exact, + d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) + # use numerical integration to find the boundary data + # initialise the weights + #fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) + # initialise any arrays needed later for the collision operator + rhsc = MPISharedArray{mk_float,1}(undef,nc_global) + rhqc = MPISharedArray{mk_float,1}(undef,nc_global) + sc = MPISharedArray{mk_float,1}(undef,nc_global) + qc = MPISharedArray{mk_float,1}(undef,nc_global) + rhsc_check = Array{mk_float,1}(undef,nc_global) + rhsc_err = Array{mk_float,1}(undef,nc_global) + rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + @serial_region begin + println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) + + begin_serial_region() + # do the numerical integration at the boundaries (N.B. G not supported) + @serial_region begin + println("begin boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M,vpa,vperp,vpa_spectral,vperp_spectral) + @serial_region begin + println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + #rpbd = rpbd_exact + @serial_region begin + println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) + end + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*F_M[ivpa,ivperp] + end + elliptic_solve!(H_M_num,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa_M_num,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp_M_num,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] + + end + elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2_M_num,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp_M_num,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa_M_num,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - d2Gdvpa2_M_num[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp_M_num[ivpa,ivperp] + end + # use the elliptic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + elliptic_solve!(d2Gdvperp2_M_num,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + @serial_region begin + println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) + end + + @serial_region begin + println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + if test_parallelism + assemble_explicit_collision_operator_rhs_serial!(rhsc_check,Fs_M, + d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, + dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, + vpa,vperp,YY_arrays) + @serial_region begin + println("finished C RHS assembly (serial) ", Dates.format(now(), dateformat"H:MM:SS")) + end + end + assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,Fs_M, + d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, + dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, + vpa,vperp,YY_arrays) + # switch back to serial region before matrix inverse + begin_serial_region() + @serial_region begin + println("finished C RHS assembly (parallel) ", Dates.format(now(), dateformat"H:MM:SS")) + end + if test_parallelism + @serial_region begin + @. rhsc_err = abs(rhsc - rhsc_check) + println("maximum(rhsc_err) (test parallelisation): ",maximum(rhsc_err)) + end + end + if impose_zero_gradient_BC + enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ rhsc + else + enforce_zero_bc!(rhsc,vpa,vperp) + # invert mass matrix and fill fc + fc = lu_obj_MM \ rhsc + end + #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) + ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) + begin_serial_region() + fkerr = allocate_error_data() + println(fkerr) + @serial_region begin + println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + + # test the boundary data calculation + test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) + + fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + fkerr.G_M.max, fkerr.G_M.L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + fkerr.d2Gdvpa2_M.max, fkerr.d2Gdvpa2_M.L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + fkerr.dGdvperp_M.max, fkerr.dGdvperp_M.L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + + if plot_test_output + plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) + plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) + plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) + plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) + plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) + plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) + plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) + plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) + end + end + if test_self_operator + delta_n = get_density(C_M_num, vpa, vperp) + delta_upar = get_upar(C_M_num, vpa, vperp, dens) + delta_ppar = get_ppar(C_M_num, vpa, vperp, upar, msp) + delta_pperp = get_pperp(C_M_num, vpa, vperp, msp) + delta_pressure = get_pressure(delta_ppar,delta_pperp) + @serial_region begin + println("delta_n: ", delta_n) + println("delta_upar: ", delta_upar) + println("delta_pressure: ", delta_pressure) + end + fkerr.moments.delta_density = delta_n + fkerr.moments.delta_upar = delta_upar + fkerr.moments.delta_pressure = delta_pressure + else + delta_n = get_density(C_M_num, vpa, vperp) + @serial_region begin + println("delta_n: ", delta_n) + end + fkerr.moments.delta_density = delta_n + end + if standalone + finalize_comms!() + end + return fkerr end - # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - - S_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - Q_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - C_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - #dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - #dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - #d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - H_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - G_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvpa2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperp2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperp2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperp2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dGdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperpdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - if test_self_operator - dens, upar, vth = 1.0, 1.0, 1.0 - denss, upars, vths = dens, upar, vth - else - denss, upars, vths = 1.0, -1.0, 2.0/3.0 - dens, upar, vth = 1.0, 1.0, 1.0 - end - ms = 1.0 - msp = 1.0 - nussp = 1.0 - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) - C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - dens,upar,vth,msp, - nussp,vpa,vperp,ivpa,ivperp) + function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) end - end - # calculate the Rosenbluth potential boundary data (rpbd) - rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) - rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) - # use known test function to provide exact data - calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, - H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, - dGdvperp_M_exact,d2Gdvperp2_M_exact, - d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) - # use numerical integration to find the boundary data - # initialise the weights - #fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) - fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) - # initialise any arrays needed later for the collision operator - rhsc = MPISharedArray{mk_float,1}(undef,nc_global) - rhqc = MPISharedArray{mk_float,1}(undef,nc_global) - sc = MPISharedArray{mk_float,1}(undef,nc_global) - qc = MPISharedArray{mk_float,1}(undef,nc_global) - rhsc_check = Array{mk_float,1}(undef,nc_global) - rhsc_err = Array{mk_float,1}(undef,nc_global) - rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - @serial_region begin - println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - YY_arrays = calculate_YY_arrays(vpa,vperp) - - begin_serial_region() - # do the numerical integration at the boundaries (N.B. G not supported) - @serial_region begin - println("begin boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M) - @serial_region begin - println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - #rpbd = rpbd_exact - @serial_region begin - println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) - end - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*F_M[ivpa,ivperp] - + + function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end end - elliptic_solve!(H_M_num,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa_M_num,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp_M_num,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] + initialize_comms!() + ngrid = 3 + plot_scan = true + plot_test_output = false + impose_zero_gradient_BC = false + test_parallelism = false + test_self_operator = true + test_dense_construction = false + nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4] + #nelement_list = Int[2, 4, 8] + #nelement_list = Int[100] + #nelement_list = Int[8] + nscan = size(nelement_list,1) + max_C_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_G_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + L2_C_err = Array{mk_float,1}(undef,nscan) + L2_H_err = Array{mk_float,1}(undef,nscan) + L2_G_err = Array{mk_float,1}(undef,nscan) + L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + #max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + #max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + n_err = Array{mk_float,1}(undef,nscan) + u_err = Array{mk_float,1}(undef,nscan) + p_err = Array{mk_float,1}(undef,nscan) - end - elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2_M_num,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp_M_num,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa_M_num,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - d2Gdvpa2_M_num[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp_M_num[ivpa,ivperp] - end - # use the elliptic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - elliptic_solve!(d2Gdvperp2_M_num,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - @serial_region begin - println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) - end + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" - @serial_region begin - println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - if test_parallelism - assemble_explicit_collision_operator_rhs_serial!(rhsc_check,Fs_M, - d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, - dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) - @serial_region begin - println("finished C RHS assembly (serial) ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,Fs_M, - d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, - dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, - vpa,vperp,vpa_spectral,vperp_spectral,YY_arrays) - # switch back to serial region before matrix inverse - begin_serial_region() - @serial_region begin - println("finished C RHS assembly (parallel) ", Dates.format(now(), dateformat"H:MM:SS")) - end - if test_parallelism - @serial_region begin - @. rhsc_err = abs(rhsc - rhsc_check) - println("maximum(rhsc_err) (test parallelisation): ",maximum(rhsc_err)) - end - end - if impose_zero_gradient_BC - enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ rhsc - else - enforce_zero_bc!(rhsc,vpa,vperp) - # invert mass matrix and fill fc - fc = lu_obj_MM \ rhsc - end - #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) - ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) - begin_serial_region() - fkerr = allocate_error_data() - println(fkerr) - @serial_region begin - println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) + for iscan in 1:nscan + local nelement = nelement_list[iscan] + nelement_vpa = 2*nelement + nelement_vperp = nelement + fkerr = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, + plot_test_output=plot_test_output, + impose_zero_gradient_BC=impose_zero_gradient_BC, + test_parallelism=test_parallelism, + test_self_operator=test_self_operator, + test_dense_construction=test_dense_construction, + standalone=false) + max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 + max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 + max_dHdvpa_err[iscan], L2_dHdvpa_err[iscan] = fkerr.dHdvpa_M.max ,fkerr.dHdvpa_M.L2 + max_dHdvperp_err[iscan], L2_dHdvperp_err[iscan] = fkerr.dHdvperp_M.max ,fkerr.dHdvperp_M.L2 + max_G_err[iscan], L2_G_err[iscan] = fkerr.G_M.max ,fkerr.G_M.L2 + max_dGdvperp_err[iscan], L2_dGdvperp_err[iscan] = fkerr.dGdvperp_M.max ,fkerr.dGdvperp_M.L2 + max_d2Gdvpa2_err[iscan], L2_d2Gdvpa2_err[iscan] = fkerr.d2Gdvpa2_M.max ,fkerr.d2Gdvpa2_M.L2 + max_d2Gdvperpdvpa_err[iscan], L2_d2Gdvperpdvpa_err[iscan] = fkerr.d2Gdvperpdvpa_M.max ,fkerr.d2Gdvperpdvpa_M.L2 + max_d2Gdvperp2_err[iscan], L2_d2Gdvperp2_err[iscan] = fkerr.d2Gdvperp2_M.max ,fkerr.d2Gdvperp2_M.L2 + n_err[iscan] = abs(fkerr.moments.delta_density) + u_err[iscan] = abs(fkerr.moments.delta_upar) + p_err[iscan] = abs(fkerr.moments.delta_pressure) + end + if global_rank[]==0 && plot_scan + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + Clabel = L"|C|_{\infty}" + dHdvpalabel = L"|dH/d v_{\|\|}|_{\infty}" + dHdvperplabel = L"|dH/d v_{\perp}|_{\infty}" + d2Gdvperp2label = L"|d^2G/d v_{\perp}^2|_{\infty}" + d2Gdvpa2label = L"|d^2G/d v_{\|\|}^2|_{\infty}" + d2Gdvperpdvpalabel = L"|d^2G/d v_{\perp} d v_{\|\|}|_{\infty}" + dGdvperplabel = L"|dG/d v_{\perp}|_{\infty}" - # test the boundary data calculation - test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) + plot(nelement_list, [max_C_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_numerical_lagrange_integration_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) - fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - fkerr.G_M.max, fkerr.G_M.L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - fkerr.d2Gdvpa2_M.max, fkerr.d2Gdvpa2_M.L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - fkerr.dGdvperp_M.max, fkerr.dGdvperp_M.L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + ClabelL2 = L"|C|_{L2}" + dHdvpalabelL2 = L"|dH/d v_{\|\|}|_{L2}" + dHdvperplabelL2 = L"|dH/d v_{\perp}|_{L2}" + d2Gdvperp2labelL2 = L"|d^2G/d v_{\perp}^2|_{L2}" + d2Gdvpa2labelL2 = L"|d^2G/d v_{\|\|}^2|_{L2}" + d2GdvperpdvpalabelL2 = L"|d^2G/d v_{\perp} d v_{\|\|}|_{L2}" + dGdvperplabelL2 = L"|dG/d v_{\perp}|_{L2}" - if plot_test_output - plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) - plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - plot_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - plot_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - plot_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - plot_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - plot_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - plot_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - plot_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - end - end - if test_self_operator - delta_n = get_density(C_M_num, vpa, vperp) - delta_upar = get_upar(C_M_num, vpa, vperp, dens) - delta_ppar = get_ppar(C_M_num, vpa, vperp, upar, msp) - delta_pperp = get_pperp(C_M_num, vpa, vperp, msp) - delta_pressure = get_pressure(delta_ppar,delta_pperp) - @serial_region begin - println("delta_n: ", delta_n) - println("delta_upar: ", delta_upar) - println("delta_pressure: ", delta_pressure) - end - fkerr.moments.delta_density = delta_n - fkerr.moments.delta_upar = delta_upar - fkerr.moments.delta_pressure = delta_pressure - else - delta_n = get_density(C_M_num, vpa, vperp) - @serial_region begin - println("delta_n: ", delta_n) + + plot(nelement_list, [L2_C_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[ClabelL2 dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_numerical_lagrange_integration_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + nlabel = L"|\Delta n|" + ulabel = L"|\Delta u_{\|\|}|" + plabel = L"|\Delta p|" + + if test_self_operator + plot(nelement_list, [max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel ClabelL2 nlabel ulabel plabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + else + plot(nelement_list, [max_C_err, L2_C_err, n_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel ClabelL2 nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) end - fkerr.moments.delta_density = delta_n end - - return fkerr + finalize_comms!() end From a25d39852ff3b6d47b3f134bceac638360a6bc22 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 10:31:04 +0000 Subject: [PATCH 187/331] Added facility for testing the timing data for the initialisation of the test arrays and weights, and the time to carry out the collision operator evaluation. --- 2D_FEM_assembly_test.jl | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index a6755e064..b9eabb65a 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -1430,7 +1430,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ vzeta=1, vr=1, vz=1) nc_global = vpa.n*vperp.n begin_serial_region() - + start_init_time = now() if test_dense_construction @@ -1624,6 +1624,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) + finish_init_time = now() + begin_serial_region() # do the numerical integration at the boundaries (N.B. G not supported) @serial_region begin @@ -1717,6 +1719,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) + init_time = Dates.value(finish_init_time - start_init_time) + calculate_time = Dates.value(now() - finish_init_time) begin_serial_region() fkerr = allocate_error_data() println(fkerr) @@ -1772,7 +1776,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ if standalone finalize_comms!() end - return fkerr + return fkerr, calculate_time, init_time end function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) @@ -1789,17 +1793,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ initialize_comms!() - ngrid = 3 + ngrid = 9 plot_scan = true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false test_self_operator = true test_dense_construction = false - nelement_list = Int[8, 16, 32, 64, 128] - #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[8, 16, 32, 64] #nelement_list = Int[2, 4] - #nelement_list = Int[2, 4, 8] + nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] #nelement_list = Int[8] nscan = size(nelement_list,1) @@ -1826,6 +1830,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ n_err = Array{mk_float,1}(undef,nscan) u_err = Array{mk_float,1}(undef,nscan) p_err = Array{mk_float,1}(undef,nscan) + calculate_times = Array{mk_float,1}(undef,nscan) + init_times = Array{mk_float,1}(undef,nscan) expected = Array{mk_float,1}(undef,nscan) expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) @@ -1839,7 +1845,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ local nelement = nelement_list[iscan] nelement_vpa = 2*nelement nelement_vperp = nelement - fkerr = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, + fkerr, calculate_times[iscan], init_times[iscan] = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, plot_test_output=plot_test_output, impose_zero_gradient_BC=impose_zero_gradient_BC, test_parallelism=test_parallelism, @@ -1922,6 +1928,17 @@ if abspath(PROGRAM_FILE) == @__FILE__ savefig(outfile) println(outfile) end + + calculate_timeslabel = "time/step (ms)" + init_timeslabel = "time/init (ms)" + plot(nelement_list, [calculate_times, init_times], + xlabel=xlabel, label=[calculate_timeslabel init_timeslabel], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_timing_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) end finalize_comms!() end From a03da6d9bf7997b2cdcca37178b0a41b5d1df29c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 11:38:16 +0000 Subject: [PATCH 188/331] Added expected curves to the timing data plot and corrected typo in error data struct. Split plots of errors into overall result (C), and coefficients. --- 2D_FEM_assembly_test.jl | 64 +++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index b9eabb65a..b1ca7d953 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -121,7 +121,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ d2Gdvperp2_M = error_data(0.0,0.0) moments = moments_error_data(0.0,0.0,0.0) return fkpl_error_data(C_M,H_M,dHdvpa_M,dHdvperp_M, - G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvpa2_M, + G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvperp2_M, moments) end # Array in compound 1D form @@ -1723,7 +1723,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ calculate_time = Dates.value(now() - finish_init_time) begin_serial_region() fkerr = allocate_error_data() - println(fkerr) @serial_region begin println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -1791,9 +1790,14 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end + function expect_timing!(expected,nelement_list,nscan,power) + for iscan in 1:nscan + expected[iscan] = nelement_list[iscan]^power + end + end initialize_comms!() - ngrid = 9 + ngrid = 3 plot_scan = true plot_test_output = false impose_zero_gradient_BC = false @@ -1802,8 +1806,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ test_dense_construction = false #nelement_list = Int[8, 16, 32, 64, 128] #nelement_list = Int[8, 16, 32, 64] - #nelement_list = Int[2, 4] - nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4] + #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] #nelement_list = Int[8] nscan = size(nelement_list,1) @@ -1837,10 +1841,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) expected_integral = Array{mk_float,1}(undef,nscan) expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - expected_label = L"(1/N_{el})^{n_g - 1}" expected_integral_label = L"(1/N_{el})^{n_g +1}" + expected_t_2 = Array{mk_float,1}(undef,nscan) + expected_t_3 = Array{mk_float,1}(undef,nscan) + expect_timing!(expected_t_2,nelement_list,nscan,2) + expect_timing!(expected_t_3,nelement_list,nscan,3) + expected_t_2_label = L"(N_{element})^2" + expected_t_3_label = L"(N_{element})^3" + for iscan in 1:nscan local nelement = nelement_list[iscan] nelement_vpa = 2*nelement @@ -1870,6 +1880,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) xlabel = L"N_{element}" Clabel = L"|C|_{\infty}" + Hlabel = L"|H|_{\infty}" + Glabel = L"|G|_{\infty}" dHdvpalabel = L"|dH/d v_{\|\|}|_{\infty}" dHdvperplabel = L"|dH/d v_{\perp}|_{\infty}" d2Gdvperp2label = L"|d^2G/d v_{\perp}^2|_{\infty}" @@ -1878,16 +1890,29 @@ if abspath(PROGRAM_FILE) == @__FILE__ dGdvperplabel = L"|dG/d v_{\perp}|_{\infty}" #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) - plot(nelement_list, [max_C_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + plot(nelement_list, [max_C_err,max_H_err,max_G_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel Hlabel Glabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_C_G_H_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + plot(nelement_list, [max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_numerical_lagrange_integration_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" + outfile = "fkpl_coeffs_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + + ClabelL2 = L"|C|_{L2}" + HlabelL2 = L"|H|_{L2}" + GlabelL2 = L"|G|_{L2}" dHdvpalabelL2 = L"|dH/d v_{\|\|}|_{L2}" dHdvperplabelL2 = L"|dH/d v_{\perp}|_{L2}" d2Gdvperp2labelL2 = L"|d^2G/d v_{\perp}^2|_{L2}" @@ -1896,12 +1921,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ dGdvperplabelL2 = L"|dG/d v_{\perp}|_{L2}" - plot(nelement_list, [L2_C_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[ClabelL2 dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", + plot(nelement_list, [L2_C_err,L2_H_err,L2_G_err, expected, expected_integral], + xlabel=xlabel, label=[ClabelL2 HlabelL2 GlabelL2 expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_C_G_H_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + plot(nelement_list, [L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_numerical_lagrange_integration_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" + outfile = "fkpl_coeffs_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) @@ -1931,9 +1965,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ calculate_timeslabel = "time/step (ms)" init_timeslabel = "time/init (ms)" - plot(nelement_list, [calculate_times, init_times], - xlabel=xlabel, label=[calculate_timeslabel init_timeslabel], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + plot(nelement_list, [calculate_times, init_times, expected_t_2, expected_t_3], + xlabel=xlabel, label=[calculate_timeslabel init_timeslabel expected_t_2_label expected_t_3_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) outfile = "fkpl_timing_test_ngrid_"*string(ngrid)*"_GLL.pdf" From e8bc613d08d758ed82e6522f18b84daa38fe5886 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 23 Oct 2023 13:07:20 +0000 Subject: [PATCH 189/331] Change labels back to using epsilon notation. --- 2D_FEM_assembly_test.jl | 45 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index b1ca7d953..3b89bc9bf 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -1797,17 +1797,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ end initialize_comms!() - ngrid = 3 + ngrid = 5 plot_scan = true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false test_self_operator = true test_dense_construction = false - #nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[8, 16, 32, 64, 128, 256] #nelement_list = Int[8, 16, 32, 64] - nelement_list = Int[2, 4] - #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8] + #nelement_list = Int[4, 8, 16, 32, 64] + #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[100] #nelement_list = Int[8] nscan = size(nelement_list,1) @@ -1879,15 +1880,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ fontsize = 8 ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) xlabel = L"N_{element}" - Clabel = L"|C|_{\infty}" - Hlabel = L"|H|_{\infty}" - Glabel = L"|G|_{\infty}" - dHdvpalabel = L"|dH/d v_{\|\|}|_{\infty}" - dHdvperplabel = L"|dH/d v_{\perp}|_{\infty}" - d2Gdvperp2label = L"|d^2G/d v_{\perp}^2|_{\infty}" - d2Gdvpa2label = L"|d^2G/d v_{\|\|}^2|_{\infty}" - d2Gdvperpdvpalabel = L"|d^2G/d v_{\perp} d v_{\|\|}|_{\infty}" - dGdvperplabel = L"|dG/d v_{\perp}|_{\infty}" + Clabel = L"\epsilon_{\infty}(C)" + Hlabel = L"\epsilon_{\infty}(H)" + Glabel = L"\epsilon_{\infty}(G)" + dHdvpalabel = L"\epsilon_{\infty}(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon_{\infty}(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon_{\infty}(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon_{\infty}(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon_{\infty}(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon_{\infty}(dG/d v_{\perp})" #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) plot(nelement_list, [max_C_err,max_H_err,max_G_err, expected, expected_integral], @@ -1910,15 +1911,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ - ClabelL2 = L"|C|_{L2}" - HlabelL2 = L"|H|_{L2}" - GlabelL2 = L"|G|_{L2}" - dHdvpalabelL2 = L"|dH/d v_{\|\|}|_{L2}" - dHdvperplabelL2 = L"|dH/d v_{\perp}|_{L2}" - d2Gdvperp2labelL2 = L"|d^2G/d v_{\perp}^2|_{L2}" - d2Gdvpa2labelL2 = L"|d^2G/d v_{\|\|}^2|_{L2}" - d2GdvperpdvpalabelL2 = L"|d^2G/d v_{\perp} d v_{\|\|}|_{L2}" - dGdvperplabelL2 = L"|dG/d v_{\perp}|_{L2}" + ClabelL2 = L"\epsilon_{L2}(C)" + HlabelL2 = L"\epsilon_{L2}(H)" + GlabelL2 = L"\epsilon_{L2}(G)" + dHdvpalabelL2 = L"\epsilon_{L2}(dH/d v_{\|\|})" + dHdvperplabelL2 = L"\epsilon_{L2}(dH/d v_{\perp})" + d2Gdvperp2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp}^2)" + d2Gdvpa2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\|\|}^2)" + d2GdvperpdvpalabelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabelL2 = L"\epsilon_{L2}(dG/d v_{\perp})" plot(nelement_list, [L2_C_err,L2_H_err,L2_G_err, expected, expected_integral], From 18d0a2cc8f5c2f47cea95cbf076dd1bf6092b9c5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 27 Oct 2023 08:07:36 +0000 Subject: [PATCH 190/331] Addition of print statements for error data. Correct typo so that nelement_vpa = 2*nelement_vperp in nelement scan. --- 2D_FEM_assembly_test.jl | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 3b89bc9bf..fc6975211 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -1366,7 +1366,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #ngrid = 3 #number of points per element nelement_local_vpa = nelement_vpa # number of elements per rank nelement_global_vpa = nelement_local_vpa # total number of elements - nelement_local_vperp = nelement_vpa # number of elements per rank + nelement_local_vperp = nelement_vperp # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements #Lvpa = 12.0 #physical box size in reference units #Lvperp = 6.0 #physical box size in reference units @@ -1797,18 +1797,19 @@ if abspath(PROGRAM_FILE) == @__FILE__ end initialize_comms!() - ngrid = 5 + ngrid = 3 plot_scan = true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false test_self_operator = true test_dense_construction = false - #nelement_list = Int[8, 16, 32, 64, 128, 256] - #nelement_list = Int[8, 16, 32, 64] - nelement_list = Int[2, 4, 8] + nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[4, 8, 16, 32, 64] + #nelement_list = Int[2, 4, 8] #nelement_list = Int[4, 8, 16, 32, 64] #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] #nelement_list = Int[8] nscan = size(nelement_list,1) @@ -1878,7 +1879,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if global_rank[]==0 && plot_scan fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + #ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + ytick_sequence = Array([1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1]) xlabel = L"N_{element}" Clabel = L"\epsilon_{\infty}(C)" Hlabel = L"\epsilon_{\infty}(H)" @@ -1899,6 +1901,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_C_G_H_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + println([max_C_err,max_H_err,max_G_err, expected, expected_integral]) plot(nelement_list, [max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral], xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", @@ -1908,7 +1911,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - + println([max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral]) ClabelL2 = L"\epsilon_{L2}(C)" @@ -1930,6 +1933,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_C_G_H_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + println([L2_C_err,L2_H_err,L2_G_err, expected, expected_integral]) plot(nelement_list, [L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral], xlabel=xlabel, label=[dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", @@ -1939,6 +1943,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_coeffs_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + println([L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral]) nlabel = L"|\Delta n|" ulabel = L"|\Delta u_{\|\|}|" @@ -1952,7 +1957,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) - println(outfile) + println(outfile) + println([max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral]) else plot(nelement_list, [max_C_err, L2_C_err, n_err, expected, expected_integral], xlabel=xlabel, label=[Clabel ClabelL2 nlabel expected_label expected_integral_label], ylabel="", @@ -1962,18 +1968,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + println([max_C_err, L2_C_err, n_err, expected, expected_integral]) end calculate_timeslabel = "time/step (ms)" init_timeslabel = "time/init (ms)" + ytick_sequence_timing = Array([10^2,10^3,10^4,10^5,10^6]) plot(nelement_list, [calculate_times, init_times, expected_t_2, expected_t_3], xlabel=xlabel, label=[calculate_timeslabel init_timeslabel expected_t_2_label expected_t_3_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + foreground_color_legend = nothing, background_color_legend = nothing, legend=:topleft) outfile = "fkpl_timing_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) + println([calculate_times, init_times, expected_t_2, expected_t_3]) end finalize_comms!() end From 812cd30397fb73ff488e402e1dc6e451fea23450 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 29 Oct 2023 08:28:39 +0000 Subject: [PATCH 191/331] Remove unused (experimental & broken) function/ --- src/fokker_planck.jl | 50 +++++++------------------------------------- 1 file changed, 7 insertions(+), 43 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index f9e8cec45..950876904 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -161,49 +161,6 @@ function allocate_boundary_integration_weights(vpa,vperp) dfdvpa,d2fdvperpdvpa,dfdvperp) end -# initialise the elliptic integral factor arrays -# note the definitions of ellipe & ellipk -# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` -# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipk` -# `ellipe(m) = \int^{\pi/2}\_0 \sqrt{ 1 - m \sin^2(\theta)} d \theta` -# `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` - -function init_elliptic_integral_factors!(elliptic_integral_E_factor, elliptic_integral_K_factor, vperp, vpa) - - # must loop over vpa, vperp, vpa', vperp' - # hence mix of looping macros for unprimed variables - # & standard local `for' loop for primed variables - nvperp = vperp.n - nvpa = vpa.n - zero = 1.0e-10 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - for ivperp in 1:nvperp - for ivpa in 1:nvpa - # the argument of the elliptic integrals - # mm = 4 vperp vperp' / ( (vpa- vpa')^2 + (vperp + vperp')) - denom = (vpa.grid[ivpa] - vpa.grid[ivpap])^2 + (vperp.grid[ivperp] + vperp.grid[ivperpp])^2 - if denom < zero - println("denom = zero ",ivperpp," ",ivpap," ",ivperp," ",ivpa) - end - # #then vpa = vpa' = vperp' = vperp = 0 - # mm = 0.0 - # prefac = 0.0 # because vperp' wgt = 0 here - #else - mm = 4.0*vperp.grid[ivperp]*vperp.grid[ivperpp]/denom - prefac = sqrt(denom) - #end - #println(mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) - elliptic_integral_E_factor[ivpap,ivperpp,ivpa,ivperp] = 2.0*ellipe(mm)*prefac/pi - elliptic_integral_K_factor[ivpap,ivperpp,ivpa,ivperp] = 2.0*ellipk(mm)/(pi*prefac) - #println(elliptic_integral_K_factor[ivpap,ivperpp,ivpa,ivperp]," ",mm," ",prefac," ",denom," ",ivperpp," ",ivpap," ",ivperp," ",ivpa) - - end - end - end - end - -end """ function that initialises the arrays needed for Fokker Planck collisions @@ -618,6 +575,13 @@ function nel_hi(ielement,nelement) return 1- floor(mk_int, ielement/nelement) end +# base level function for computing the Green's function weights +# note the definitions of ellipe & ellipk +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipk` +# `ellipe(m) = \int^{\pi/2}\_0 \sqrt{ 1 - m \sin^2(\theta)} d \theta` +# `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` + function local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids From 018f2d0b87631e02f9b081b811f104bd27fd9090 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 29 Oct 2023 08:36:19 +0000 Subject: [PATCH 192/331] Remove unused (experimental & broken) functions --- src/fokker_planck.jl | 178 ++----------------------------------------- 1 file changed, 5 insertions(+), 173 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 950876904..abd35644c 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -802,50 +802,7 @@ function loop_over_vperp_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_w vpa_val, vperp_val) end return nothing -end - -""" -calculates the (normalised) Rosenbluth potential G -""" -# G(vpa,vperp) = \int^\infty_0 \int^{\infty}_{-\infty} ((vpa- vpa')^2 + (vperp + vperp'))^{1/2} -# * (2 ellipe(mm)/ \pi) F(vpa',vperp') (2 vperp'/\sqrt{\pi}) d vperp' d vpa' - - -function calculate_Rosenbluth_potentials!(Rosenbluth_G,Rosenbluth_H,fsp_in, - elliptic_integral_E_factor,elliptic_integral_K_factor,buffer_vpavperp,vperp,vpa) - - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - # G - @views @. buffer_vpavperp[:,:] = fsp_in*elliptic_integral_E_factor[ivpa,ivperp,:,:] - @views Rosenbluth_G[ivpa,ivperp] = integrate_over_vspace(buffer_vpavperp, vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - # H - @views @. buffer_vpavperp[:,:] = fsp_in*elliptic_integral_K_factor[ivpa,ivperp,:,:] - @views Rosenbluth_H[ivpa,ivperp] = integrate_over_vspace(buffer_vpavperp, vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - end - end - -end - -""" -Computes the Laplacian of G in vpa vperp coordinates to obtain H -""" -function calculate_Rosenbluth_H_from_G!(Rosenbluth_H,Rosenbluth_G,vpa,vpa_spectral,vperp,vperp_spectral,buffer_vpavperp_1,buffer_vpavperp_2) - Rosenbluth_H .= 0.0 - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa, vpa_spectral) - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @views @. buffer_vpavperp_1[:,ivperp] = vpa.scratch2 - end - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp, vperp_spectral) - @. vperp.scratch = vperp.grid*vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @views @. buffer_vpavperp_2[ivpa,:] = vperp.scratch2/vperp.grid - end - @views @. Rosenbluth_H = 0.5*(buffer_vpavperp_1 + buffer_vpavperp_2) -end - +end """ calculates the (normalised) Rosenbluth potential coefficients d2Gdvpa2, d2Gdvperpdvpa, ..., dHdvperp for a Maxwellian inputs. @@ -883,135 +840,6 @@ function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, return Cflux_vpa, Cflux_vperp end -""" -returns (normalised) C[Fs,Fs'] - -""" -#returns (normalised) C[F_s,F_s'] = C[F_s,F_s'](vpa,vperp) given inputs -#distribution F_s = F_s(vpa,vperp) -#distribution F_s' = F_s'(vpa,vperp) -#mass m_s -#mass m_s' -#collision frequency nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 -#with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 -function evaluate_RMJ_collision_operator!(Cssp_out,fs_in,fsp_in,ms,msp,cfreqssp, fokkerplanck_arrays::fokkerplanck_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral) - # calculate the Rosenbluth potentials - # and store in fokkerplanck_arrays_struct - @views calculate_Rosenbluth_potentials!(fokkerplanck_arrays.Rosenbluth_G,fokkerplanck_arrays.Rosenbluth_H,fsp_in, - fokkerplanck_arrays.elliptic_integral_E_factor, - fokkerplanck_arrays.elliptic_integral_K_factor, - fokkerplanck_arrays.buffer_vpavperp_1,vperp,vpa) - - # short names for buffer arrays - buffer_1 = fokkerplanck_arrays.buffer_vpavperp_1 - buffer_2 = fokkerplanck_arrays.buffer_vpavperp_2 - Rosenbluth_G = fokkerplanck_arrays.Rosenbluth_G - Rosenbluth_H = fokkerplanck_arrays.Rosenbluth_H - nvperp = vperp.n - nvpa = vpa.n - # zero Cssp to prepare for addition of collision terms - Cssp_out .= 0.0 - - # + d^2 F_s / d vpa^2 * d^2 G_sp / d vpa^2 - for ivperp in 1:nvperp - vpa.scratch2 .= 1.0 # remove Q argument from second_derivative! as never different from 1? - @views second_derivative!(vpa.scratch, fs_in[:,ivperp], vpa.scratch2, vpa, vpa_spectral) - @views @. buffer_1[:,ivperp] = vpa.scratch - @views second_derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa.scratch2, vpa, vpa_spectral) - @views @. buffer_2[:,ivperp] = vpa.scratch - end - @views @. Cssp_out += buffer_1*buffer_2 - - # + 2 d^2 F_s / d vpa d vperp * d^2 G_sp / d vpa d vperp - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) - @views @. buffer_1[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch, Rosenbluth_G[:,ivperp], vpa, vpa_spectral) - @views @. buffer_2[:,ivperp] = vpa.scratch - end - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, buffer_1[ivpa,:], vperp, vperp_spectral) - @views @. buffer_1[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch, buffer_2[ivpa,:], vperp, vperp_spectral) - @views @. buffer_2[ivpa,:] = vperp.scratch - end - @views @. Cssp_out += 2.0*buffer_1*buffer_2 - - # + d^2 F_s / d vperp^2 * d^2 G_sp / d vperp^2 - for ivpa in 1:nvpa - vperp.scratch2 .= 1.0 # remove Q argument from second_derivative! as never different from 1? - @views second_derivative!(vperp.scratch, fs_in[ivpa,:], vperp.scratch2, vperp, vperp_spectral) - @views @. buffer_1[ivpa,:] = vperp.scratch - @views second_derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp.scratch2, vperp, vperp_spectral) - @views @. buffer_2[ivpa,:] = vperp.scratch - end - @views @. Cssp_out += buffer_1*buffer_2 - - # + ( 1/vperp^2) d F_s / d vperp * d G_sp / d vperp - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) - @views @. buffer_1[ivpa,:] = vperp.scratch/(vperp.grid^2) # MRH this line causes divide by zero! - @views derivative!(vperp.scratch, Rosenbluth_G[ivpa,:], vperp, vperp_spectral) - @views @. buffer_2[ivpa,:] = vperp.scratch - end - @views @. Cssp_out += buffer_1*buffer_2 - - # + 2( 1 - ms/msp) * d F_s / d vpa * d H_sp / d vpa - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) - @views @. buffer_1[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch, Rosenbluth_H[:,ivperp], vpa, vpa_spectral) - @views @. buffer_2[:,ivperp] = vpa.scratch - end - @views @. Cssp_out += 2.0*(1.0 - ms/msp)*buffer_1*buffer_2 - - # + 2( 1 - ms/msp) * d F_s / d vperp * d H_sp / d vperp - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) - @views @. buffer_1[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch, Rosenbluth_H[ivpa,:], vperp, vperp_spectral) - @views @. buffer_2[ivpa,:] = vperp.scratch - end - @views @. Cssp_out += 2.0*(1.0 - ms/msp)*buffer_1*buffer_2 - - # + (8 ms / \sqrt{\pi} msp ) F_s F_sp - @views @. Cssp_out += ((8.0*ms)/(sqrt(pi)*msp))*fs_in*fsp_in - - # multiply by overall collision frequency - @views @. Cssp_out = cfreqssp*Cssp_out -end - -function explicit_fokker_planck_collisions_old!(pdf_out,pdf_in,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) - n_ion_species = composition.n_ion_species - @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) - @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) - @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) - @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) - @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) - @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) - @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) - @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) - @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) - Cssp_result_vpavperp = scratch_dummy.dummy_vpavperp - mi = 1.0 # generalise this to an Array with size n_ion_species - cfreqii = collisions.nuii # generalise this to an Array with size (n_ion_species,n_ion_species) - - begin_r_z_region() - # serial in s vperp vpa for now - for is in 1:n_ion_species - for isp in 1:n_ion_species - @loop_r_z ir iz begin - @views evaluate_RMJ_collision_operator!(Cssp_result_vpavperp,pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,isp], - mi, mi, cfreqii, fokkerplanck_arrays, vperp, vpa, - vperp_spectral, vpa_spectral) - @views @. pdf_out[:,:,iz,ir,is] += dt*Cssp_result_vpavperp[:,:] - end - end - end -end - """ Function to carry out the integration of the revelant distribution functions to form the required coefficients @@ -1074,6 +902,10 @@ in place the fokkerplanck_arrays struct with testable distributions function derivatives, Rosenbluth potentials, and collision operator in place. """ +#returns distribution function advanced by the (normalised) +# C[F_s,F_s'] = C[F_s,F_s'](vpa,vperp) given inputs +#collision frequency nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 +#with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, From 411fa9727beba11da2116823f8ea848fd89e188b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 29 Oct 2023 10:29:33 +0000 Subject: [PATCH 193/331] Major refactoring of organisation of the Fokker-Planck functions. Created two new modules "fokker_planck_calculus.jl" for integration and weak-form matrices and "fokker_planck_test.jl" for functions derived from analytical expressions that are mostly used for testing of the numerical implementation. Also bring fkpl_functional_test.jl back in line with the main source to permit the testing of a strong-form operator at the same time as implementing the weak-form operator. --- 2D_FEM_assembly_test.jl | 1255 +-------------------- Project.toml | 8 + fkpl_functional_test.jl | 61 +- src/fokker_planck.jl | 965 +--------------- src/fokker_planck_calculus.jl | 1993 +++++++++++++++++++++++++++++++++ src/fokker_planck_test.jl | 269 +++++ src/initial_conditions.jl | 4 + src/moment_kinetics.jl | 2 + src/time_advance.jl | 2 + 9 files changed, 2349 insertions(+), 2210 deletions(-) create mode 100644 src/fokker_planck_calculus.jl create mode 100644 src/fokker_planck_test.jl diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index fc6975211..2d220b47c 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -10,11 +10,8 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.fokker_planck: F_Maxwellian, H_Maxwellian, G_Maxwellian, Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck: d2Gdvpa2, d2Gdvperp2, dGdvperp, d2Gdvperpdvpa, dHdvpa, dHdvperp -using moment_kinetics.fokker_planck: init_fokker_planck_collisions, fokkerplanck_arrays_struct, fokkerplanck_boundary_data_arrays_struct -using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new, boundary_integration_weights_struct -using moment_kinetics.fokker_planck: get_element_limit_indices +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.communication @@ -23,6 +20,21 @@ using moment_kinetics.looping using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky +using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian +using moment_kinetics.fokker_planck_test: d2Gdvpa2, d2Gdvperp2, d2Gdvperpdvpa, dGdvperp +using moment_kinetics.fokker_planck_test: dHdvperp, dHdvpa +using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs + +using moment_kinetics.fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc, assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient +using moment_kinetics.fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse, assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse +using moment_kinetics.fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial!, assemble_explicit_collision_operator_rhs_parallel! +using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! +using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data +using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data!, calculate_rosenbluth_potential_boundary_data_exact! +using moment_kinetics.fokker_planck_calculus: calculate_YY_arrays +using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data + + if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") @@ -124,1234 +136,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvperp2_M, moments) end - # Array in compound 1D form - - function ic_func(ivpa::mk_int,ivperp::mk_int,nvpa::mk_int) - return ivpa + nvpa*(ivperp-1) - end - function ivperp_func(ic::mk_int,nvpa::mk_int) - return floor(Int64,(ic-1)/nvpa) + 1 - end - function ivpa_func(ic::mk_int,nvpa::mk_int) - ivpa = ic - nvpa*(ivperp_func(ic,nvpa) - 1) - return ivpa - end - - function ravel_vpavperp_to_c!(pdf_c,pdf_vpavperp,nvpa::mk_int,nvperp::mk_int) - for ivperp in 1:nvperp - for ivpa in 1:nvpa - ic = ic_func(ivpa,ivperp,nvpa) - pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] - end - end - return nothing - end - - function ravel_vpavperp_to_c_parallel!(pdf_c,pdf_vpavperp,nvpa::mk_int) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - ic = ic_func(ivpa,ivperp,nvpa) - pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] - end - return nothing - end - - function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc::mk_int,nvpa::mk_int) - for ic in 1:nc - ivpa = ivpa_func(ic,nvpa) - ivperp = ivperp_func(ic,nvpa) - pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] - end - return nothing - end - - function ravel_c_to_vpavperp_parallel!(pdf_vpavperp,pdf_c,nvpa::mk_int) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - ic = ic_func(ivpa,ivperp,nvpa) - pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] - end - return nothing - end - - function ivpa_global_func(ivpa_local::mk_int,ielement_vpa::mk_int,ngrid_vpa::mk_int) - ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) - return ivpa_global - end - - # function that returns the sparse matrix index - # used to directly construct the nonzero entries - # of a 2D assembled sparse matrix - function icsc_func(ivpa_local::mk_int,ivpap_local::mk_int, - ielement_vpa::mk_int, - ngrid_vpa::mk_int,nelement_vpa::mk_int, - ivperp_local::mk_int,ivperpp_local::mk_int, - ielement_vperp::mk_int, - ngrid_vperp::mk_int,nelement_vperp::mk_int) - ntot_vpa = (nelement_vpa - 1)*(ngrid_vpa^2 - 1) + ngrid_vpa^2 - ntot_vperp = (nelement_vperp - 1)*(ngrid_vperp^2 - 1) + ngrid_vperp^2 - - icsc_vpa = ((ivpap_local - 1) + (ivpa_local - 1)*ngrid_vpa + - (ielement_vpa - 1)*(ngrid_vpa^2 - 1)) - icsc_vperp = ((ivperpp_local - 1) + (ivperp_local - 1)*ngrid_vperp + - (ielement_vperp - 1)*(ngrid_vperp^2 - 1)) - icsc = 1 + icsc_vpa + ntot_vpa*icsc_vperp - return icsc - end - - struct sparse_matrix_constructor - # the Ith row - II::Array{mk_float,1} - # the Jth column - JJ::Array{mk_float,1} - # the data S[I,J] - SS::Array{mk_float,1} - end - - function allocate_sparse_matrix_constructor(nsparse::mk_int) - II = Array{mk_int,1}(undef,nsparse) - @. II = 0 - JJ = Array{mk_int,1}(undef,nsparse) - @. JJ = 0 - SS = Array{mk_float,1}(undef,nsparse) - @. SS = 0.0 - return sparse_matrix_constructor(II,JJ,SS) - end - - function assign_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) - data.II[icsc] = ii - data.JJ[icsc] = jj - data.SS[icsc] = ss - return nothing - end - function assemble_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) - data.II[icsc] = ii - data.JJ[icsc] = jj - data.SS[icsc] += ss - return nothing - end - - function create_sparse_matrix(data::sparse_matrix_constructor) - return sparse(data.II,data.JJ,data.SS) - end - struct vpa_vperp_boundary_data - lower_boundary_vpa::MPISharedArray{mk_float,1} - upper_boundary_vpa::MPISharedArray{mk_float,1} - upper_boundary_vperp::MPISharedArray{mk_float,1} - end - - struct rosenbluth_potential_boundary_data - H_data::vpa_vperp_boundary_data - dHdvpa_data::vpa_vperp_boundary_data - dHdvperp_data::vpa_vperp_boundary_data - G_data::vpa_vperp_boundary_data - dGdvperp_data::vpa_vperp_boundary_data - d2Gdvperp2_data::vpa_vperp_boundary_data - d2Gdvperpdvpa_data::vpa_vperp_boundary_data - d2Gdvpa2_data::vpa_vperp_boundary_data - end - function allocate_boundary_data(vpa,vperp) - lower_boundary_vpa = Array{mk_float,1}(undef,vperp.n) - upper_boundary_vpa = Array{mk_float,1}(undef,vperp.n) - upper_boundary_vperp = Array{mk_float,1}(undef,vpa.n) - return vpa_vperp_boundary_data(lower_boundary_vpa, - upper_boundary_vpa,upper_boundary_vperp) - end - - function assign_exact_boundary_data!(func_data::vpa_vperp_boundary_data, - func_exact,vpa,vperp) - nvpa = vpa.n - nvperp = vperp.n - for ivperp in 1:nvperp - func_data.lower_boundary_vpa[ivperp] = func_exact[1,ivperp] - func_data.upper_boundary_vpa[ivperp] = func_exact[nvpa,ivperp] - end - for ivpa in 1:nvpa - func_data.upper_boundary_vperp[ivpa] = func_exact[ivpa,nvperp] - end - return nothing - end - - function allocate_rosenbluth_potential_boundary_data(vpa,vperp) - H_data = allocate_boundary_data(vpa,vperp) - dHdvpa_data = allocate_boundary_data(vpa,vperp) - dHdvperp_data = allocate_boundary_data(vpa,vperp) - G_data = allocate_boundary_data(vpa,vperp) - dGdvperp_data = allocate_boundary_data(vpa,vperp) - d2Gdvperp2_data = allocate_boundary_data(vpa,vperp) - d2Gdvperpdvpa_data = allocate_boundary_data(vpa,vperp) - d2Gdvpa2_data = allocate_boundary_data(vpa,vperp) - return rosenbluth_potential_boundary_data(H_data,dHdvpa_data, - dHdvperp_data,G_data,dGdvperp_data,d2Gdvperp2_data, - d2Gdvperpdvpa_data,d2Gdvpa2_data) - end - - function calculate_rosenbluth_potential_boundary_data_exact!(rpbd::rosenbluth_potential_boundary_data, - H_exact,dHdvpa_exact,dHdvperp_exact,G_exact,dGdvperp_exact, - d2Gdvperp2_exact,d2Gdvperpdvpa_exact,d2Gdvpa2_exact, - vpa,vperp) - assign_exact_boundary_data!(rpbd.H_data,H_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.dHdvpa_data,dHdvpa_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.dHdvperp_data,dHdvperp_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.G_data,G_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.dGdvperp_data,dGdvperp_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.d2Gdvperp2_data,d2Gdvperp2_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.d2Gdvperpdvpa_data,d2Gdvperpdvpa_exact,vpa,vperp) - assign_exact_boundary_data!(rpbd.d2Gdvpa2_data,d2Gdvpa2_exact,vpa,vperp) - return nothing - end - - - function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, - weight::MPISharedArray{mk_float,4},func_input,vpa,vperp) - nvpa = vpa.n - nvperp = vperp.n - #for ivperp in 1:nvperp - begin_vperp_region() - @loop_vperp ivperp begin - func_data.lower_boundary_vpa[ivperp] = 0.0 - func_data.upper_boundary_vpa[ivperp] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - func_data.lower_boundary_vpa[ivperp] += weight[ivpap,ivperpp,1,ivperp]*func_input[ivpap,ivperpp] - func_data.upper_boundary_vpa[ivperp] += weight[ivpap,ivperpp,nvpa,ivperp]*func_input[ivpap,ivperpp] - end - end - end - #for ivpa in 1:nvpa - begin_vpa_region() - @loop_vpa ivpa begin - func_data.upper_boundary_vperp[ivpa] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - func_data.upper_boundary_vperp[ivpa] += weight[ivpap,ivperpp,ivpa,nvperp]*func_input[ivpap,ivperpp] - end - end - end - # return to serial parallelisation - begin_serial_region() - return nothing - end - - function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, - weight::boundary_integration_weights_struct, - func_input,vpa,vperp) - nvpa = vpa.n - nvperp = vperp.n - #for ivperp in 1:nvperp - begin_vperp_region() - @loop_vperp ivperp begin - func_data.lower_boundary_vpa[ivperp] = 0.0 - func_data.upper_boundary_vpa[ivperp] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - func_data.lower_boundary_vpa[ivperp] += weight.lower_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] - func_data.upper_boundary_vpa[ivperp] += weight.upper_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] - end - end - end - #for ivpa in 1:nvpa - begin_vpa_region() - @loop_vpa ivpa begin - func_data.upper_boundary_vperp[ivpa] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - func_data.upper_boundary_vperp[ivpa] += weight.upper_vperp_boundary[ivpap,ivperpp,ivpa]*func_input[ivpap,ivperpp] - end - end - end - # return to serial parallelisation - begin_serial_region() - return nothing - end - - function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, - fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral) - # get derivatives of pdf - dfdvperp = fkpl.dfdvperp - dfdvpa = fkpl.dfdvpa - d2fdvperpdvpa = fkpl.d2fdvperpdvpa - #for ivpa in 1:vpa.n - begin_vpa_region() - @loop_vpa ivpa begin - @views derivative!(vperp.scratch, pdf[ivpa,:], vperp, vperp_spectral) - @. dfdvperp[ivpa,:] = vperp.scratch - end - begin_vperp_region() - @loop_vperp ivperp begin - #for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) - @. dfdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch, dfdvperp[:,ivperp], vpa, vpa_spectral) - @. d2fdvperpdvpa[:,ivperp] = vpa.scratch - end - # ensure data is synchronized - begin_serial_region() - # carry out the numerical integration - calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) - calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) - calculate_boundary_data!(rpbd.dHdvperp_data,fkpl.H1_weights,dfdvperp,vpa,vperp) - calculate_boundary_data!(rpbd.G_data,fkpl.G0_weights,pdf,vpa,vperp) - calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) - calculate_boundary_data!(rpbd.d2Gdvperp2_data,fkpl.H2_weights,dfdvperp,vpa,vperp) - calculate_boundary_data!(rpbd.d2Gdvperpdvpa_data,fkpl.G1_weights,d2fdvperpdvpa,vpa,vperp) - calculate_boundary_data!(rpbd.d2Gdvpa2_data,fkpl.H3_weights,dfdvpa,vpa,vperp) - - return nothing - end - - function test_rosenbluth_potential_boundary_data(rpbd::rosenbluth_potential_boundary_data, - rpbd_exact::rosenbluth_potential_boundary_data,vpa,vperp) - - error_buffer_vpa = Array{mk_float,1}(undef,vpa.n) - error_buffer_vperp_1 = Array{mk_float,1}(undef,vperp.n) - error_buffer_vperp_2 = Array{mk_float,1}(undef,vperp.n) - test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - - return nothing - end - - function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2) - nvpa = vpa.n - nvperp = vperp.n - for ivperp in 1:nvperp - buffer_vperp_1 = abs(func.lower_boundary_vpa[ivperp] - func_exact.lower_boundary_vpa[ivperp]) - buffer_vperp_2 = abs(func.upper_boundary_vpa[ivperp] - func_exact.upper_boundary_vpa[ivperp]) - end - for ivpa in 1:nvpa - buffer_vpa = abs(func.upper_boundary_vperp[ivpa] - func_exact.upper_boundary_vperp[ivpa]) - end - @serial_region begin - max_lower_vpa_err = maximum(buffer_vperp_1) - max_upper_vpa_err = maximum(buffer_vperp_2) - max_upper_vperp_err = maximum(buffer_vpa) - println(string(func_name*" boundary data:")) - println("max(lower_vpa_err) = ",max_lower_vpa_err) - println("max(upper_vpa_err) = ",max_upper_vpa_err) - println("max(upper_vperp_err) = ",max_upper_vperp_err) - end - return nothing - end - - function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - # global indices on the grids - ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] - ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] - # global compound index - ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) - return ic_global, ivpa_global, ivperp_global - end - function enforce_zero_bc!(fc,vpa,vperp;impose_BC_at_zero_vperp=false) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - - if impose_BC_at_zero_vperp - # lower vperp boundary - ielement_vperp = 1 - ivperp_local = 1 - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = 0.0 - end - end - end - - function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc;dirichlet_vperp_BC=false) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - - if dirichlet_vperp_BC - # upper vperp boundary - ielement_vperp = 1 - ivperp_local = 1 - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc[ivpa_global,ivperp_global] - end - end - end - - function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) - # lower vpa boundary - ielement_vpa = 1 - ivpa_local = 1 - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.lower_boundary_vpa[ivperp_global] - end - end - - # upper vpa boundary - ielement_vpa = vpa.nelement_local - ivpa_local = vpa.ngrid - for ielement_vperp in 1:vperp.nelement_local - for ivperp_local in 1:vperp.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.upper_boundary_vpa[ivperp_global] - end - end - - # upper vperp boundary - ielement_vperp = vperp.nelement_local - ivperp_local = vperp.ngrid - for ielement_vpa in 1:vpa.nelement_local - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - fc[ic_global] = f_bc.upper_boundary_vperp[ivpa_global] - end - end - return nothing - end - - function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) - nc_global = vpa.n*vperp.n - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - MM2D = Array{mk_float,2}(undef,nc_global,nc_global) - MM2D .= 0.0 - KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) - KKpar2D .= 0.0 - KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - KKperp2D .= 0.0 - PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - PUperp2D .= 0.0 - PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - PPparPUperp2D .= 0.0 - PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) - PPpar2D .= 0.0 - MMparMNperp2D = Array{mk_float,2}(undef,nc_global,nc_global) - MMparMNperp2D .= 0.0 - # Laplacian matrix - LP2D = Array{mk_float,2}(undef,nc_global,nc_global) - LP2D .= 0.0 - # Modified Laplacian matrix - LV2D = Array{mk_float,2}(undef,nc_global,nc_global) - LV2D .= 0.0 - - #print_matrix(MM2D,"MM2D",nc_global,nc_global) - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - impose_BC_at_zero_vperp = false - @serial_region begin - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") - get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") - get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") - get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") - get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") - #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) - #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) - - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") - #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) - #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) - #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) - - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) - #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) - #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) - #println("ic: ",ic_global," icp: ",icp_global) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp - if ivperpp_local == 1 && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 - LP2D[ic_global,icp_global] = 1.0 - LV2D[ic_global,icp_global] = 1.0 - else - MM2D[ic_global,icp_global] = 0.0 - LP2D[ic_global,icp_global] = 0.0 - LV2D[ic_global,icp_global] = 0.0 - end - else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - LLperp[ivperp_local,ivperpp_local]) - LV2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* - MRperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - (KJperp[ivperp_local,ivperpp_local] - - PPperp[ivperp_local,ivperpp_local] - - MNperp[ivperp_local,ivperpp_local])) - end - - # assign K matrices - KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - KKperp[ivperp_local,ivperpp_local] - # assign PU matrix - PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local] - PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local] - PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - # assign RHS mass matrix for d2Gdvperp2 - MMparMNperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MNperp[ivperp_local,ivperpp_local] - end - end - end - end - end - end - @serial_region begin - println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - - if nc_global < 60 - print_matrix(MM2D,"MM2D",nc_global,nc_global) - #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - #print_matrix(LP2D,"LP",nc_global,nc_global) - #print_matrix(LV2D,"LV",nc_global,nc_global) - end - # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - MM2D_sparse = sparse(MM2D) - KKpar2D_sparse = sparse(KKpar2D) - KKperp2D_sparse = sparse(KKperp2D) - LP2D_sparse = sparse(LP2D) - LV2D_sparse = sparse(LV2D) - PUperp2D_sparse = sparse(PUperp2D) - PPparPUperp2D_sparse = sparse(PPparPUperp2D) - PPpar2D_sparse = sparse(PPpar2D) - MMparMNperp2D_sparse = sparse(MMparMNperp2D) - return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, - LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, - PPpar2D_sparse, MMparMNperp2D_sparse - end - - function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) - nc_global = vpa.n*vperp.n - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) - MM2DZG .= 0.0 - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - @serial_region begin - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp - if ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - else - # assign mass matrix data - MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - end - end - end - end - end - end - end - @serial_region begin - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - if nc_global < 30 - print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) - end - # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - MM2DZG_sparse = sparse(MM2DZG) - return MM2DZG_sparse - end - - function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 - ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 - nsparse = ntot_vpa*ntot_vperp - ngrid_vpa = vpa.ngrid - nelement_vpa = vpa.nelement_local - ngrid_vperp = vperp.ngrid - nelement_vperp = vperp.nelement_local - - MM2D = allocate_sparse_matrix_constructor(nsparse) - KKpar2D = allocate_sparse_matrix_constructor(nsparse) - KKperp2D = allocate_sparse_matrix_constructor(nsparse) - PUperp2D = allocate_sparse_matrix_constructor(nsparse) - PPparPUperp2D = allocate_sparse_matrix_constructor(nsparse) - PPpar2D = allocate_sparse_matrix_constructor(nsparse) - MMparMNperp2D = allocate_sparse_matrix_constructor(nsparse) - # Laplacian matrix - LP2D = allocate_sparse_matrix_constructor(nsparse) - # Modified Laplacian matrix - LV2D = allocate_sparse_matrix_constructor(nsparse) - - # local dummy arrays - MMpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) - MMperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - MNperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) - KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - KJperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - PPperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - PUperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - PPpar = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - - impose_BC_at_zero_vperp = false - @serial_region begin - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - end - for ielement_vperp in 1:nelement_vperp - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") - get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") - get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") - get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") - get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") - get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") - #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) - #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) - #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) - #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) - #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) - - for ielement_vpa in 1:nelement_vpa - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") - #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) - #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) - #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) - - for ivperpp_local in 1:ngrid_vperp - for ivperp_local in 1:ngrid_vperp - for ivpap_local in 1:ngrid_vpa - for ivpa_local in 1:ngrid_vpa - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, - ngrid_vpa,nelement_vpa, - ivperp_local,ivperpp_local, - ielement_vperp, - ngrid_vperp,nelement_vperp) - #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) - #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) - #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) - #println("ic: ",ic_global," icp: ",icp_global) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) - end - elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp - if ivperpp_local == 1 && ivpa_local == ivpap_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) - assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) - end - else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) - assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, - (KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - LLperp[ivperp_local,ivperpp_local])) - assemble_constructor_data!(LV2D,icsc,ic_global,icp_global, - (KKpar[ivpa_local,ivpap_local]* - MRperp[ivperp_local,ivperpp_local] + - MMpar[ivpa_local,ivpap_local]* - (KJperp[ivperp_local,ivperpp_local] - - PPperp[ivperp_local,ivperpp_local] - - MNperp[ivperp_local,ivperpp_local]))) - end - - # assign K matrices - assemble_constructor_data!(KKpar2D,icsc,ic_global,icp_global, - (KKpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) - assemble_constructor_data!(KKperp2D,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - KKperp[ivperp_local,ivperpp_local])) - # assign PU matrix - assemble_constructor_data!(PUperp2D,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local])) - assemble_constructor_data!(PPparPUperp2D,icsc,ic_global,icp_global, - (PPpar[ivpa_local,ivpap_local]* - PUperp[ivperp_local,ivperpp_local])) - assemble_constructor_data!(PPpar2D,icsc,ic_global,icp_global, - (PPpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) - # assign RHS mass matrix for d2Gdvperp2 - assemble_constructor_data!(MMparMNperp2D,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - MNperp[ivperp_local,ivperpp_local])) - end - end - end - end - end - end - MM2D_sparse = create_sparse_matrix(MM2D) - KKpar2D_sparse = create_sparse_matrix(KKpar2D) - KKperp2D_sparse = create_sparse_matrix(KKperp2D) - LP2D_sparse = create_sparse_matrix(LP2D) - LV2D_sparse = create_sparse_matrix(LV2D) - PUperp2D_sparse = create_sparse_matrix(PUperp2D) - PPparPUperp2D_sparse = create_sparse_matrix(PPparPUperp2D) - PPpar2D_sparse = create_sparse_matrix(PPpar2D) - MMparMNperp2D_sparse = create_sparse_matrix(MMparMNperp2D) - @serial_region begin - println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) - - if nc_global < 60 - println("MM2D_sparse \n",MM2D_sparse) - print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) - # print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - # print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - # print_matrix(LP2D,"LP",nc_global,nc_global) - # print_matrix(LV2D,"LV",nc_global,nc_global) - end - # convert these matrices to sparse matrices - #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, - LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, - PPpar2D_sparse, MMparMNperp2D_sparse - end - - function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 - ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 - nsparse = ntot_vpa*ntot_vperp - ngrid_vpa = vpa.ngrid - nelement_vpa = vpa.nelement_local - ngrid_vperp = vperp.ngrid - nelement_vperp = vperp.nelement_local - - MM2DZG = allocate_sparse_matrix_constructor(nsparse) - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - @serial_region begin - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, - ngrid_vpa,nelement_vpa, - ivperp_local,ivperpp_local, - ielement_vperp, - ngrid_vperp,nelement_vperp) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp - if ivpa_local == ivpap_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,vperp_spectral.radau.D0[ivperpp_local]) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - else - # assign mass matrix data - assemble_constructor_data!(MM2DZG,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) - end - end - end - end - end - end - end - @serial_region begin - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - #if nc_global < 30 - # print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) - #end - # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - MM2DZG_sparse = create_sparse_matrix(MM2DZG) - return MM2DZG_sparse - end - - struct YY_collision_operator_arrays - # let phi_j(vperp) be the jth Lagrange basis function, - # and phi'_j(vperp) the first derivative of the Lagrange basis function - # on the iel^th element. Then, the arrays are defined as follows. - # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp - YY0perp::Array{mk_float,4} - # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp - YY1perp::Array{mk_float,4} - # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp - YY2perp::Array{mk_float,4} - # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp - YY3perp::Array{mk_float,4} - # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa - YY0par::Array{mk_float,4} - # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa - YY1par::Array{mk_float,4} - # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa - YY2par::Array{mk_float,4} - # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa - YY3par::Array{mk_float,4} - end - - function calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) - YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY3perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) - YY0par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY1par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY2par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - YY3par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) - - for ielement_vperp in 1:vperp.nelement_local - @views get_QQ_local!(YY0perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") - @views get_QQ_local!(YY1perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") - @views get_QQ_local!(YY2perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") - @views get_QQ_local!(YY3perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") - end - for ielement_vpa in 1:vpa.nelement_local - @views get_QQ_local!(YY0par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") - @views get_QQ_local!(YY1par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") - @views get_QQ_local!(YY2par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") - @views get_QQ_local!(YY3par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") - end - - return YY_collision_operator_arrays(YY0perp,YY1perp,YY2perp,YY3perp, - YY0par,YY1par,YY2par,YY3par) - end - - function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,YY_arrays::YY_collision_operator_arrays) - # assemble RHS of collision operator - @. rhsc = 0.0 - - # loop over elements - for ielement_vperp in 1:vperp.nelement_local - YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] - YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] - YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] - YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - - for ielement_vpa in 1:vpa.nelement_local - YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] - YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] - YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] - YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] - - # loop over field positions in each element - for ivperp_local in 1:vperp.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - # carry out the matrix sum on each 2D element - for jvperpp_local in 1:vperp.ngrid - jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] - for kvperpp_local in 1:vperp.ngrid - kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] - for jvpap_local in 1:vpa.ngrid - jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] - pdfjj = pdfs[jvpap,jvperpp] - for kvpap_local in 1:vpa.ngrid - kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - # first three lines represent parallel flux terms - # second three lines represent perpendicular flux terms - rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + - YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - - 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + - # end parallel flux, start of perpendicular flux - YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + - YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - - 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) - end - end - end - end - end - end - end - end - # correct for minus sign due to integration by parts - # and multiply by the normalised collision frequency - @. rhsc *= -nussp - return nothing - end - - function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, - vpa,vperp,YY_arrays::YY_collision_operator_arrays) - # assemble RHS of collision operator - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - rhsvpavperp[ivpa,ivperp] = 0.0 - end - # loop over collocation points to benefit from shared-memory parallelism - ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid - @loop_vperp_vpa ivperp_global ivpa_global begin - igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) - # loop over elements belonging to this collocation point - for ielement_vperp in ielement_vperp_low:ielement_vperp_hi - # correct local ivperp in the case that we on a boundary point - ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) - @views YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] - @views YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] - @views YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] - @views YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - - for ielement_vpa in ielement_vpa_low:ielement_vpa_hi - # correct local ivpa in the case that we on a boundary point - ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) - @views YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] - @views YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] - @views YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] - @views YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] - - # carry out the matrix sum on each 2D element - for jvperpp_local in 1:vperp.ngrid - jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] - for kvperpp_local in 1:vperp.ngrid - kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] - for jvpap_local in 1:vpa.ngrid - jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] - pdfjj = pdfs[jvpap,jvperpp] - for kvpap_local in 1:vpa.ngrid - kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - # first three lines represent parallel flux terms - # second three lines represent perpendicular flux terms - rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + - YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - - 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + - # end parallel flux, start of perpendicular flux - YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + - YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - - 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) - end - end - end - end - end - end - end - # ravel to compound index - #begin_serial_region() - #ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) - ravel_vpavperp_to_c_parallel!(rhsc,rhsvpavperp,vpa.n) - return nothing - end - - - # Elliptic solve function. - # field: the solution - # source: the source function on the RHS - # boundary data: the known values of field at infinity - # lu_object_lhs: the object for the differential operator that defines field - # matrix_rhs: the weak matrix acting on the source vector - # rhsc, sc: dummy arrays in the compound index (assumed MPISharedArray or SubArray type) - # vpa, vperp: coordinate structs - function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, - lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) - # get data into the compound index format - begin_vperp_vpa_region() - ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) - # assemble the rhs of the weak system - begin_serial_region() - mul!(rhsc,matrix_rhs,sc) - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) - # solve the linear system - sc = lu_object_lhs \ rhsc - # get data into the vpa vperp indices format - begin_vperp_vpa_region() - ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) - return nothing - end - # same as above but source is made of two different terms - # with different weak matrices - function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, - lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) - # get data into the compound index format - begin_vperp_vpa_region() - ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) - ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) - - # assemble the rhs of the weak system - begin_serial_region() - mul!(rhsc_1,matrix_rhs_1,sc_1) - mul!(rhsc_2,matrix_rhs_2,sc_2) - @loop_vperp_vpa ivperp ivpa begin - ic = ic_func(ivpa,ivperp,vpa.n) - rhsc_1[ic] += rhsc_2[ic] - end - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) - # solve the linear system - sc_1 = lu_object_lhs \ rhsc_1 - # get data into the vpa vperp indices format - begin_vperp_vpa_region() - ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) - return nothing - end - function test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0,plot_test_output=false,impose_zero_gradient_BC=false, @@ -1797,21 +582,21 @@ if abspath(PROGRAM_FILE) == @__FILE__ end initialize_comms!() - ngrid = 3 + ngrid = 9 plot_scan = true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false test_self_operator = true test_dense_construction = false - nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[8, 16, 32, 64, 128] #nelement_list = Int[4, 8, 16, 32, 64] #nelement_list = Int[2, 4, 8] #nelement_list = Int[4, 8, 16, 32, 64] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] - #nelement_list = Int[8] + nelement_list = Int[4, 8] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) diff --git a/Project.toml b/Project.toml index 8039f8e39..51acefbd1 100644 --- a/Project.toml +++ b/Project.toml @@ -4,17 +4,23 @@ authors = ["Michael Barnes "] version = "0.1.0" [deps] +ArbNumerics = "7e558dbc-694d-5a72-987c-6f4ebed21442" ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +Cubature = "667455a9-e2ce-5579-9412-b964f529a492" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +Elliptic = "b305315f-e792-5b7a-8f41-49f472929428" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +LegendrePolynomials = "3db4a2ba-fc88-11e8-3e01-49c72059a882" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LsqFit = "2fda8390-95c7-5789-9bda-21331edee243" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" @@ -26,10 +32,12 @@ OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl index 563c6723d..2b722b426 100644 --- a/fkpl_functional_test.jl +++ b/fkpl_functional_test.jl @@ -10,17 +10,12 @@ using moment_kinetics.input_structs: grid_input, advection_input, species_compos using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! -using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! -using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! -#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! using moment_kinetics.fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! -using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients -using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian -using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.fokker_planck_test: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +using moment_kinetics.fokker_planck_test: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +using moment_kinetics.fokker_planck_test: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck_test: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.calculus: derivative!, second_derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure @@ -29,6 +24,10 @@ using moment_kinetics.communication using moment_kinetics.looping using moment_kinetics.array_allocation: allocate_shared_float, allocate_float using moment_kinetics.time_advance: setup_dummy_and_buffer_arrays +using moment_kinetics.advection: setup_advection +using moment_kinetics.initial_conditions: create_and_init_boundary_distributions, create_pdf +using moment_kinetics.input_structs: advance_info +using moment_kinetics.time_advance: setup_runge_kutta_coefficients function get_vth(pres,dens,mass) return sqrt(pres/(dens*mass)) @@ -133,18 +132,32 @@ if abspath(PROGRAM_FILE) == @__FILE__ nvz = 1 dt = 1.0 adv_input = advection_input("default", 1.0, 0.0, 0.0) + vr_input = grid_input("vr", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) + vz_input = grid_input("vz", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) + vzeta_input = grid_input("vzeta", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) r_input = grid_input("r", 1, 1, 1, 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) z_input = grid_input("z", 1, 1, 1, 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) + vr = define_coordinate(vr_input) + vz = define_coordinate(vz_input) + vzeta = define_coordinate(vzeta_input) r = define_coordinate(r_input) z = define_coordinate(z_input) composition = species_composition(n_ion_species, n_ion_species, n_neutral_species, boltzmann_electron_response, false, 1:n_ion_species, n_ion_species+1:n_ion_species, 1.0, 1.0, 1.0, 0.0, 0.0, false, 1.0, 1.0, 0.0, allocate_float(n_ion_species)) nuii = 1.0 - collisions = collisions_input(0.0, 0.0, false, nuii, 0.0, 0.0) - + collisions = collisions_input(0.0, 0.0, false, nuii, 0.0, 0.0, "none") + rk_coefs = setup_runge_kutta_coefficients(4) + advance = advance_info(false, false, false, false, false, + false, false, false, false, false, false, + false, false, false, false, rk_coefs, + false, false, true, true, false, false) + # Set up MPI if standalone initialize_comms!() @@ -227,16 +240,30 @@ if abspath(PROGRAM_FILE) == @__FILE__ nussp,vpa,vperp,ivpa,ivperp) end end + vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) + # initialise the vpa advection speed + begin_s_r_z_vperp_region() + begin_serial_region() + z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) + r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) + pdf_unused = create_pdf(vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species) + boundary_distributions = create_and_init_boundary_distributions(pdf_unused, vz, vr, vzeta, vpa, vperp, z, r, composition) + dSdt = 0.0 # initialise the weights fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) # evaluate the collision operator - explicit_fokker_planck_collisions!(fs_out,fs_in,composition,collisions,dt,fokkerplanck_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) + explicit_fokker_planck_collisions!(fs_out,fs_in,dSdt,composition,collisions,dt,fokkerplanck_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, + boundary_distributions, advance, + vpa_advect, z_advect, r_advect, + diagnose_entropy_production = false) + fka = fokkerplanck_arrays # error analysis of distribution function begin_serial_region() @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) max_dfsdvpa_err = maximum(dfsdvpa_err) println("max_dfsdvpa_err: ",max_dfsdvpa_err) @@ -263,7 +290,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ plot_d2Gdvpa2 = false #true @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) @. Cssp_err = abs(fka.Cssp_result_vpavperp - Cssp_Maxwell) max_C_err, max_C_index = findmax(Cssp_err) println("max_C_err: ",max_C_err," ",max_C_index) @@ -396,14 +422,15 @@ if abspath(PROGRAM_FILE) == @__FILE__ end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 9 + ngrid = 5 plot_scan = true #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[2, 4, 8] + nelement_list = Int[2, 4] #nelement_list = Int[100] - nelement_list = Int[2,4,8,16] + #nelement_list = Int[2,4,8,16] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index abd35644c..8e8866d75 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -7,19 +7,8 @@ module fokker_planck export init_fokker_planck_collisions, fokkerplanck_arrays_struct export init_fokker_planck_collisions_new export explicit_fokker_planck_collisions! -export calculate_Rosenbluth_potentials! -export calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients -export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -export calculate_Rosenbluth_H_from_G! - -export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs -export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian -export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian -export H_Maxwellian, G_Maxwellian -export boundary_integration_weights_struct, fokkerplanck_boundary_data_arrays_struct -export Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions -export get_element_limit_indices +export calculate_Maxwellian_Rosenbluth_coefficients +export get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing export symmetric_matrix_inverse @@ -34,38 +23,11 @@ using ..velocity_moments: integrate_over_vspace using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom using ..calculus: derivative!, second_derivative! using ..looping -""" -a struct of dummy arrays and precalculated coefficients -for the Fokker-Planck collision operator -""" - -struct fokkerplanck_arrays_struct - G0_weights::MPISharedArray{mk_float,4} - G1_weights::MPISharedArray{mk_float,4} - H0_weights::MPISharedArray{mk_float,4} - H1_weights::MPISharedArray{mk_float,4} - H2_weights::MPISharedArray{mk_float,4} - H3_weights::MPISharedArray{mk_float,4} - #Rosenbluth_G::Array{mk_float,2} - d2Gdvpa2::MPISharedArray{mk_float,2} - d2Gdvperpdvpa::MPISharedArray{mk_float,2} - d2Gdvperp2::MPISharedArray{mk_float,2} - dGdvperp::MPISharedArray{mk_float,2} - #Rosenbluth_H::Array{mk_float,2} - dHdvpa::MPISharedArray{mk_float,2} - dHdvperp::MPISharedArray{mk_float,2} - #Cflux_vpa::MPISharedArray{mk_float,2} - #Cflux_vperp::MPISharedArray{mk_float,2} - buffer_vpavperp_1::Array{mk_float,2} - buffer_vpavperp_2::Array{mk_float,2} - Cssp_result_vpavperp::MPISharedArray{mk_float,2} - dfdvpa::MPISharedArray{mk_float,2} - d2fdvpa2::MPISharedArray{mk_float,2} - d2fdvperpdvpa::MPISharedArray{mk_float,2} - dfdvperp::MPISharedArray{mk_float,2} - d2fdvperp2::MPISharedArray{mk_float,2} -end - +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 +using ..fokker_planck_calculus: fokkerplanck_arrays_struct +using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes """ allocate the required ancilliary arrays """ @@ -107,60 +69,6 @@ function allocate_fokkerplanck_arrays(vperp,vpa) end -""" -a struct to contain the integration weights for the boundary points -in the (vpa,vperp) domain -""" -struct boundary_integration_weights_struct - lower_vpa_boundary::MPISharedArray{mk_float,3} - upper_vpa_boundary::MPISharedArray{mk_float,3} - upper_vperp_boundary::MPISharedArray{mk_float,3} -end - -""" -a struct used for calculating the integration weights for the -boundary of the velocity space domain in (vpa,vperp) coordinates -""" -struct fokkerplanck_boundary_data_arrays_struct - G0_weights::boundary_integration_weights_struct - G1_weights::boundary_integration_weights_struct - H0_weights::boundary_integration_weights_struct - H1_weights::boundary_integration_weights_struct - H2_weights::boundary_integration_weights_struct - H3_weights::boundary_integration_weights_struct - dfdvpa::MPISharedArray{mk_float,2} - d2fdvperpdvpa::MPISharedArray{mk_float,2} - dfdvperp::MPISharedArray{mk_float,2} -end - - -function allocate_boundary_integration_weight(vpa,vperp) - nvpa = vpa.n - nvperp = vperp.n - lower_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) - upper_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) - upper_vperp_boundary = allocate_shared_float(nvpa,nvperp,nvpa) - return boundary_integration_weights_struct(lower_vpa_boundary, - upper_vpa_boundary, upper_vperp_boundary) -end - -function allocate_boundary_integration_weights(vpa,vperp) - G0_weights = allocate_boundary_integration_weight(vpa,vperp) - G1_weights = allocate_boundary_integration_weight(vpa,vperp) - H0_weights = allocate_boundary_integration_weight(vpa,vperp) - H1_weights = allocate_boundary_integration_weight(vpa,vperp) - H2_weights = allocate_boundary_integration_weight(vpa,vperp) - H3_weights = allocate_boundary_integration_weight(vpa,vperp) - nvpa = vpa.n - nvperp = vperp.n - dfdvpa = allocate_shared_float(nvpa,nvperp) - d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) - dfdvperp = allocate_shared_float(nvpa,nvperp) - return fokkerplanck_boundary_data_arrays_struct(G0_weights, - G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - dfdvpa,d2fdvperpdvpa,dfdvperp) -end - """ function that initialises the arrays needed for Fokker Planck collisions @@ -191,619 +99,6 @@ function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false) return fka end -""" -function that precomputes the required integration weights -""" -function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) - - x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) - - @serial_region begin - println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # precalculated weights, integrating over Lagrange polynomials - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - for ivperpp in 1:vperp.n - for ivpap in 1:vpa.n - G0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - G1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - end - end - # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G0_weights[:,:,ivpa,ivperp],G1_weights[:,:,ivpa,ivperp], - H0_weights[:,:,ivpa,ivperp],H1_weights[:,:,ivpa,ivperp], - H2_weights[:,:,ivpa,ivperp],H3_weights[:,:,ivpa,ivperp], - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - end - - - @serial_region begin - println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - return nothing -end - -""" -function for getting the basic quadratures used for the -numerical integration of the Lagrange polynomials and the -Green's function. -""" -function setup_basic_quadratures(vpa,vperp) - @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # get Gauss-Legendre points and weights on (-1,1) - ngrid = max(vpa.ngrid,vperp.ngrid) - nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) - #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries - nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - - x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - - return x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre -end - - -""" -function for getting the indices used to choose the integration -quadrature -""" -function get_element_limit_indices(ivpa,ivperp,vpa,vperp) - nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid - nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] - ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) - ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) - igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] - ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) - ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) - return igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, - igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi -end -""" -function that precomputes the required integration weights -only along the velocity space boundaries -""" -function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, - G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp) - - x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) - - @serial_region begin - println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # precalculate weights, integrating over Lagrange polynomials - # first compute weights along lower vpa boundary - begin_vperp_region() - ivpa = 1 # lower_vpa_boundary - @loop_vperp ivperp begin - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - for ivperpp in 1:vperp.n - for ivpap in 1:vpa.n - G0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - G1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H2_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H3_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - end - end - # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G0_weights.lower_vpa_boundary[:,:,ivperp], - G1_weights.lower_vpa_boundary[:,:,ivperp], - H0_weights.lower_vpa_boundary[:,:,ivperp], - H1_weights.lower_vpa_boundary[:,:,ivperp], - H2_weights.lower_vpa_boundary[:,:,ivperp], - H3_weights.lower_vpa_boundary[:,:,ivperp], - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - end - # second compute weights along upper vpa boundary - ivpa = vpa.n # upper_vpa_boundary - @loop_vperp ivperp begin - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - for ivperpp in 1:vperp.n - for ivpap in 1:vpa.n - G0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - G1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H2_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - H3_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 - #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - end - end - # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G0_weights.upper_vpa_boundary[:,:,ivperp], - G1_weights.upper_vpa_boundary[:,:,ivperp], - H0_weights.upper_vpa_boundary[:,:,ivperp], - H1_weights.upper_vpa_boundary[:,:,ivperp], - H2_weights.upper_vpa_boundary[:,:,ivperp], - H3_weights.upper_vpa_boundary[:,:,ivperp], - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - end - # finally compute weight along upper vperp boundary - begin_vpa_region() - ivperp = vperp.n # upper_vperp_boundary - @loop_vpa ivpa begin - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - for ivperpp in 1:vperp.n - for ivpap in 1:vpa.n - G0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - G1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - H0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - H1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - H2_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - H3_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 - #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 - end - end - # loop over elements and grid points within elements on primed coordinate - @views loop_over_vperp_vpa_elements!(G0_weights.upper_vperp_boundary[:,:,ivpa], - G1_weights.upper_vperp_boundary[:,:,ivpa], - H0_weights.upper_vperp_boundary[:,:,ivpa], - H1_weights.upper_vperp_boundary[:,:,ivpa], - H2_weights.upper_vperp_boundary[:,:,ivpa], - H3_weights.upper_vperp_boundary[:,:,ivpa], - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - end - # return the parallelisation status to serial - begin_serial_region() - @serial_region begin - println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - return nothing -end - -function get_imin_imax(coord,iel) - j = iel - if j > 1 - k = 1 - else - k = 0 - end - imin = coord.imin[j] - k - imax = coord.imax[j] - return imin, imax -end - -function get_nodes(coord,iel) - # get imin and imax of this element on full grid - (imin, imax) = get_imin_imax(coord,iel) - nodes = coord.grid[imin:imax] - return nodes -end -""" -Lagrange polynomial -args: -j - index of l_j from list of nodes -x_nodes - array of x node values -x - point where interpolated value is returned -""" -function lagrange_poly(j,x_nodes,x) - # get number of nodes - n = size(x_nodes,1) - # location where l(x0) = 1 - x0 = x_nodes[j] - # evaluate polynomial - poly = 1.0 - for i in 1:j-1 - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - for i in j+1:n - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - return poly -end - -function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) - #println("nodes ",nodes) - zero = 1.0e-10 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - nnodes = size(nodes,1) - nquad_legendre = size(x_legendre,1) - nquad_laguerre = size(x_laguerre,1) - # assume x_scaled, w_scaled are arrays of length 2*nquad - # use only nquad points for most elements, but use 2*nquad for - # elements with interior divergences - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - if abs(coord_val - node_max) < zero # divergence at upper endpoint - node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 - - n = nquad_laguerre + nquad_legendre - shift = 0.5*(node_min + node_cut) - scale = 0.5*(node_cut - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - - @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) - @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre - - nquad_coord = n - #println("upper divergence") - elseif abs(coord_val - node_min) < zero # divergence at lower endpoint - n = nquad_laguerre + nquad_legendre - nquad = size(x_laguerre,1) - node_cut = (nodes[1] + nodes[2])/2.0 - for j in 1:nquad_laguerre - x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) - w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] - end - shift = 0.5*(node_max + node_cut) - scale = 0.5*(node_max - node_cut) - @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift - @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre - - nquad_coord = n - #println("lower divergence") - else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - #println(nodes[igrid_coord]," ", coord_val) - n = 2*nquad_laguerre - node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 - if igrid_coord == 1 - # exception for vperp coordinate near orgin - k = 0 - node_cut_low = node_min - nquad_coord = nquad_legendre + 2*nquad_laguerre - else - # fill in lower Gauss-Legendre points - node_cut_low = (nodes[igrid_coord-1]+nodes[igrid_coord])/2.0 - shift = 0.5*(node_cut_low + node_min) - scale = 0.5*(node_cut_low - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - k = nquad_legendre - nquad_coord = 2*(nquad_laguerre + nquad_legendre) - end - # lower half of domain - for j in 1:nquad_laguerre - x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] - end - # upper half of domain - for j in 1:nquad_laguerre - x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] - end - # fill in upper Gauss-Legendre points - shift = 0.5*(node_cut_high + node_max) - scale = 0.5*(node_max - node_cut_high) - @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift - @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre - - #println("intermediate divergence") - #else # no divergences - # nquad = size(x_legendre,1) - # shift = 0.5*(node_min + node_max) - # scale = 0.5*(node_max - node_min) - # @. x_scaled[1:nquad] = scale*x_legendre + shift - # @. w_scaled[1:nquad] = scale*w_legendre - # #println("no divergence") - # nquad_coord = nquad - end - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) - return nquad_coord -end - -function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) - zero = 1.0e-6 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - nquad = size(x_legendre,1) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled[1:nquad] = scale*x_legendre + shift - @. w_scaled[1:nquad] = scale*w_legendre - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) - return nquad -end - -# function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid -function ng_low(igrid,ngrid) - return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) -end -# function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid -function ng_hi(igrid,ngrid) - return floor(mk_int, igrid/ngrid) -end -# function returns 1 for nelement >= ielement > 1, 0 for ielement =1 -function nel_low(ielement,nelement) - return floor(mk_int, (ielement - 2 + nelement)/nelement) -end -# function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement -function nel_hi(ielement,nelement) - return 1- floor(mk_int, ielement/nelement) -end - -# base level function for computing the Green's function weights -# note the definitions of ellipe & ellipk -# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` -# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipk` -# `ellipe(m) = \int^{\pi/2}\_0 \sqrt{ 1 - m \sin^2(\theta)} d \theta` -# `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` - -function local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids - nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids - vpa_val, vperp_val) # values and indices for unprimed (field) grids - for igrid_vperp in 1:vperp.ngrid - for igrid_vpa in 1:vpa.ngrid - # get grid index for point on full grid - ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad_vperp - for kvpa in 1:nquad_vpa - x_kvpa = x_vpa[kvpa] - x_kvperp = x_vperp[kvperp] - w_kvperp = w_vperp[kvperp] - w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - #mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) - #if mm_test > 1.0 - # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - #end - G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - - (G0_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G1_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - #(G2_weights[ivpap,ivperpp] += - # lagrange_poly_vpa*lagrange_poly_vperp* - # G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - #(G3_weights[ivpap,ivperpp] += - # lagrange_poly_vpa*lagrange_poly_vperp* - # G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H0_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H1_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H2_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*(vpa_val - x_kvpa)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - #(n_weights[ivpap,ivperpp] += - # lagrange_poly_vpa*lagrange_poly_vperp* - # x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - end - end - end - end - return nothing -end - -function loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - vperp,ielement_vperpp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in 1:ielement_vpa_low-1 - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val) - end - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - for ielement_vpap in ielement_vpa_low:ielement_vpa_hi - #for ielement_vpap in 1:vpa.nelement_local - # use general grid function that checks divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val) - end - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val) - - end - return nothing -end - -function loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre, - vpa_val, vperp_val) - for ielement_vpap in 1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val) - - end - return nothing -end - -function loop_over_vperp_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - for ielement_vperpp in 1:ielement_vperp_low-1 - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre, - vpa_val, vperp_val) - end - for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - - #vperp_nodes = get_nodes(vperp,ielement_vperpp) - #vperp_max = vperp_nodes[end] - #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - @views loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperpp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre,x_laguerre,w_laguerre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - end - for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre, - vpa_val, vperp_val) - end - return nothing -end - -function loop_over_vperp_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre, - igrid_vpa, igrid_vperp, vpa_val, vperp_val) - for ielement_vperpp in 1:vperp.nelement_local - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - x_legendre,w_legendre, - vpa_val, vperp_val) - end - return nothing -end - """ calculates the (normalised) Rosenbluth potential coefficients d2Gdvpa2, d2Gdvperpdvpa, ..., dHdvperp for a Maxwellian inputs. """ @@ -826,19 +121,6 @@ function calculate_Maxwellian_Rosenbluth_coefficients(dens,upar,vth,vpa,vperp,iv return Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp end -""" -calculates the collisional fluxes given input F_s and G_sp, H_sp -""" -function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, - d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dHdvpa,dHdvperp, - ms,msp) - # fill in value at (ivpa,ivperp) - Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa - 2.0*(ms/msp)*F*dHdvpa - #Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa # - 2.0*(ms/msp)*F*dHdvpa - #Cflux_vpa = - 2.0*(ms/msp)*F*dHdvpa - Cflux_vperp = dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp - return Cflux_vpa, Cflux_vperp -end """ Function to carry out the integration of the revelant @@ -874,25 +156,7 @@ function get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, end return nothing end -""" -Function calculating the fully expanded form of the collision operator -taking floats as arguments. This function is designed to be used at the -lowest level of a coordinate loop, with derivatives and integrals -all previously calculated. -""" -function Cssp_fully_expanded_form(nussp,ms,msp, - d2fsdvpa2,d2fsdvperp2,d2fsdvperpdvpa,dfsdvpa,dfsdvperp,fs, - d2Gspdvpa2,d2Gspdvperp2,d2Gspdvperpdvpa,dGspdvperp, - dHspdvpa,dHspdvperp,fsp,vperp_val) - ( Cssp = nussp*( d2fsdvpa2*d2Gspdvpa2 + - d2fsdvperp2*d2Gspdvperp2 + - 2.0*d2fsdvperpdvpa*d2Gspdvperpdvpa + - (1.0/(vperp_val^2))*dfsdvperp*dGspdvperp + - 2.0*(1.0 - (ms/msp))*(dfsdvpa*dHspdvpa + dfsdvperp*dHspdvperp) + - (8.0/sqrt(pi))*(ms/msp)*fs*fsp) ) - return Cssp -end """ Evaluate the Fokker Planck collision Operator @@ -1120,221 +384,6 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ end end -# below are a series of functions that can be used to test the calculation -# of the Rosenbluth potentials for a shifted Maxwellian -# or provide an estimate for collisional coefficients - -# G (defined by Del^4 G = -(8/sqrt(pi))*F -# with F = cref^3 pi^(3/2) F_Maxwellian / nref -# the normalised Maxwellian -function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - G = 2.0/sqrt(pi) - else - # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) - G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) - end - return G*dens*vth -end - -# H (defined by Del^2 H = -(4/sqrt(pi))*F -# with F = cref^3 pi^(3/2) F_Maxwellian / nref -# the normalised Maxwellian -function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 - H = 2.0/sqrt(pi) - else - # H_M = erf(eta)/eta - H = erf(eta)/eta - end - return H*dens/vth -end - -# 1D derivative functions - -function dGdeta(eta::mk_float) - # d \tilde{G} / d eta - dGdeta_fac = (1.0/sqrt(pi))*exp(-eta^2)/eta + (1.0 - 0.5/(eta^2))*erf(eta) - return dGdeta_fac -end - -function d2Gdeta2(eta::mk_float) - # d \tilde{G} / d eta - d2Gdeta2_fac = erf(eta)/(eta^3) - (2.0/sqrt(pi))*exp(-eta^2)/(eta^2) - return d2Gdeta2_fac -end - -function ddGddeta(eta::mk_float) - # d / d eta ( (1/ eta) d \tilde{G} d eta - ddGddeta_fac = (1.5/(eta^2) - 1.0)*erf(eta)/(eta^2) - (3.0/sqrt(pi))*exp(-eta^2)/(eta^3) - return ddGddeta_fac -end - -function dHdeta(eta::mk_float) - dHdeta_fac = (2.0/sqrt(pi))*(exp(-eta^2))/eta - erf(eta)/(eta^2) - return dHdeta_fac -end - -# functions of vpa & vperp -function eta_func(upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - speed = sqrt( (vpa.grid[ivpa] - upar)^2 + vperp.grid[ivperp]^2)/vth - return speed -end - -function d2Gdvpa2(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = dGdeta(eta) + ddGddeta(eta)*((vpa.grid[ivpa] - upar)^2)/(vth^2) - d2Gdvpa2_fac = fac*dens/(eta*vth) - return d2Gdvpa2_fac -end - -function d2Gdvperpdvpa(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = ddGddeta(eta)*vperp.grid[ivperp]*(vpa.grid[ivpa] - upar)/(vth^2) - d2Gdvperpdvpa_fac = fac*dens/(eta*vth) - return d2Gdvperpdvpa_fac -end - -function d2Gdvperp2(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = dGdeta(eta) + ddGddeta(eta)*(vperp.grid[ivperp]^2)/(vth^2) - d2Gdvperp2_fac = fac*dens/(eta*vth) - return d2Gdvperp2_fac -end - -function dGdvperp(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = dGdeta(eta)*vperp.grid[ivperp]*dens/(vth*eta) - return fac -end - -function dHdvperp(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = dHdeta(eta)*vperp.grid[ivperp]*dens/(eta*vth^3) - return fac -end - -function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = dHdeta(eta)*(vpa.grid[ivpa]-upar)*dens/(eta*vth^3) - return fac -end - -function F_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = (dens/(vth^3))*exp(-eta^2) - return fac -end - -function dFdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = -2.0*(dens/(vth^4))*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) - return fac -end - -function dFdvperp_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = -2.0*(dens/(vth^4))*(vperp.grid[ivperp]/vth)*exp(-eta^2) - return fac -end - -function d2Fdvperpdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = 4.0*(dens/(vth^5))*(vperp.grid[ivperp]/vth)*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) - return fac -end - -function d2Fdvpa2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = 4.0*(dens/(vth^5))*( ((vpa.grid[ivpa] - upar)/vth)^2 - 0.5 )*exp(-eta^2) - return fac -end - -function d2Fdvperp2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, - vpa,vperp,ivpa,ivperp) - eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) - fac = 4.0*(dens/(vth^5))*((vperp.grid[ivperp]/vth)^2 - 0.5)*exp(-eta^2) - return fac -end - -function Cssp_Maxwellian_inputs(denss::mk_float,upars::mk_float,vths::mk_float,ms::mk_float, - denssp::mk_float,uparsp::mk_float,vthsp::mk_float,msp::mk_float, - nussp::mk_float,vpa,vperp,ivpa,ivperp) - - d2Fsdvpa2 = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Fsdvperp2 = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Fsdvperpdvpa = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dFsdvperp = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dFsdvpa = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - Fs = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - d2Gspdvpa2 = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gspdvperp2 = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gspdvperpdvpa = d2Gdvperpdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dGspdvperp = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHspdvperp = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHspdvpa = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - Fsp = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - ( Cssp_Maxwellian = - d2Fsdvpa2*d2Gspdvpa2 + - d2Fsdvperp2*d2Gspdvperp2 + - 2.0*d2Fsdvperpdvpa*d2Gspdvperpdvpa + - (1.0/(vperp.grid[ivperp]^2))*dFsdvperp*dGspdvperp + - 2.0*(1.0 - (ms/msp))*(dFsdvpa*dHspdvpa + dFsdvperp*dHspdvperp) + - (8.0/sqrt(pi))*(ms/msp)*Fs*Fsp ) - - Cssp_Maxwellian *= nussp - return Cssp_Maxwellian -end - -function Cflux_vpa_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, - msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, - vpa,vperp,ivpa,ivperp) - etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) - eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) - prefac = -2.0*denss*denssp*exp( -eta^2)/(vthsp*vths^5) - (fac = (vpa.grid[ivpa]-uparsp)*(d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) - + (uparsp - upars)*( dGdeta(etap) + ((vpa.grid[ivpa]-uparsp)^2/vthsp^2)*ddGddeta(etap) )/etap ) - Cflux = prefac*fac - #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap - #fac *= d2Gdeta2(etap) - return Cflux -end - -function Cflux_vperp_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, - msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, - vpa,vperp,ivpa,ivperp) - etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) - eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) - prefac = -2.0*(vperp.grid[ivperp])*denss*denssp*exp( -eta^2)/(vthsp*vths^5) - (fac = (d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) - + ((uparsp - upars)*(vpa.grid[ivpa]-uparsp)/vthsp^2)*ddGddeta(etap)/etap ) - Cflux = prefac*fac - #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap - #fac *= d2Gdeta2(etap) - return Cflux -end - # solves A x = b for a matrix of the form # A00 0 A02 # 0 A11 A12 diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl new file mode 100644 index 000000000..061d61b0c --- /dev/null +++ b/src/fokker_planck_calculus.jl @@ -0,0 +1,1993 @@ +""" +module for functions used +in calculating the integrals and doing +the numerical differentiation for +the implementation of the +the Full-F Fokker-Planck Collision Operator +""" +module fokker_planck_calculus + +export assemble_matrix_operators_dirichlet_bc +export assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient +export assemble_matrix_operators_dirichlet_bc_sparse +export assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse +export assemble_explicit_collision_operator_rhs_serial! +export assemble_explicit_collision_operator_rhs_parallel! +export YY_collision_operator_arrays, calculate_YY_arrays +export calculate_rosenbluth_potential_boundary_data! +export elliptic_solve! +export fokkerplanck_arrays_struct + +# testing +export calculate_rosenbluth_potential_boundary_data_exact! +export ravel_c_to_vpavperp!, ravel_vpavperp_to_c! +export enforce_zero_bc!, ravel_c_to_vpavperp_parallel! +export allocate_rosenbluth_potential_boundary_data +export calculate_rosenbluth_potential_boundary_data_exact! +export test_rosenbluth_potential_boundary_data + +using ..type_definitions: mk_float, mk_int +using ..array_allocation: allocate_float, allocate_shared_float +using ..calculus: derivative! +using ..communication +using ..communication: MPISharedArray +using ..looping +using moment_kinetics.gauss_legendre: get_QQ_local! +using Dates +using SpecialFunctions: ellipk, ellipe +using SparseArrays: sparse +using LinearAlgebra: mul! +using FastGaussQuadrature +using Printf + +function print_matrix(matrix,name::String,n::mk_int,m::mk_int) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.2f ", matrix[i,j]) + end + println("") + end + println("\n") +end + +function print_vector(vector,name::String,m::mk_int) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") +end + +""" +a struct of dummy arrays and precalculated coefficients +for the Fokker-Planck collision operator +""" + +struct fokkerplanck_arrays_struct + G0_weights::MPISharedArray{mk_float,4} + G1_weights::MPISharedArray{mk_float,4} + H0_weights::MPISharedArray{mk_float,4} + H1_weights::MPISharedArray{mk_float,4} + H2_weights::MPISharedArray{mk_float,4} + H3_weights::MPISharedArray{mk_float,4} + #Rosenbluth_G::Array{mk_float,2} + d2Gdvpa2::MPISharedArray{mk_float,2} + d2Gdvperpdvpa::MPISharedArray{mk_float,2} + d2Gdvperp2::MPISharedArray{mk_float,2} + dGdvperp::MPISharedArray{mk_float,2} + #Rosenbluth_H::Array{mk_float,2} + dHdvpa::MPISharedArray{mk_float,2} + dHdvperp::MPISharedArray{mk_float,2} + #Cflux_vpa::MPISharedArray{mk_float,2} + #Cflux_vperp::MPISharedArray{mk_float,2} + buffer_vpavperp_1::Array{mk_float,2} + buffer_vpavperp_2::Array{mk_float,2} + Cssp_result_vpavperp::MPISharedArray{mk_float,2} + dfdvpa::MPISharedArray{mk_float,2} + d2fdvpa2::MPISharedArray{mk_float,2} + d2fdvperpdvpa::MPISharedArray{mk_float,2} + dfdvperp::MPISharedArray{mk_float,2} + d2fdvperp2::MPISharedArray{mk_float,2} +end + + +""" +a struct to contain the integration weights for the boundary points +in the (vpa,vperp) domain +""" +struct boundary_integration_weights_struct + lower_vpa_boundary::MPISharedArray{mk_float,3} + upper_vpa_boundary::MPISharedArray{mk_float,3} + upper_vperp_boundary::MPISharedArray{mk_float,3} +end + +""" +a struct used for calculating the integration weights for the +boundary of the velocity space domain in (vpa,vperp) coordinates +""" +struct fokkerplanck_boundary_data_arrays_struct + G0_weights::boundary_integration_weights_struct + G1_weights::boundary_integration_weights_struct + H0_weights::boundary_integration_weights_struct + H1_weights::boundary_integration_weights_struct + H2_weights::boundary_integration_weights_struct + H3_weights::boundary_integration_weights_struct + dfdvpa::MPISharedArray{mk_float,2} + d2fdvperpdvpa::MPISharedArray{mk_float,2} + dfdvperp::MPISharedArray{mk_float,2} +end + + +function allocate_boundary_integration_weight(vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + lower_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) + upper_vpa_boundary = allocate_shared_float(nvpa,nvperp,nvperp) + upper_vperp_boundary = allocate_shared_float(nvpa,nvperp,nvpa) + return boundary_integration_weights_struct(lower_vpa_boundary, + upper_vpa_boundary, upper_vperp_boundary) +end + +function allocate_boundary_integration_weights(vpa,vperp) + G0_weights = allocate_boundary_integration_weight(vpa,vperp) + G1_weights = allocate_boundary_integration_weight(vpa,vperp) + H0_weights = allocate_boundary_integration_weight(vpa,vperp) + H1_weights = allocate_boundary_integration_weight(vpa,vperp) + H2_weights = allocate_boundary_integration_weight(vpa,vperp) + H3_weights = allocate_boundary_integration_weight(vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + dfdvpa = allocate_shared_float(nvpa,nvperp) + d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) + dfdvperp = allocate_shared_float(nvpa,nvperp) + return fokkerplanck_boundary_data_arrays_struct(G0_weights, + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + dfdvpa,d2fdvperpdvpa,dfdvperp) +end + + +""" +function that precomputes the required integration weights +""" +function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) + + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) + + @serial_region begin + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # precalculated weights, integrating over Lagrange polynomials + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + G1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H1_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights[:,:,ivpa,ivperp],G1_weights[:,:,ivpa,ivperp], + H0_weights[:,:,ivpa,ivperp],H1_weights[:,:,ivpa,ivperp], + H2_weights[:,:,ivpa,ivperp],H3_weights[:,:,ivpa,ivperp], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + + + @serial_region begin + println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + return nothing +end + +""" +function for getting the basic quadratures used for the +numerical integration of the Lagrange polynomials and the +Green's function. +""" +function setup_basic_quadratures(vpa,vperp) + @serial_region begin + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # get Gauss-Legendre points and weights on (-1,1) + ngrid = max(vpa.ngrid,vperp.ngrid) + nquad = 2*ngrid + x_legendre, w_legendre = gausslegendre(nquad) + #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries + nlaguerre = nquad + x_laguerre, w_laguerre = gausslaguerre(nlaguerre) + + x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) + + return x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre +end + + +""" +function for getting the indices used to choose the integration +quadrature +""" +function get_element_limit_indices(ivpa,ivperp,vpa,vperp) + nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid + nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] + ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) + ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) + #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) + igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] + ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) + ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) + #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) + return igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, + igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi +end +""" +function that precomputes the required integration weights +only along the velocity space boundaries +""" +function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp) + + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) + + @serial_region begin + println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # precalculate weights, integrating over Lagrange polynomials + # first compute weights along lower vpa boundary + begin_vperp_region() + ivpa = 1 # lower_vpa_boundary + @loop_vperp ivperp begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + G1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H1_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H2_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H3_weights.lower_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights.lower_vpa_boundary[:,:,ivperp], + G1_weights.lower_vpa_boundary[:,:,ivperp], + H0_weights.lower_vpa_boundary[:,:,ivperp], + H1_weights.lower_vpa_boundary[:,:,ivperp], + H2_weights.lower_vpa_boundary[:,:,ivperp], + H3_weights.lower_vpa_boundary[:,:,ivperp], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + # second compute weights along upper vpa boundary + ivpa = vpa.n # upper_vpa_boundary + @loop_vperp ivperp begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + G1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H1_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H2_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + H3_weights.upper_vpa_boundary[ivpap,ivperpp,ivperp] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights.upper_vpa_boundary[:,:,ivperp], + G1_weights.upper_vpa_boundary[:,:,ivperp], + H0_weights.upper_vpa_boundary[:,:,ivperp], + H1_weights.upper_vpa_boundary[:,:,ivperp], + H2_weights.upper_vpa_boundary[:,:,ivperp], + H3_weights.upper_vpa_boundary[:,:,ivperp], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + # finally compute weight along upper vperp boundary + begin_vpa_region() + ivperp = vperp.n # upper_vperp_boundary + @loop_vpa ivpa begin + #limits where checks required to determine which divergence-safe grid is needed + igrid_vpa, ielement_vpa, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperp, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa,ivperp,vpa,vperp) + + vperp_val = vperp.grid[ivperp] + vpa_val = vpa.grid[ivpa] + for ivperpp in 1:vperp.n + for ivpap in 1:vpa.n + G0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + G1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + # G2_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + # G3_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + H0_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H1_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H2_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + H3_weights.upper_vperp_boundary[ivpap,ivperpp,ivpa] = 0.0 + #@. n_weights[ivpap,ivperpp,ivpa,ivperp] = 0.0 + end + end + # loop over elements and grid points within elements on primed coordinate + @views loop_over_vperp_vpa_elements!(G0_weights.upper_vperp_boundary[:,:,ivpa], + G1_weights.upper_vperp_boundary[:,:,ivpa], + H0_weights.upper_vperp_boundary[:,:,ivpa], + H1_weights.upper_vperp_boundary[:,:,ivpa], + H2_weights.upper_vperp_boundary[:,:,ivpa], + H3_weights.upper_vperp_boundary[:,:,ivpa], + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + # return the parallelisation status to serial + begin_serial_region() + @serial_region begin + println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + return nothing +end + +function get_imin_imax(coord,iel) + j = iel + if j > 1 + k = 1 + else + k = 0 + end + imin = coord.imin[j] - k + imax = coord.imax[j] + return imin, imax +end + +function get_nodes(coord,iel) + # get imin and imax of this element on full grid + (imin, imax) = get_imin_imax(coord,iel) + nodes = coord.grid[imin:imax] + return nodes +end +""" +Lagrange polynomial +args: +j - index of l_j from list of nodes +x_nodes - array of x node values +x - point where interpolated value is returned +""" +function lagrange_poly(j,x_nodes,x) + # get number of nodes + n = size(x_nodes,1) + # location where l(x0) = 1 + x0 = x_nodes[j] + # evaluate polynomial + poly = 1.0 + for i in 1:j-1 + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + for i in j+1:n + poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) + end + return poly +end + +function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) + #println("nodes ",nodes) + zero = 1.0e-10 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + nnodes = size(nodes,1) + nquad_legendre = size(x_legendre,1) + nquad_laguerre = size(x_laguerre,1) + # assume x_scaled, w_scaled are arrays of length 2*nquad + # use only nquad points for most elements, but use 2*nquad for + # elements with interior divergences + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + if abs(coord_val - node_max) < zero # divergence at upper endpoint + node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 + + n = nquad_laguerre + nquad_legendre + shift = 0.5*(node_min + node_cut) + scale = 0.5*(node_cut - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + + @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) + @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre + + nquad_coord = n + #println("upper divergence") + elseif abs(coord_val - node_min) < zero # divergence at lower endpoint + n = nquad_laguerre + nquad_legendre + nquad = size(x_laguerre,1) + node_cut = (nodes[1] + nodes[2])/2.0 + for j in 1:nquad_laguerre + x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) + w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] + end + shift = 0.5*(node_max + node_cut) + scale = 0.5*(node_max - node_cut) + @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift + @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre + + nquad_coord = n + #println("lower divergence") + else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence + #println(nodes[igrid_coord]," ", coord_val) + n = 2*nquad_laguerre + node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 + if igrid_coord == 1 + # exception for vperp coordinate near orgin + k = 0 + node_cut_low = node_min + nquad_coord = nquad_legendre + 2*nquad_laguerre + else + # fill in lower Gauss-Legendre points + node_cut_low = (nodes[igrid_coord-1]+nodes[igrid_coord])/2.0 + shift = 0.5*(node_cut_low + node_min) + scale = 0.5*(node_cut_low - node_min) + @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift + @. w_scaled[1:nquad_legendre] = scale*w_legendre + k = nquad_legendre + nquad_coord = 2*(nquad_laguerre + nquad_legendre) + end + # lower half of domain + for j in 1:nquad_laguerre + x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] + end + # upper half of domain + for j in 1:nquad_laguerre + x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) + w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] + end + # fill in upper Gauss-Legendre points + shift = 0.5*(node_cut_high + node_max) + scale = 0.5*(node_max - node_cut_high) + @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift + @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre + + #println("intermediate divergence") + #else # no divergences + # nquad = size(x_legendre,1) + # shift = 0.5*(node_min + node_max) + # scale = 0.5*(node_max - node_min) + # @. x_scaled[1:nquad] = scale*x_legendre + shift + # @. w_scaled[1:nquad] = scale*w_legendre + # #println("no divergence") + # nquad_coord = nquad + end + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad_coord +end + +function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) + zero = 1.0e-6 + @. x_scaled = 0.0 + @. w_scaled = 0.0 + #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) + nquad = size(x_legendre,1) + shift = 0.5*(node_min + node_max) + scale = 0.5*(node_max - node_min) + @. x_scaled[1:nquad] = scale*x_legendre + shift + @. w_scaled[1:nquad] = scale*w_legendre + #println("x_scaled",x_scaled) + #println("w_scaled",w_scaled) + return nquad +end + +# function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid +function ng_low(igrid,ngrid) + return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) +end +# function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid +function ng_hi(igrid,ngrid) + return floor(mk_int, igrid/ngrid) +end +# function returns 1 for nelement >= ielement > 1, 0 for ielement =1 +function nel_low(ielement,nelement) + return floor(mk_int, (ielement - 2 + nelement)/nelement) +end +# function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement +function nel_hi(ielement,nelement) + return 1- floor(mk_int, ielement/nelement) +end + +# base level function for computing the Green's function weights +# note the definitions of ellipe & ellipk +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipe` +# `https://specialfunctions.juliamath.org/stable/functions_list/#SpecialFunctions.ellipk` +# `ellipe(m) = \int^{\pi/2}\_0 \sqrt{ 1 - m \sin^2(\theta)} d \theta` +# `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` + +function local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids + vpa_val, vperp_val) # values and indices for unprimed (field) grids + for igrid_vperp in 1:vperp.ngrid + for igrid_vpa in 1:vpa.ngrid + # get grid index for point on full grid + ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] + ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] + # carry out integration over Lagrange polynomial at this node, on this element + for kvperp in 1:nquad_vperp + for kvpa in 1:nquad_vpa + x_kvpa = x_vpa[kvpa] + x_kvperp = x_vperp[kvperp] + w_kvperp = w_vperp[kvperp] + w_kvpa = w_vpa[kvpa] + denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 + mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) + #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) + #mm = 4.0*vperp_val*x_kvperp/denom + prefac = sqrt(denom) + ellipe_mm = ellipe(mm) + ellipk_mm = ellipk(mm) + #if mm_test > 1.0 + # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) + #end + G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi + G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) + #G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + #G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) + H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) + H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) + H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) + lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) + lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) + + (G0_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (G1_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(G2_weights[ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(G3_weights[ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H0_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H1_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + (H2_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + (H3_weights[ivpap,ivperpp] += + lagrange_poly_vpa*lagrange_poly_vperp* + H_elliptic_integral_factor*(vpa_val - x_kvpa)* + x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + + #(n_weights[ivpap,ivperpp] += + # lagrange_poly_vpa*lagrange_poly_vperp* + # x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) + end + end + end + end + return nothing +end + +function loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + vperp,ielement_vperpp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + for ielement_vpap in 1:ielement_vpa_low-1 + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val) + end + nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + for ielement_vpap in ielement_vpa_low:ielement_vpa_hi + #for ielement_vpap in 1:vpa.nelement_local + # use general grid function that checks divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val) + end + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val) + + end + return nothing +end + +function loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val) + for ielement_vpap in 1:vpa.nelement_local + # do integration over part of the domain with no divergences + vpa_nodes = get_nodes(vpa,ielement_vpap) + vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] + nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) + @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nquad_vpa,ielement_vpap,vpa_nodes,vpa, + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, + x_vpa, w_vpa, x_vperp, w_vperp, + vpa_val, vperp_val) + + end + return nothing +end + +function loop_over_vperp_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + for ielement_vperpp in 1:ielement_vperp_low-1 + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val) + end + for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi + + #vperp_nodes = get_nodes(vperp,ielement_vperpp) + #vperp_max = vperp_nodes[end] + #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + @views loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperpp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre,x_laguerre,w_laguerre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + end + for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local + + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val) + end + return nothing +end + +function loop_over_vperp_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + igrid_vpa, igrid_vperp, vpa_val, vperp_val) + for ielement_vperpp in 1:vperp.nelement_local + vperp_nodes = get_nodes(vperp,ielement_vperpp) + vperp_max = vperp_nodes[end] + vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) + nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) + @views loop_over_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids + nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids + x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids + x_legendre,w_legendre, + vpa_val, vperp_val) + end + return nothing +end + + +# Array indices for compound 1D form +function ic_func(ivpa::mk_int,ivperp::mk_int,nvpa::mk_int) + return ivpa + nvpa*(ivperp-1) +end +function ivperp_func(ic::mk_int,nvpa::mk_int) + return floor(Int64,(ic-1)/nvpa) + 1 +end +function ivpa_func(ic::mk_int,nvpa::mk_int) + ivpa = ic - nvpa*(ivperp_func(ic,nvpa) - 1) + return ivpa +end + +function ravel_vpavperp_to_c!(pdf_c,pdf_vpavperp,nvpa::mk_int,nvperp::mk_int) + for ivperp in 1:nvperp + for ivpa in 1:nvpa + ic = ic_func(ivpa,ivperp,nvpa) + pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] + end + end + return nothing +end + +function ravel_vpavperp_to_c_parallel!(pdf_c,pdf_vpavperp,nvpa::mk_int) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,nvpa) + pdf_c[ic] = pdf_vpavperp[ivpa,ivperp] + end + return nothing +end + +function ravel_c_to_vpavperp!(pdf_vpavperp,pdf_c,nc::mk_int,nvpa::mk_int) + for ic in 1:nc + ivpa = ivpa_func(ic,nvpa) + ivperp = ivperp_func(ic,nvpa) + pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] + end + return nothing +end + +function ravel_c_to_vpavperp_parallel!(pdf_vpavperp,pdf_c,nvpa::mk_int) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,nvpa) + pdf_vpavperp[ivpa,ivperp] = pdf_c[ic] + end + return nothing +end + +function ivpa_global_func(ivpa_local::mk_int,ielement_vpa::mk_int,ngrid_vpa::mk_int) + ivpa_global = ivpa_local + (ielement_vpa - 1)*(ngrid_vpa - 1) + return ivpa_global +end + +# function that returns the sparse matrix index +# used to directly construct the nonzero entries +# of a 2D assembled sparse matrix +function icsc_func(ivpa_local::mk_int,ivpap_local::mk_int, + ielement_vpa::mk_int, + ngrid_vpa::mk_int,nelement_vpa::mk_int, + ivperp_local::mk_int,ivperpp_local::mk_int, + ielement_vperp::mk_int, + ngrid_vperp::mk_int,nelement_vperp::mk_int) + ntot_vpa = (nelement_vpa - 1)*(ngrid_vpa^2 - 1) + ngrid_vpa^2 + ntot_vperp = (nelement_vperp - 1)*(ngrid_vperp^2 - 1) + ngrid_vperp^2 + + icsc_vpa = ((ivpap_local - 1) + (ivpa_local - 1)*ngrid_vpa + + (ielement_vpa - 1)*(ngrid_vpa^2 - 1)) + icsc_vperp = ((ivperpp_local - 1) + (ivperp_local - 1)*ngrid_vperp + + (ielement_vperp - 1)*(ngrid_vperp^2 - 1)) + icsc = 1 + icsc_vpa + ntot_vpa*icsc_vperp + return icsc +end + +struct sparse_matrix_constructor + # the Ith row + II::Array{mk_float,1} + # the Jth column + JJ::Array{mk_float,1} + # the data S[I,J] + SS::Array{mk_float,1} +end + +function allocate_sparse_matrix_constructor(nsparse::mk_int) + II = Array{mk_int,1}(undef,nsparse) + @. II = 0 + JJ = Array{mk_int,1}(undef,nsparse) + @. JJ = 0 + SS = Array{mk_float,1}(undef,nsparse) + @. SS = 0.0 + return sparse_matrix_constructor(II,JJ,SS) +end + +function assign_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) + data.II[icsc] = ii + data.JJ[icsc] = jj + data.SS[icsc] = ss + return nothing +end +function assemble_constructor_data!(data::sparse_matrix_constructor,icsc::mk_int,ii::mk_int,jj::mk_int,ss::mk_float) + data.II[icsc] = ii + data.JJ[icsc] = jj + data.SS[icsc] += ss + return nothing +end + +function create_sparse_matrix(data::sparse_matrix_constructor) + return sparse(data.II,data.JJ,data.SS) +end +struct vpa_vperp_boundary_data + lower_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vperp::MPISharedArray{mk_float,1} +end + +struct rosenbluth_potential_boundary_data + H_data::vpa_vperp_boundary_data + dHdvpa_data::vpa_vperp_boundary_data + dHdvperp_data::vpa_vperp_boundary_data + G_data::vpa_vperp_boundary_data + dGdvperp_data::vpa_vperp_boundary_data + d2Gdvperp2_data::vpa_vperp_boundary_data + d2Gdvperpdvpa_data::vpa_vperp_boundary_data + d2Gdvpa2_data::vpa_vperp_boundary_data +end + +function allocate_boundary_data(vpa,vperp) + lower_boundary_vpa = Array{mk_float,1}(undef,vperp.n) + upper_boundary_vpa = Array{mk_float,1}(undef,vperp.n) + upper_boundary_vperp = Array{mk_float,1}(undef,vpa.n) + return vpa_vperp_boundary_data(lower_boundary_vpa, + upper_boundary_vpa,upper_boundary_vperp) +end + + +function assign_exact_boundary_data!(func_data::vpa_vperp_boundary_data, + func_exact,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + for ivperp in 1:nvperp + func_data.lower_boundary_vpa[ivperp] = func_exact[1,ivperp] + func_data.upper_boundary_vpa[ivperp] = func_exact[nvpa,ivperp] + end + for ivpa in 1:nvpa + func_data.upper_boundary_vperp[ivpa] = func_exact[ivpa,nvperp] + end + return nothing +end + +function allocate_rosenbluth_potential_boundary_data(vpa,vperp) + H_data = allocate_boundary_data(vpa,vperp) + dHdvpa_data = allocate_boundary_data(vpa,vperp) + dHdvperp_data = allocate_boundary_data(vpa,vperp) + G_data = allocate_boundary_data(vpa,vperp) + dGdvperp_data = allocate_boundary_data(vpa,vperp) + d2Gdvperp2_data = allocate_boundary_data(vpa,vperp) + d2Gdvperpdvpa_data = allocate_boundary_data(vpa,vperp) + d2Gdvpa2_data = allocate_boundary_data(vpa,vperp) + return rosenbluth_potential_boundary_data(H_data,dHdvpa_data, + dHdvperp_data,G_data,dGdvperp_data,d2Gdvperp2_data, + d2Gdvperpdvpa_data,d2Gdvpa2_data) +end + +function calculate_rosenbluth_potential_boundary_data_exact!(rpbd::rosenbluth_potential_boundary_data, + H_exact,dHdvpa_exact,dHdvperp_exact,G_exact,dGdvperp_exact, + d2Gdvperp2_exact,d2Gdvperpdvpa_exact,d2Gdvpa2_exact, + vpa,vperp) + assign_exact_boundary_data!(rpbd.H_data,H_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dHdvpa_data,dHdvpa_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dHdvperp_data,dHdvperp_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.G_data,G_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.dGdvperp_data,dGdvperp_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvperp2_data,d2Gdvperp2_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvperpdvpa_data,d2Gdvperpdvpa_exact,vpa,vperp) + assign_exact_boundary_data!(rpbd.d2Gdvpa2_data,d2Gdvpa2_exact,vpa,vperp) + return nothing +end + + +function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, + weight::MPISharedArray{mk_float,4},func_input,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + #for ivperp in 1:nvperp + begin_vperp_region() + @loop_vperp ivperp begin + func_data.lower_boundary_vpa[ivperp] = 0.0 + func_data.upper_boundary_vpa[ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.lower_boundary_vpa[ivperp] += weight[ivpap,ivperpp,1,ivperp]*func_input[ivpap,ivperpp] + func_data.upper_boundary_vpa[ivperp] += weight[ivpap,ivperpp,nvpa,ivperp]*func_input[ivpap,ivperpp] + end + end + end + #for ivpa in 1:nvpa + begin_vpa_region() + @loop_vpa ivpa begin + func_data.upper_boundary_vperp[ivpa] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.upper_boundary_vperp[ivpa] += weight[ivpap,ivperpp,ivpa,nvperp]*func_input[ivpap,ivperpp] + end + end + end + # return to serial parallelisation + begin_serial_region() + return nothing +end + +function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, + weight::boundary_integration_weights_struct, + func_input,vpa,vperp) + nvpa = vpa.n + nvperp = vperp.n + #for ivperp in 1:nvperp + begin_vperp_region() + @loop_vperp ivperp begin + func_data.lower_boundary_vpa[ivperp] = 0.0 + func_data.upper_boundary_vpa[ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.lower_boundary_vpa[ivperp] += weight.lower_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] + func_data.upper_boundary_vpa[ivperp] += weight.upper_vpa_boundary[ivpap,ivperpp,ivperp]*func_input[ivpap,ivperpp] + end + end + end + #for ivpa in 1:nvpa + begin_vpa_region() + @loop_vpa ivpa begin + func_data.upper_boundary_vperp[ivpa] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + func_data.upper_boundary_vperp[ivpa] += weight.upper_vperp_boundary[ivpap,ivperpp,ivpa]*func_input[ivpap,ivperpp] + end + end + end + # return to serial parallelisation + begin_serial_region() + return nothing +end + +function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, + fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral) + # get derivatives of pdf + dfdvperp = fkpl.dfdvperp + dfdvpa = fkpl.dfdvpa + d2fdvperpdvpa = fkpl.d2fdvperpdvpa + #for ivpa in 1:vpa.n + begin_vpa_region() + @loop_vpa ivpa begin + @views derivative!(vperp.scratch, pdf[ivpa,:], vperp, vperp_spectral) + @. dfdvperp[ivpa,:] = vperp.scratch + end + begin_vperp_region() + @loop_vperp ivperp begin + #for ivperp in 1:vperp.n + @views derivative!(vpa.scratch, pdf[:,ivperp], vpa, vpa_spectral) + @. dfdvpa[:,ivperp] = vpa.scratch + @views derivative!(vpa.scratch, dfdvperp[:,ivperp], vpa, vpa_spectral) + @. d2fdvperpdvpa[:,ivperp] = vpa.scratch + end + # ensure data is synchronized + begin_serial_region() + # carry out the numerical integration + calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) + calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) + calculate_boundary_data!(rpbd.dHdvperp_data,fkpl.H1_weights,dfdvperp,vpa,vperp) + calculate_boundary_data!(rpbd.G_data,fkpl.G0_weights,pdf,vpa,vperp) + calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvperp2_data,fkpl.H2_weights,dfdvperp,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvperpdvpa_data,fkpl.G1_weights,d2fdvperpdvpa,vpa,vperp) + calculate_boundary_data!(rpbd.d2Gdvpa2_data,fkpl.H3_weights,dfdvpa,vpa,vperp) + + return nothing +end + +function test_rosenbluth_potential_boundary_data(rpbd::rosenbluth_potential_boundary_data, + rpbd_exact::rosenbluth_potential_boundary_data,vpa,vperp) + + error_buffer_vpa = Array{mk_float,1}(undef,vpa.n) + error_buffer_vperp_1 = Array{mk_float,1}(undef,vperp.n) + error_buffer_vperp_2 = Array{mk_float,1}(undef,vperp.n) + test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + + return nothing +end + +function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2) + nvpa = vpa.n + nvperp = vperp.n + for ivperp in 1:nvperp + buffer_vperp_1 = abs(func.lower_boundary_vpa[ivperp] - func_exact.lower_boundary_vpa[ivperp]) + buffer_vperp_2 = abs(func.upper_boundary_vpa[ivperp] - func_exact.upper_boundary_vpa[ivperp]) + end + for ivpa in 1:nvpa + buffer_vpa = abs(func.upper_boundary_vperp[ivpa] - func_exact.upper_boundary_vperp[ivpa]) + end + @serial_region begin + max_lower_vpa_err = maximum(buffer_vperp_1) + max_upper_vpa_err = maximum(buffer_vperp_2) + max_upper_vperp_err = maximum(buffer_vpa) + println(string(func_name*" boundary data:")) + println("max(lower_vpa_err) = ",max_lower_vpa_err) + println("max(upper_vpa_err) = ",max_upper_vpa_err) + println("max(upper_vperp_err) = ",max_upper_vperp_err) + end + return nothing +end + +function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # global indices on the grids + ivpa_global = vpa.igrid_full[ivpa_local,ielement_vpa] + ivperp_global = vperp.igrid_full[ivperp_local,ielement_vperp] + # global compound index + ic_global = ic_func(ivpa_global,ivperp_global,vpa.n) + return ic_global, ivpa_global, ivperp_global +end +function enforce_zero_bc!(fc,vpa,vperp;impose_BC_at_zero_vperp=false) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + + if impose_BC_at_zero_vperp + # lower vperp boundary + ielement_vperp = 1 + ivperp_local = 1 + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = 0.0 + end + end +end + +function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc;dirichlet_vperp_BC=false) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + + if dirichlet_vperp_BC + # upper vperp boundary + ielement_vperp = 1 + ivperp_local = 1 + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc[ivpa_global,ivperp_global] + end + end +end + +function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) + # lower vpa boundary + ielement_vpa = 1 + ivpa_local = 1 + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.lower_boundary_vpa[ivperp_global] + end + end + + # upper vpa boundary + ielement_vpa = vpa.nelement_local + ivpa_local = vpa.ngrid + for ielement_vperp in 1:vperp.nelement_local + for ivperp_local in 1:vperp.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vpa[ivperp_global] + end + end + + # upper vperp boundary + ielement_vperp = vperp.nelement_local + ivperp_local = vperp.ngrid + for ielement_vpa in 1:vpa.nelement_local + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + fc[ic_global] = f_bc.upper_boundary_vperp[ivpa_global] + end + end + return nothing +end + +function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) + nc_global = vpa.n*vperp.n + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + MM2D = Array{mk_float,2}(undef,nc_global,nc_global) + MM2D .= 0.0 + KKpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKpar2D .= 0.0 + KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + KKperp2D .= 0.0 + PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PUperp2D .= 0.0 + PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPparPUperp2D .= 0.0 + PPpar2D = Array{mk_float,2}(undef,nc_global,nc_global) + PPpar2D .= 0.0 + MMparMNperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + MMparMNperp2D .= 0.0 + # Laplacian matrix + LP2D = Array{mk_float,2}(undef,nc_global,nc_global) + LP2D .= 0.0 + # Modified Laplacian matrix + LV2D = Array{mk_float,2}(undef,nc_global,nc_global) + LV2D .= 0.0 + + #print_matrix(MM2D,"MM2D",nc_global,nc_global) + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + impose_BC_at_zero_vperp = false + @serial_region begin + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) + #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) + + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) + #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) + #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) + + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp + if ivperpp_local == 1 && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2D[ic_global,icp_global] = 1.0 + LP2D[ic_global,icp_global] = 1.0 + LV2D[ic_global,icp_global] = 1.0 + else + MM2D[ic_global,icp_global] = 0.0 + LP2D[ic_global,icp_global] = 0.0 + LV2D[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + LLperp[ivperp_local,ivperpp_local]) + LV2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + MNperp[ivperp_local,ivperpp_local])) + end + + # assign K matrices + KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local] + # assign PU matrix + PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] + PPparPUperp2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local] + PPpar2D[ic_global,icp_global] += PPpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + # assign RHS mass matrix for d2Gdvperp2 + MMparMNperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MNperp[ivperp_local,ivperpp_local] + end + end + end + end + end + end + @serial_region begin + println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + + if nc_global < 60 + print_matrix(MM2D,"MM2D",nc_global,nc_global) + #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + #print_matrix(LP2D,"LP",nc_global,nc_global) + #print_matrix(LV2D,"LV",nc_global,nc_global) + end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + MM2D_sparse = sparse(MM2D) + KKpar2D_sparse = sparse(KKpar2D) + KKperp2D_sparse = sparse(KKperp2D) + LP2D_sparse = sparse(LP2D) + LV2D_sparse = sparse(LV2D) + PUperp2D_sparse = sparse(PUperp2D) + PPparPUperp2D_sparse = sparse(PPparPUperp2D) + PPpar2D_sparse = sparse(PPpar2D) + MMparMNperp2D_sparse = sparse(MMparMNperp2D) + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, + LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + PPpar2D_sparse, MMparMNperp2D_sparse +end + +function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) + nc_global = vpa.n*vperp.n + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) + MM2DZG .= 0.0 + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + @serial_region begin + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp + if ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] + else + MM2DZG[ic_global,icp_global] = 0.0 + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + MM2DZG[ic_global,icp_global] = 1.0 + else + MM2DZG[ic_global,icp_global] = 0.0 + end + else + # assign mass matrix data + MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + end + end + end + end + end + end + end + @serial_region begin + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if nc_global < 30 + print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) + end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + MM2DZG_sparse = sparse(MM2DZG) + return MM2DZG_sparse +end + +function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 + ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 + nsparse = ntot_vpa*ntot_vperp + ngrid_vpa = vpa.ngrid + nelement_vpa = vpa.nelement_local + ngrid_vperp = vperp.ngrid + nelement_vperp = vperp.nelement_local + + MM2D = allocate_sparse_matrix_constructor(nsparse) + KKpar2D = allocate_sparse_matrix_constructor(nsparse) + KKperp2D = allocate_sparse_matrix_constructor(nsparse) + PUperp2D = allocate_sparse_matrix_constructor(nsparse) + PPparPUperp2D = allocate_sparse_matrix_constructor(nsparse) + PPpar2D = allocate_sparse_matrix_constructor(nsparse) + MMparMNperp2D = allocate_sparse_matrix_constructor(nsparse) + # Laplacian matrix + LP2D = allocate_sparse_matrix_constructor(nsparse) + # Modified Laplacian matrix + LV2D = allocate_sparse_matrix_constructor(nsparse) + + # local dummy arrays + MMpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + MMperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MNperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + KJperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PPperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PUperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PPpar = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + + impose_BC_at_zero_vperp = false + @serial_region begin + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:nelement_vperp + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") + get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") + get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") + get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") + #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MRperp,"MRperp",vperp.ngrid,vperp.ngrid) + #print_matrix(MNperp,"MNperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KKperp,"KKperp",vperp.ngrid,vperp.ngrid) + #print_matrix(KJperp,"KJperp",vperp.ngrid,vperp.ngrid) + #print_matrix(LLperp,"LLperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PPperp,"PPperp",vperp.ngrid,vperp.ngrid) + #print_matrix(PUperp,"PUperp",vperp.ngrid,vperp.ngrid) + + for ielement_vpa in 1:nelement_vpa + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") + #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) + #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) + #print_matrix(PPpar,"PPpar",vpa.ngrid,vpa.ngrid) + + for ivperpp_local in 1:ngrid_vperp + for ivperp_local in 1:ngrid_vperp + for ivpap_local in 1:ngrid_vpa + for ivpa_local in 1:ngrid_vpa + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, + ngrid_vpa,nelement_vpa, + ivperp_local,ivperpp_local, + ielement_vperp, + ngrid_vperp,nelement_vperp) + #println("ielement_vpa: ",ielement_vpa," ielement_vperp: ",ielement_vperp) + #println("ivpa_local: ",ivpa_local," ivpap_local: ",ivpap_local) + #println("ivperp_local: ",ivperp_local," ivperpp_local: ",ivperpp_local) + #println("ic: ",ic_global," icp: ",icp_global) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp + if ivperpp_local == 1 && ivpa_local == ivpap_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + end + else + # assign mass matrix data + #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) + assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + LLperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(LV2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + MNperp[ivperp_local,ivperpp_local]))) + end + + # assign K matrices + assemble_constructor_data!(KKpar2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(KKperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + KKperp[ivperp_local,ivperpp_local])) + # assign PU matrix + assemble_constructor_data!(PUperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(PPparPUperp2D,icsc,ic_global,icp_global, + (PPpar[ivpa_local,ivpap_local]* + PUperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(PPpar2D,icsc,ic_global,icp_global, + (PPpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + # assign RHS mass matrix for d2Gdvperp2 + assemble_constructor_data!(MMparMNperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MNperp[ivperp_local,ivperpp_local])) + end + end + end + end + end + end + MM2D_sparse = create_sparse_matrix(MM2D) + KKpar2D_sparse = create_sparse_matrix(KKpar2D) + KKperp2D_sparse = create_sparse_matrix(KKperp2D) + LP2D_sparse = create_sparse_matrix(LP2D) + LV2D_sparse = create_sparse_matrix(LV2D) + PUperp2D_sparse = create_sparse_matrix(PUperp2D) + PPparPUperp2D_sparse = create_sparse_matrix(PPparPUperp2D) + PPpar2D_sparse = create_sparse_matrix(PPpar2D) + MMparMNperp2D_sparse = create_sparse_matrix(MMparMNperp2D) + @serial_region begin + println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) + + if nc_global < 60 + println("MM2D_sparse \n",MM2D_sparse) + print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) + # print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) + # print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) + # print_matrix(LP2D,"LP",nc_global,nc_global) + # print_matrix(LV2D,"LV",nc_global,nc_global) + end + # convert these matrices to sparse matrices + #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, + LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + PPpar2D_sparse, MMparMNperp2D_sparse +end + +function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + # Assemble a 2D mass matrix in the global compound coordinate + nc_global = vpa.n*vperp.n + ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 + ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 + nsparse = ntot_vpa*ntot_vperp + ngrid_vpa = vpa.ngrid + nelement_vpa = vpa.nelement_local + ngrid_vperp = vperp.ngrid + nelement_vperp = vperp.nelement_local + + MM2DZG = allocate_sparse_matrix_constructor(nsparse) + # local dummy arrays + MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + + @serial_region begin + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end + for ielement_vperp in 1:vperp.nelement_local + get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") + for ielement_vpa in 1:vpa.nelement_local + get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + for ivperpp_local in 1:vperp.ngrid + for ivperp_local in 1:vperp.ngrid + for ivpap_local in 1:vpa.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) + icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, + ngrid_vpa,nelement_vpa, + ivperp_local,ivperpp_local, + ielement_vperp, + ngrid_vperp,nelement_vperp) + # boundary condition possibilities + lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) + upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) + lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) + upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) + + + if lower_boundary_row_vpa + if ivpap_local == 1 && ivperp_local == ivperpp_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vpa + if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp + if ivpa_local == ivpap_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,vperp_spectral.radau.D0[ivperpp_local]) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + elseif upper_boundary_row_vperp + if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) + else + assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) + end + else + # assign mass matrix data + assemble_constructor_data!(MM2DZG,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + end + end + end + end + end + end + end + @serial_region begin + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + #if nc_global < 30 + # print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) + #end + # convert these matrices to sparse matrices + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end + MM2DZG_sparse = create_sparse_matrix(MM2DZG) + return MM2DZG_sparse +end + +struct YY_collision_operator_arrays + # let phi_j(vperp) be the jth Lagrange basis function, + # and phi'_j(vperp) the first derivative of the Lagrange basis function + # on the iel^th element. Then, the arrays are defined as follows. + # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp + YY0perp::Array{mk_float,4} + # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp + YY1perp::Array{mk_float,4} + # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp + YY2perp::Array{mk_float,4} + # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp + YY3perp::Array{mk_float,4} + # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa + YY0par::Array{mk_float,4} + # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa + YY1par::Array{mk_float,4} + # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa + YY2par::Array{mk_float,4} + # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa + YY3par::Array{mk_float,4} +end + +function calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) + YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY2perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY3perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) + YY0par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY1par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY2par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + YY3par = Array{mk_float,4}(undef,vpa.ngrid,vpa.ngrid,vpa.ngrid,vpa.nelement_local) + + for ielement_vperp in 1:vperp.nelement_local + @views get_QQ_local!(YY0perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY0") + @views get_QQ_local!(YY1perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY1") + @views get_QQ_local!(YY2perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY2") + @views get_QQ_local!(YY3perp[:,:,:,ielement_vperp],ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"YY3") + end + for ielement_vpa in 1:vpa.nelement_local + @views get_QQ_local!(YY0par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY0") + @views get_QQ_local!(YY1par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY1") + @views get_QQ_local!(YY2par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY2") + @views get_QQ_local!(YY3par[:,:,:,ielement_vpa],ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"YY3") + end + + return YY_collision_operator_arrays(YY0perp,YY1perp,YY2perp,YY3perp, + YY0par,YY1par,YY2par,YY3par) +end + +function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + @. rhsc = 0.0 + + # loop over elements + for ielement_vperp in 1:vperp.nelement_local + YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in 1:vpa.nelement_local + YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # loop over field positions in each element + for ivperp_local in 1:vperp.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + end + # correct for minus sign due to integration by parts + # and multiply by the normalised collision frequency + @. rhsc *= -nussp + return nothing +end + +function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + rhsvpavperp[ivpa,ivperp] = 0.0 + end + # loop over collocation points to benefit from shared-memory parallelism + ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid + @loop_vperp_vpa ivperp_global ivpa_global begin + igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) + # loop over elements belonging to this collocation point + for ielement_vperp in ielement_vperp_low:ielement_vperp_hi + # correct local ivperp in the case that we on a boundary point + ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) + @views YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + @views YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + @views YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + @views YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in ielement_vpa_low:ielement_vpa_hi + # correct local ivpa in the case that we on a boundary point + ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) + @views YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + @views YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + @views YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + @views YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + # ravel to compound index + #begin_serial_region() + #ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) + ravel_vpavperp_to_c_parallel!(rhsc,rhsvpavperp,vpa.n) + return nothing +end + + +# Elliptic solve function. +# field: the solution +# source: the source function on the RHS +# boundary data: the known values of field at infinity +# lu_object_lhs: the object for the differential operator that defines field +# matrix_rhs: the weak matrix acting on the source vector +# rhsc, sc: dummy arrays in the compound index (assumed MPISharedArray or SubArray type) +# vpa, vperp: coordinate structs +function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs,rhsc,sc,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc,matrix_rhs,sc) + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) + # solve the linear system + sc = lu_object_lhs \ rhsc + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) + return nothing +end +# same as above but source is made of two different terms +# with different weak matrices +function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) + ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) + + # assemble the rhs of the weak system + begin_serial_region() + mul!(rhsc_1,matrix_rhs_1,sc_1) + mul!(rhsc_2,matrix_rhs_2,sc_2) + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,vpa.n) + rhsc_1[ic] += rhsc_2[ic] + end + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) + # solve the linear system + sc_1 = lu_object_lhs \ rhsc_1 + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) + return nothing +end + +end diff --git a/src/fokker_planck_test.jl b/src/fokker_planck_test.jl new file mode 100644 index 000000000..d828002dd --- /dev/null +++ b/src/fokker_planck_test.jl @@ -0,0 +1,269 @@ +""" +module for including functions used +in testing the implementation of the +the Full-F Fokker-Planck Collision Operator +""" +module fokker_planck_test + +export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs +export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 +export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +export H_Maxwellian, G_Maxwellian + +export Cssp_fully_expanded_form, calculate_collisional_fluxes + +using ..type_definitions: mk_float, mk_int +using SpecialFunctions: erf +# below are a series of functions that can be used to test the calculation +# of the Rosenbluth potentials for a shifted Maxwellian +# or provide an estimate for collisional coefficients + +# G (defined by Del^4 G = -(8/sqrt(pi))*F +# with F = cref^3 pi^(3/2) F_Maxwellian / nref +# the normalised Maxwellian +function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + G = 2.0/sqrt(pi) + else + # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) + G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) + end + return G*dens*vth +end + +# H (defined by Del^2 H = -(4/sqrt(pi))*F +# with F = cref^3 pi^(3/2) F_Maxwellian / nref +# the normalised Maxwellian +function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + # speed variable + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + zero = 1.0e-10 + if eta < zero + # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 + H = 2.0/sqrt(pi) + else + # H_M = erf(eta)/eta + H = erf(eta)/eta + end + return H*dens/vth +end + +# 1D derivative functions + +function dGdeta(eta::mk_float) + # d \tilde{G} / d eta + dGdeta_fac = (1.0/sqrt(pi))*exp(-eta^2)/eta + (1.0 - 0.5/(eta^2))*erf(eta) + return dGdeta_fac +end + +function d2Gdeta2(eta::mk_float) + # d \tilde{G} / d eta + d2Gdeta2_fac = erf(eta)/(eta^3) - (2.0/sqrt(pi))*exp(-eta^2)/(eta^2) + return d2Gdeta2_fac +end + +function ddGddeta(eta::mk_float) + # d / d eta ( (1/ eta) d \tilde{G} d eta + ddGddeta_fac = (1.5/(eta^2) - 1.0)*erf(eta)/(eta^2) - (3.0/sqrt(pi))*exp(-eta^2)/(eta^3) + return ddGddeta_fac +end + +function dHdeta(eta::mk_float) + dHdeta_fac = (2.0/sqrt(pi))*(exp(-eta^2))/eta - erf(eta)/(eta^2) + return dHdeta_fac +end + +# functions of vpa & vperp +function eta_func(upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + speed = sqrt( (vpa.grid[ivpa] - upar)^2 + vperp.grid[ivperp]^2)/vth + return speed +end + +function d2Gdvpa2(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta) + ddGddeta(eta)*((vpa.grid[ivpa] - upar)^2)/(vth^2) + d2Gdvpa2_fac = fac*dens/(eta*vth) + return d2Gdvpa2_fac +end + +function d2Gdvperpdvpa(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = ddGddeta(eta)*vperp.grid[ivperp]*(vpa.grid[ivpa] - upar)/(vth^2) + d2Gdvperpdvpa_fac = fac*dens/(eta*vth) + return d2Gdvperpdvpa_fac +end + +function d2Gdvperp2(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta) + ddGddeta(eta)*(vperp.grid[ivperp]^2)/(vth^2) + d2Gdvperp2_fac = fac*dens/(eta*vth) + return d2Gdvperp2_fac +end + +function dGdvperp(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dGdeta(eta)*vperp.grid[ivperp]*dens/(vth*eta) + return fac +end + +function dHdvperp(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dHdeta(eta)*vperp.grid[ivperp]*dens/(eta*vth^3) + return fac +end + +function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = dHdeta(eta)*(vpa.grid[ivpa]-upar)*dens/(eta*vth^3) + return fac +end + +function F_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = (dens/(vth^3))*exp(-eta^2) + return fac +end + +function dFdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = -2.0*(dens/(vth^4))*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) + return fac +end + +function dFdvperp_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = -2.0*(dens/(vth^4))*(vperp.grid[ivperp]/vth)*exp(-eta^2) + return fac +end + +function d2Fdvperpdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*(vperp.grid[ivperp]/vth)*((vpa.grid[ivpa] - upar)/vth)*exp(-eta^2) + return fac +end + +function d2Fdvpa2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*( ((vpa.grid[ivpa] - upar)/vth)^2 - 0.5 )*exp(-eta^2) + return fac +end + +function d2Fdvperp2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, + vpa,vperp,ivpa,ivperp) + eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) + fac = 4.0*(dens/(vth^5))*((vperp.grid[ivperp]/vth)^2 - 0.5)*exp(-eta^2) + return fac +end + +function Cssp_Maxwellian_inputs(denss::mk_float,upars::mk_float,vths::mk_float,ms::mk_float, + denssp::mk_float,uparsp::mk_float,vthsp::mk_float,msp::mk_float, + nussp::mk_float,vpa,vperp,ivpa,ivperp) + + d2Fsdvpa2 = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Fsdvperp2 = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Fsdvperpdvpa = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dFsdvperp = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dFsdvpa = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + Fs = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + d2Gspdvpa2 = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperp2 = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperpdvpa = d2Gdvperpdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGspdvperp = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvperp = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvpa = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + Fsp = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + + ( Cssp_Maxwellian = + d2Fsdvpa2*d2Gspdvpa2 + + d2Fsdvperp2*d2Gspdvperp2 + + 2.0*d2Fsdvperpdvpa*d2Gspdvperpdvpa + + (1.0/(vperp.grid[ivperp]^2))*dFsdvperp*dGspdvperp + + 2.0*(1.0 - (ms/msp))*(dFsdvpa*dHspdvpa + dFsdvperp*dHspdvperp) + + (8.0/sqrt(pi))*(ms/msp)*Fs*Fsp ) + + Cssp_Maxwellian *= nussp + return Cssp_Maxwellian +end + +function Cflux_vpa_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, + msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, + vpa,vperp,ivpa,ivperp) + etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) + eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) + prefac = -2.0*denss*denssp*exp( -eta^2)/(vthsp*vths^5) + (fac = (vpa.grid[ivpa]-uparsp)*(d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) + + (uparsp - upars)*( dGdeta(etap) + ((vpa.grid[ivpa]-uparsp)^2/vthsp^2)*ddGddeta(etap) )/etap ) + Cflux = prefac*fac + #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap + #fac *= d2Gdeta2(etap) + return Cflux +end + +function Cflux_vperp_Maxwellian_inputs(ms::mk_float,denss::mk_float,upars::mk_float,vths::mk_float, + msp::mk_float,denssp::mk_float,uparsp::mk_float,vthsp::mk_float, + vpa,vperp,ivpa,ivperp) + etap = eta_func(uparsp,vthsp,vpa,vperp,ivpa,ivperp) + eta = eta_func(upars,vths,vpa,vperp,ivpa,ivperp) + prefac = -2.0*(vperp.grid[ivperp])*denss*denssp*exp( -eta^2)/(vthsp*vths^5) + (fac = (d2Gdeta2(etap) + (ms/msp)*((vths/vthsp)^2)*dHdeta(etap)/etap) + + ((uparsp - upars)*(vpa.grid[ivpa]-uparsp)/vthsp^2)*ddGddeta(etap)/etap ) + Cflux = prefac*fac + #fac *= (ms/msp)*(vths/vthsp)*dHdeta(etap)/etap + #fac *= d2Gdeta2(etap) + return Cflux +end + +""" +Function calculating the fully expanded form of the collision operator +taking floats as arguments. This function is designed to be used at the +lowest level of a coordinate loop, with derivatives and integrals +all previously calculated. +""" + +function Cssp_fully_expanded_form(nussp,ms,msp, + d2fsdvpa2,d2fsdvperp2,d2fsdvperpdvpa,dfsdvpa,dfsdvperp,fs, + d2Gspdvpa2,d2Gspdvperp2,d2Gspdvperpdvpa,dGspdvperp, + dHspdvpa,dHspdvperp,fsp,vperp_val) + ( Cssp = nussp*( d2fsdvpa2*d2Gspdvpa2 + + d2fsdvperp2*d2Gspdvperp2 + + 2.0*d2fsdvperpdvpa*d2Gspdvperpdvpa + + (1.0/(vperp_val^2))*dfsdvperp*dGspdvperp + + 2.0*(1.0 - (ms/msp))*(dfsdvpa*dHspdvpa + dfsdvperp*dHspdvperp) + + (8.0/sqrt(pi))*(ms/msp)*fs*fsp) ) + return Cssp +end + + +""" +calculates the collisional fluxes given input F_s and G_sp, H_sp +""" +function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dHdvpa,dHdvperp, + ms,msp) + # fill in value at (ivpa,ivperp) + Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa - 2.0*(ms/msp)*F*dHdvpa + #Cflux_vpa = dFdvpa*d2Gdvpa2 + dFdvperp*d2Gdvperpdvpa # - 2.0*(ms/msp)*F*dHdvpa + #Cflux_vpa = - 2.0*(ms/msp)*F*dHdvpa + Cflux_vperp = dFdvpa*d2Gdvperpdvpa + dFdvperp*d2Gdvperp2 - 2.0*(ms/msp)*F*dHdvperp + return Cflux_vpa, Cflux_vperp +end + +end diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index d8c1a9c73..7e0ec0a3e 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -12,6 +12,10 @@ export enforce_neutral_boundary_conditions! export enforce_neutral_r_boundary_condition! export enforce_neutral_z_boundary_condition! +# functional testing +export create_and_init_boundary_distributions +export create_pdf + # package using SpecialFunctions: erfc # modules diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index 93131e1f9..725775883 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -34,6 +34,8 @@ include("bgk.jl") include("manufactured_solns.jl") # MRH Here? include("initial_conditions.jl") #include("semi_lagrange.jl") +include("fokker_planck_test.jl") +include("fokker_planck_calculus.jl") include("fokker_planck.jl") include("collision_models.jl") include("advection.jl") diff --git a/src/time_advance.jl b/src/time_advance.jl index 9a6f28e24..6968923e1 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -5,6 +5,8 @@ module time_advance export setup_time_advance! export time_advance! export setup_dummy_and_buffer_arrays +# functional testing +export setup_runge_kutta_coefficients using MPI using ..type_definitions: mk_float From f96c65a6ff8cd833d48e7fdd220bad3eb43a2a1c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 29 Oct 2023 18:18:53 +0000 Subject: [PATCH 194/331] Refactor initialisation of arrays needed to perform the elliptic solve and assemble the explicit weak-form collision operator so that there is a new struct which contains the required data. The Rosenbluth potentials are not included in the struct as it is anticipated that distribution size dummy arrasy will be required to make efficient use of the shared-memory parallelisation. --- 2D_FEM_assembly_test.jl | 72 ++++++++++++----------------- Project.toml | 1 + src/fokker_planck.jl | 49 ++++++++++++++++++-- src/fokker_planck_calculus.jl | 86 +++++++++++++++++++++++++---------- 4 files changed, 136 insertions(+), 72 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 2d220b47c..6cf6b5aad 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -25,13 +25,10 @@ using moment_kinetics.fokker_planck_test: d2Gdvpa2, d2Gdvperp2, d2Gdvperpdvpa, d using moment_kinetics.fokker_planck_test: dHdvperp, dHdvpa using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc, assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient -using moment_kinetics.fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse, assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse using moment_kinetics.fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial!, assemble_explicit_collision_operator_rhs_parallel! using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data!, calculate_rosenbluth_potential_boundary_data_exact! -using moment_kinetics.fokker_planck_calculus: calculate_YY_arrays using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data @@ -217,31 +214,23 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_serial_region() start_init_time = now() + fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp,vpa_spectral,vperp_spectral; + precompute_weights=true, test_dense_matrix_construction=test_dense_construction) - if test_dense_construction - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, - PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) - else - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, - PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - end - #MM2D_sparse - @serial_region begin - # create LU decomposition for mass matrix inversion - println("begin LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - end - lu_obj_MM = lu(MM2D_sparse) - lu_obj_MMZG = lu(MM2DZG_sparse) - lu_obj_LP = lu(LP2D_sparse) - lu_obj_LV = lu(LV2D_sparse) - #cholesky_obj = cholesky(MM2D_sparse) - @serial_region begin - println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) - end + MM2D_sparse = fkpl_arrays.MM2D_sparse + KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse + KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse + LP2D_sparse = fkpl_arrays.LP2D_sparse + LV2D_sparse = fkpl_arrays.LV2D_sparse + PUperp2D_sparse = fkpl_arrays.PUperp2D_sparse + PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse + PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse + MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse + MM2DZG_sparse = fkpl_arrays.MM2DZG_sparse + lu_obj_MM = fkpl_arrays.lu_obj_MM + lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG + lu_obj_LP = fkpl_arrays.lu_obj_LP + lu_obj_LV = fkpl_arrays.lu_obj_LV # define a test function fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -322,8 +311,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - S_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - Q_dummy = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + S_dummy = fkpl_arrays.S_dummy + Q_dummy = fkpl_arrays.Q_dummy Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -393,21 +382,16 @@ if abspath(PROGRAM_FILE) == @__FILE__ dGdvperp_M_exact,d2Gdvperp2_M_exact, d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) # use numerical integration to find the boundary data - # initialise the weights - #fkpl_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) - fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=true) # initialise any arrays needed later for the collision operator - rhsc = MPISharedArray{mk_float,1}(undef,nc_global) - rhqc = MPISharedArray{mk_float,1}(undef,nc_global) - sc = MPISharedArray{mk_float,1}(undef,nc_global) - qc = MPISharedArray{mk_float,1}(undef,nc_global) + rhsc = fkpl_arrays.rhsc + rhqc = fkpl_arrays.rhqc + sc = fkpl_arrays.sc + qc = fkpl_arrays.qc rhsc_check = Array{mk_float,1}(undef,nc_global) rhsc_err = Array{mk_float,1}(undef,nc_global) - rhsvpavperp = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) - @serial_region begin - println("begin YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) + rhsvpavperp = fkpl_arrays.rhsvpavperp + + YY_arrays = fkpl_arrays.YY_arrays finish_init_time = now() @@ -416,7 +400,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ @serial_region begin println("begin boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays,F_M,vpa,vperp,vpa_spectral,vperp_spectral) + calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays.bwgt,F_M,vpa,vperp,vpa_spectral,vperp_spectral) @serial_region begin println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) end @@ -583,7 +567,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ initialize_comms!() ngrid = 9 - plot_scan = true + plot_scan = false#true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false @@ -596,7 +580,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] - nelement_list = Int[4, 8] + nelement_list = Int[8] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) diff --git a/Project.toml b/Project.toml index 51acefbd1..cb68d68ce 100644 --- a/Project.toml +++ b/Project.toml @@ -40,6 +40,7 @@ SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 8e8866d75..50f14cc2f 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -15,6 +15,7 @@ export symmetric_matrix_inverse using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature using Dates +using LinearAlgebra: lu using ..initial_conditions: enforce_boundary_conditions! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float @@ -26,7 +27,12 @@ using ..looping 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 -using ..fokker_planck_calculus: fokkerplanck_arrays_struct +using ..fokker_planck_calculus: fokkerplanck_arrays_struct, fokkerplanck_weakform_arrays_struct +using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc +using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse +using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient +using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse +using ..fokker_planck_calculus: calculate_YY_arrays using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes """ allocate the required ancilliary arrays @@ -77,13 +83,50 @@ at the boundary and using an elliptic solve to obtain the potentials in the rest of the velocity space domain. """ -function init_fokker_planck_collisions_new(vpa,vperp; precompute_weights=false) +function init_fokker_planck_collisions_new(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false) bwgt = allocate_boundary_integration_weights(vpa,vperp) if vperp.n > 1 && precompute_weights @views init_Rosenbluth_potential_boundary_integration_weights!(bwgt.G0_weights, bwgt.G1_weights, bwgt.H0_weights, bwgt.H1_weights, bwgt.H2_weights, bwgt.H3_weights, vpa, vperp) end - return bwgt + if test_dense_matrix_construction + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) + else + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + end + lu_obj_MM = lu(MM2D_sparse) + lu_obj_MMZG = lu(MM2DZG_sparse) + lu_obj_LP = lu(LP2D_sparse) + lu_obj_LV = lu(LV2D_sparse) + @serial_region begin + println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) + @serial_region begin + println("finished YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end + nvpa, nvperp = vpa.n, vperp.n + nc = nvpa*nvperp + S_dummy = allocate_shared_float(nvpa,nvperp) + Q_dummy = allocate_shared_float(nvpa,nvperp) + rhsvpavperp = allocate_shared_float(nvpa,nvperp) + rhsc = allocate_shared_float(nc) + rhqc = allocate_shared_float(nc) + sc = allocate_shared_float(nc) + qc = allocate_shared_float(nc) + fka = fokkerplanck_weakform_arrays_struct(bwgt,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, + LP2D_sparse,LV2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, + PPpar2D_sparse,MMparMNperp2D_sparse,MM2DZG_sparse, + lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV, + YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc) + return fka end """ diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 061d61b0c..e566d00fd 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -35,8 +35,9 @@ using ..looping using moment_kinetics.gauss_legendre: get_QQ_local! using Dates using SpecialFunctions: ellipk, ellipe -using SparseArrays: sparse -using LinearAlgebra: mul! +using SparseArrays: sparse, AbstractSparseArray +using SuiteSparse +using LinearAlgebra: mul!, LU using FastGaussQuadrature using Printf @@ -62,7 +63,7 @@ end """ a struct of dummy arrays and precalculated coefficients -for the Fokker-Planck collision operator +for the strong-form Fokker-Planck collision operator """ struct fokkerplanck_arrays_struct @@ -92,7 +93,6 @@ struct fokkerplanck_arrays_struct d2fdvperp2::MPISharedArray{mk_float,2} end - """ a struct to contain the integration weights for the boundary points in the (vpa,vperp) domain @@ -120,6 +120,63 @@ struct fokkerplanck_boundary_data_arrays_struct end +struct YY_collision_operator_arrays + # let phi_j(vperp) be the jth Lagrange basis function, + # and phi'_j(vperp) the first derivative of the Lagrange basis function + # on the iel^th element. Then, the arrays are defined as follows. + # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp + YY0perp::Array{mk_float,4} + # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp + YY1perp::Array{mk_float,4} + # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp + YY2perp::Array{mk_float,4} + # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp + YY3perp::Array{mk_float,4} + # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa + YY0par::Array{mk_float,4} + # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa + YY1par::Array{mk_float,4} + # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa + YY2par::Array{mk_float,4} + # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa + YY3par::Array{mk_float,4} +end + +""" +a struct of dummy arrays and precalculated coefficients +for the weak-form Fokker-Planck collision operator +""" +struct fokkerplanck_weakform_arrays_struct{N} + # boundary weights (Green's function) data + bwgt::fokkerplanck_boundary_data_arrays_struct + # assembled 2D weak-form matrices + MM2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + KKpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + KKperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + LP2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + LV2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + PUperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + PPparPUperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + PPpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + MMparMNperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + MM2DZG_sparse::AbstractSparseArray{mk_float,mk_int,N} + # lu decomposition objects + lu_obj_MM::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} + lu_obj_MMZG::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} + lu_obj_LP::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} + lu_obj_LV::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} + # elemental matrices for the assembly of C[Fs,Fsp] + YY_arrays::YY_collision_operator_arrays + # dummy arrays for elliptic solvers + S_dummy::MPISharedArray{mk_float,2} + Q_dummy::MPISharedArray{mk_float,2} + rhsvpavperp::MPISharedArray{mk_float,2} + rhsc::MPISharedArray{mk_float,1} + rhqc::MPISharedArray{mk_float,1} + sc::MPISharedArray{mk_float,1} + qc::MPISharedArray{mk_float,1} +end + function allocate_boundary_integration_weight(vpa,vperp) nvpa = vpa.n nvperp = vperp.n @@ -1770,27 +1827,6 @@ function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse( return MM2DZG_sparse end -struct YY_collision_operator_arrays - # let phi_j(vperp) be the jth Lagrange basis function, - # and phi'_j(vperp) the first derivative of the Lagrange basis function - # on the iel^th element. Then, the arrays are defined as follows. - # YY0perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi_k(vperp) vperp d vperp - YY0perp::Array{mk_float,4} - # YY1perp[i,j,k,iel] = \int phi_i(vperp) phi_j(vperp) phi'_k(vperp) vperp d vperp - YY1perp::Array{mk_float,4} - # YY2perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi'_k(vperp) vperp d vperp - YY2perp::Array{mk_float,4} - # YY3perp[i,j,k,iel] = \int phi_i(vperp) phi'_j(vperp) phi_k(vperp) vperp d vperp - YY3perp::Array{mk_float,4} - # YY0par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi_k(vpa) vpa d vpa - YY0par::Array{mk_float,4} - # YY1par[i,j,k,iel] = \int phi_i(vpa) phi_j(vpa) phi'_k(vpa) vpa d vpa - YY1par::Array{mk_float,4} - # YY2par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi'_k(vpa) vpa d vpa - YY2par::Array{mk_float,4} - # YY3par[i,j,k,iel] = \int phi_i(vpa) phi'_j(vpa) phi_k(vpa) vpa d vpa - YY3par::Array{mk_float,4} -end function calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) From 082b99d5de4299aa274138652b8fc0c7d7494821 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 31 Oct 2023 14:16:52 +0000 Subject: [PATCH 195/331] Correct t/cref labels to t/Lref/cref. --- src/post_processing.jl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/post_processing.jl b/src/post_processing.jl index bb2b1b4e3..35202909d 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -1884,10 +1884,10 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp savefig(outfile) end if pp.plot_dens0_vs_t - @views plot(time, density[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"n_i", label = "") + @views plot(time, density[iz0,ir0,is,:], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"n_i", label = "") outfile = string(run_name, "_density"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) - @views plot(time, density[iz0,ir0,is,:] .- density[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"n_i(t) - n_i(0)", label = "") + @views plot(time, density[iz0,ir0,is,:] .- density[iz0,ir0,is,1], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"n_i(t) - n_i(0)", label = "") outfile = string(run_name, "_delta_density"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end @@ -1919,10 +1919,10 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp savefig(outfile) end if pp.plot_upar0_vs_t - @views plot(time, parallel_flow[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"u_{i\|\|}(t)", label = "") + @views plot(time, parallel_flow[iz0,ir0,is,:], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"u_{i\|\|}(t)", label = "") outfile = string(run_name, "_parallel_flow"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) - @views plot(time, parallel_flow[iz0,ir0,is,:] .- parallel_flow[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"u_{i\|\|}(t) - u_{i\|\|}(0)", label = "") + @views plot(time, parallel_flow[iz0,ir0,is,:] .- parallel_flow[iz0,ir0,is,1], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"u_{i\|\|}(t) - u_{i\|\|}(0)", label = "") outfile = string(run_name, "_delta_parallel_flow"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end @@ -1954,19 +1954,19 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp savefig(outfile) end if pp.plot_ppar0_vs_t - @views plot(time, parallel_pressure[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"p_{i\|\|}(t)", label = "") + @views plot(time, parallel_pressure[iz0,ir0,is,:], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i\|\|}(t)", label = "") outfile = string(run_name, "_parallel_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) - @views plot(time, parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"p_{i\|\|}(t) - p_{i\|\|}(0)", label = "") + @views plot(time, parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i\|\|}(t) - p_{i\|\|}(0)", label = "") outfile = string(run_name, "_delta_parallel_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end # the perpendicular pressure if pp.plot_pperp0_vs_t - @views plot(time, perpendicular_pressure[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"p_{i\perp}(t)", label = "") + @views plot(time, perpendicular_pressure[iz0,ir0,is,:], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i\perp}(t)", label = "") outfile = string(run_name, "_perpendicular_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) - @views plot(time, perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"p_{i\perp}(t) - p_{i\perp}(0)", label = "") + @views plot(time, perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i\perp}(t) - p_{i\perp}(0)", label = "") outfile = string(run_name, "_delta_perpendicular_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end @@ -1975,40 +1975,40 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, perp @views plot([time, time, time] , [parallel_pressure[iz0,ir0,is,:], perpendicular_pressure[iz0,ir0,is,:], (2.0/3.0).*perpendicular_pressure[iz0,ir0,is,:] .+ (1.0/3.0).*parallel_pressure[iz0,ir0,is,:]], - xlabel=L"t/c_{ref}", ylabel="", label = [L"p_{i\|\|}(t)" L"p_{i\perp}(t)" L"p_{i}(t)"]) + xlabel=L"t/ (L_{ref}/c_{ref})", ylabel="", label = [L"p_{i\|\|}(t)" L"p_{i\perp}(t)" L"p_{i}(t)"]) outfile = string(run_name, "_pressures"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) @views plot([time, time, time] , [parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1], perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1], (2.0/3.0).*(perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1]).+ (1.0/3.0).*(parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1])], - xlabel=L"t/c_{ref}", ylabel="", label = [L"p_{i\|\|}(t) - p_{i\|\|}(0)" L"p_{i\perp}(t) - p_{i\perp}(0)" L"p_{i}(t) - p_{i}(0)"]) + xlabel=L"t/ (L_{ref}/c_{ref})", ylabel="", label = [L"p_{i\|\|}(t) - p_{i\|\|}(0)" L"p_{i\perp}(t) - p_{i\perp}(0)" L"p_{i}(t) - p_{i}(0)"]) outfile = string(run_name, "_delta_pressures"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) @views plot([time] , [(2.0/3.0).*perpendicular_pressure[iz0,ir0,is,:] .+ (1.0/3.0).*parallel_pressure[iz0,ir0,is,:]], - xlabel=L"t/c_{ref}", ylabel=L"p_{i}(t)", label = "") + xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i}(t)", label = "") outfile = string(run_name, "_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) @views plot([time] , [(2.0/3.0).*(perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1]).+ (1.0/3.0).*(parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1])], - xlabel=L"t/c_{ref}", ylabel=L"p_{i}(t) - p_{i}(0)", label = "") + xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"p_{i}(t) - p_{i}(0)", label = "") outfile = string(run_name, "_delta_pressure"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end # the thermal speed if pp.plot_vth0_vs_t - @views plot(time, thermal_speed[iz0,ir0,is,:], xlabel=L"t/c_{ref}", ylabel=L"v_{i,th}(t)", label = "") + @views plot(time, thermal_speed[iz0,ir0,is,:], xlabel=L"t / (L_{ref}/c_{ref})", ylabel=L"v_{i,th}(t)", label = "") outfile = string(run_name, "_thermal_speed"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) - @views plot(time, thermal_speed[iz0,ir0,is,:] .- thermal_speed[iz0,ir0,is,1], xlabel=L"t/c_{ref}", ylabel=L"v_{i,th}(t) - v_{i,th}(0)", label = "") + @views plot(time, thermal_speed[iz0,ir0,is,:] .- thermal_speed[iz0,ir0,is,1], xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"v_{i,th}(t) - v_{i,th}(0)", label = "") outfile = string(run_name, "_delta_thermal_speed"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end # the entropy production if pp.plot_dSdt0_vs_t - @views plot(time[2:ntime], entropy_production[iz0,ir0,is,2:ntime], xlabel=L"t/c_{ref}", ylabel=L"\dot{S}(t)", label = "") + @views plot(time[2:ntime], entropy_production[iz0,ir0,is,2:ntime], xlabel=L"t/(L_{ref}/c_{ref})", ylabel=L"\dot{S}(t)", label = "") outfile = string(run_name, "_entropy production"*description*"(iz0,ir0)_vs_t.pdf") savefig(outfile) end @@ -2050,10 +2050,10 @@ function plot_Maxwellian_diagnostic(ff, density, parallel_flow, thermal_speed, v iz0_string = string("_iz0", string(iz0)) ir0_string = string("_ir0", string(ir0)) description = "_ion_spec"*string(is)*"_"*iz0_string*ir0_string - @views plot(time, ff_norm, xlabel=L"t/c_{ref}", ylabel=L"L^2(f - f_M)(t)", label = "") + @views plot(time, ff_norm, xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"L^2(f - f_M)(t)", label = "") outfile = string(run_name, "_L2_Maxwellian_norm"*description*"_vs_t.pdf") savefig(outfile) - @views plot(time, ff_norm, xlabel=L"t/c_{ref}", ylabel=L"L^2(f - f_M)(t)", label = "", yscale=:log10) + @views plot(time, ff_norm, xlabel=L"t/ (L_{ref}/c_{ref})", ylabel=L"L^2(f - f_M)(t)", label = "", yscale=:log10) outfile = string(run_name, "_L2_Maxwellian_norm_log_scale"*description*"_vs_t.pdf") savefig(outfile) end From 3f6e4db9e7ba96e0d003253ad5055d298d357537 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 31 Oct 2023 14:29:20 +0000 Subject: [PATCH 196/331] Add weak-form collision operator to time evolving code. Plausbile behaviour is observed on a single core. MPI issues may be present on runs using multiple cores. --- 2D_FEM_assembly_test.jl | 176 ++++++++------------------------ fkpl_functional_test.jl | 4 +- src/fokker_planck.jl | 187 +++++++++++++++++++++++++++++++++- src/fokker_planck_calculus.jl | 45 +++++--- src/input_structs.jl | 3 + src/moment_kinetics_input.jl | 5 +- src/time_advance.jl | 27 ++++- 7 files changed, 284 insertions(+), 163 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 6cf6b5aad..7967e7882 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -11,7 +11,8 @@ using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: init_fokker_planck_collisions -using moment_kinetics.fokker_planck: init_fokker_planck_collisions_new +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form +using moment_kinetics.fokker_planck: fokker_planck_collision_operator_weak_form! using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.communication @@ -25,7 +26,6 @@ using moment_kinetics.fokker_planck_test: d2Gdvpa2, d2Gdvperp2, d2Gdvperpdvpa, d using moment_kinetics.fokker_planck_test: dHdvperp, dHdvpa using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial!, assemble_explicit_collision_operator_rhs_parallel! using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data!, calculate_rosenbluth_potential_boundary_data_exact! @@ -214,24 +214,13 @@ if abspath(PROGRAM_FILE) == @__FILE__ begin_serial_region() start_init_time = now() - fkpl_arrays = init_fokker_planck_collisions_new(vpa,vperp,vpa_spectral,vperp_spectral; + fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true, test_dense_matrix_construction=test_dense_construction) - - MM2D_sparse = fkpl_arrays.MM2D_sparse KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse - LP2D_sparse = fkpl_arrays.LP2D_sparse - LV2D_sparse = fkpl_arrays.LV2D_sparse - PUperp2D_sparse = fkpl_arrays.PUperp2D_sparse - PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse - PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse - MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse - MM2DZG_sparse = fkpl_arrays.MM2DZG_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG - lu_obj_LP = fkpl_arrays.lu_obj_LP - lu_obj_LV = fkpl_arrays.lu_obj_LV - # define a test function + finish_init_time = now() fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -254,14 +243,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end - # boundary conditions - - #fvpavperp[vpa.n,:] .= 0.0 - #fvpavperp[1,:] .= 0.0 - #fvpavperp[:,vperp.n] .= 0.0 - - - #print_matrix(fvpavperp,"fvpavperp",vpa.n,vperp.n) # fill fc with fvpavperp ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) @@ -282,11 +263,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ gc = lu_obj_MMZG \ dgc else # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) - enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) + enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=false) + enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=false) # invert mass matrix and fill fc - fc = lu_obj_MMZG \ dfc - gc = lu_obj_MMZG \ dgc + fc = lu_obj_MM \ dfc + gc = lu_obj_MM \ dgc end #fc = cholesky_obj \ dfc #print_vector(fc,"fc",nc_global) @@ -311,8 +292,6 @@ if abspath(PROGRAM_FILE) == @__FILE__ end # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - S_dummy = fkpl_arrays.S_dummy - Q_dummy = fkpl_arrays.Q_dummy Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -372,122 +351,47 @@ if abspath(PROGRAM_FILE) == @__FILE__ dens,upar,vth,msp, nussp,vpa,vperp,ivpa,ivperp) end - end - # calculate the Rosenbluth potential boundary data (rpbd) + end rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) - rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) # use known test function to provide exact data calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, - H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, - dGdvperp_M_exact,d2Gdvperp2_M_exact, - d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) - # use numerical integration to find the boundary data - # initialise any arrays needed later for the collision operator - rhsc = fkpl_arrays.rhsc - rhqc = fkpl_arrays.rhqc - sc = fkpl_arrays.sc - qc = fkpl_arrays.qc - rhsc_check = Array{mk_float,1}(undef,nc_global) - rhsc_err = Array{mk_float,1}(undef,nc_global) - rhsvpavperp = fkpl_arrays.rhsvpavperp - - YY_arrays = fkpl_arrays.YY_arrays - - finish_init_time = now() - - begin_serial_region() - # do the numerical integration at the boundaries (N.B. G not supported) - @serial_region begin - println("begin boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - calculate_rosenbluth_potential_boundary_data!(rpbd,fkpl_arrays.bwgt,F_M,vpa,vperp,vpa_spectral,vperp_spectral) - @serial_region begin - println("finished boundary data calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - #rpbd = rpbd_exact + H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, + dGdvperp_M_exact,d2Gdvperp2_M_exact, + d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) @serial_region begin - println("begin elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) + println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) end - + + fokker_planck_collision_operator_weak_form!(Fs_M,F_M,ms,msp,nussp, + fkpl_arrays, + vperp, vpa, vperp_spectral, vpa_spectral, + test_assembly_serial=test_parallelism, + impose_zero_gradient_BC=impose_zero_gradient_BC) + # extract C[Fs,Fs'] result + # and Rosenbluth potentials for testing begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*F_M[ivpa,ivperp] - + C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + H_M_num[ivpa,ivperp] = fkpl_arrays.HH[ivpa,ivperp] + dHdvpa_M_num[ivpa,ivperp] = fkpl_arrays.dHdvpa[ivpa,ivperp] + dGdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dHdvperp[ivpa,ivperp] + dGdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dGdvperp[ivpa,ivperp] + d2Gdvperp2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperp2[ivpa,ivperp] + d2Gdvpa2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvpa2[ivpa,ivperp] + d2Gdvperpdvpa_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperpdvpa[ivpa,ivperp] end - elliptic_solve!(H_M_num,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa_M_num,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp_M_num,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + S_dummy = fkpl_arrays.S_dummy begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - - end - elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2_M_num,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp_M_num,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa_M_num,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - d2Gdvpa2_M_num[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp_M_num[ivpa,ivperp] - end - # use the elliptic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - elliptic_solve!(d2Gdvperp2_M_num,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - @serial_region begin - println("finished elliptic solve ", Dates.format(now(), dateformat"H:MM:SS")) end + # solve for G as an added test bonus + elliptic_solve!(G_M_num,S_dummy,fkpl_arrays.rpbd.G_data, + fkpl_arrays.lu_obj_LP,fkpl_arrays.MM2D_sparse,fkpl_arrays.rhsc, + fkpl_arrays.sc,vpa,vperp) + - @serial_region begin - println("begin C calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - if test_parallelism - assemble_explicit_collision_operator_rhs_serial!(rhsc_check,Fs_M, - d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, - dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, - vpa,vperp,YY_arrays) - @serial_region begin - println("finished C RHS assembly (serial) ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,Fs_M, - d2Gdvpa2_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num, - dHdvpa_M_num,dHdvperp_M_num,ms,msp,nussp, - vpa,vperp,YY_arrays) - # switch back to serial region before matrix inverse - begin_serial_region() - @serial_region begin - println("finished C RHS assembly (parallel) ", Dates.format(now(), dateformat"H:MM:SS")) - end - if test_parallelism - @serial_region begin - @. rhsc_err = abs(rhsc - rhsc_check) - println("maximum(rhsc_err) (test parallelisation): ",maximum(rhsc_err)) - end - end - if impose_zero_gradient_BC - enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ rhsc - else - enforce_zero_bc!(rhsc,vpa,vperp) - # invert mass matrix and fill fc - fc = lu_obj_MM \ rhsc - end - #ravel_c_to_vpavperp!(C_M_num,fc,nc_global,vpa.n) - ravel_c_to_vpavperp_parallel!(C_M_num,fc,vpa.n) init_time = Dates.value(finish_init_time - start_init_time) calculate_time = Dates.value(now() - finish_init_time) begin_serial_region() @@ -496,7 +400,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) # test the boundary data calculation - test_rosenbluth_potential_boundary_data(rpbd,rpbd_exact,vpa,vperp) + test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) @@ -566,8 +470,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ end initialize_comms!() - ngrid = 9 - plot_scan = false#true + ngrid = 5 + plot_scan = true plot_test_output = false impose_zero_gradient_BC = false test_parallelism = false @@ -578,9 +482,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ #nelement_list = Int[2, 4, 8] #nelement_list = Int[4, 8, 16, 32, 64] #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] + nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] - nelement_list = Int[8] + #nelement_list = Int[8] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl index 2b722b426..82610fde1 100644 --- a/fkpl_functional_test.jl +++ b/fkpl_functional_test.jl @@ -151,12 +151,12 @@ if abspath(PROGRAM_FILE) == @__FILE__ boltzmann_electron_response, false, 1:n_ion_species, n_ion_species+1:n_ion_species, 1.0, 1.0, 1.0, 0.0, 0.0, false, 1.0, 1.0, 0.0, allocate_float(n_ion_species)) nuii = 1.0 - collisions = collisions_input(0.0, 0.0, false, nuii, 0.0, 0.0, "none") + collisions = collisions_input(0.0, 0.0, false, false, nuii, 0.0, 0.0, "none") rk_coefs = setup_runge_kutta_coefficients(4) advance = advance_info(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, rk_coefs, - false, false, true, true, false, false) + false, false, true, true, false, false, false) # Set up MPI if standalone diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 50f14cc2f..2c2a632ca 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -5,12 +5,14 @@ module fokker_planck export init_fokker_planck_collisions, fokkerplanck_arrays_struct -export init_fokker_planck_collisions_new +export init_fokker_planck_collisions_weak_form +export explicit_fokker_planck_collisions_weak_form! export explicit_fokker_planck_collisions! export calculate_Maxwellian_Rosenbluth_coefficients export get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing export symmetric_matrix_inverse +export fokker_planck_collision_operator_weak_form! using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature @@ -27,12 +29,17 @@ using ..looping 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 +using ..fokker_planck_calculus: allocate_rosenbluth_potential_boundary_data using ..fokker_planck_calculus: fokkerplanck_arrays_struct, fokkerplanck_weakform_arrays_struct using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse +using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial! +using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel! using ..fokker_planck_calculus: calculate_YY_arrays +using ..fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data! +using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, ravel_c_to_vpavperp_parallel! using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes """ allocate the required ancilliary arrays @@ -83,12 +90,13 @@ at the boundary and using an elliptic solve to obtain the potentials in the rest of the velocity space domain. """ -function init_fokker_planck_collisions_new(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false) +function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false) bwgt = allocate_boundary_integration_weights(vpa,vperp) if vperp.n > 1 && precompute_weights @views init_Rosenbluth_potential_boundary_integration_weights!(bwgt.G0_weights, bwgt.G1_weights, bwgt.H0_weights, bwgt.H1_weights, bwgt.H2_weights, bwgt.H3_weights, vpa, vperp) end + rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) if test_dense_matrix_construction MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, @@ -121,14 +129,185 @@ function init_fokker_planck_collisions_new(vpa,vperp,vpa_spectral,vperp_spectral rhqc = allocate_shared_float(nc) sc = allocate_shared_float(nc) qc = allocate_shared_float(nc) - fka = fokkerplanck_weakform_arrays_struct(bwgt,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, + + CC = allocate_shared_float(nvpa,nvperp) + HH = allocate_shared_float(nvpa,nvperp) + dHdvpa = allocate_shared_float(nvpa,nvperp) + dHdvperp = allocate_shared_float(nvpa,nvperp) + dGdvperp = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + + fka = fokkerplanck_weakform_arrays_struct(bwgt,rpbd,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, LP2D_sparse,LV2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, PPpar2D_sparse,MMparMNperp2D_sparse,MM2DZG_sparse, lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV, - YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc) + YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc, + CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa) return fka end +""" +Function for advancing with the explicit, weak-form, self-collision operator +""" + +function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,composition,collisions,dt, + fkpl_arrays::fokkerplanck_weakform_arrays_struct, + r, z, vperp, vpa, vperp_spectral, vpa_spectral; + test_assembly_serial=false,impose_zero_gradient_BC=false) + # N.B. only self-collisions are currently supported + # This can be modified by adding a loop over s' below + n_ion_species = composition.n_ion_species + @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) + @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) + @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) + @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + + # masses and collision frequencies + ms, msp = 1.0, 1.0 # generalise! + nussp = collisions.nuii # generalise! + + # N.B. parallelisation is only over vpa vperp + # ensure s, r, z are local before initiating the s, r, z loop + begin_serial_region() + @loop_s_r_z is ir iz begin + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # first argument is Fs, and second argument is Fs' in C[Fs,Fs'] + @views fokker_planck_collision_operator_weak_form!(pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,is],ms,msp,nussp, + fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) + # advance this part of s,r,z with the resulting C[Fs,Fs] + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + pdf_out[ivpa,ivperp,iz,ir,is] += dt*fkpl_arrays.CC[ivpa,ivperp] + end + end + return nothing +end + +""" +Function for evaluating Css' = Css'[Fs,Fs'] +""" +function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp, + fkpl_arrays::fokkerplanck_weakform_arrays_struct, + vperp, vpa, vperp_spectral, vpa_spectral; + test_assembly_serial=false,impose_zero_gradient_BC=false) + @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) + @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) + @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) + @boundscheck vperp.n == size(ffs_in,2) || throw(BoundsError(ffs_in)) + + # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays + MM2D_sparse = fkpl_arrays.MM2D_sparse + KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse + KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse + LP2D_sparse = fkpl_arrays.LP2D_sparse + LV2D_sparse = fkpl_arrays.LV2D_sparse + PUperp2D_sparse = fkpl_arrays.PUperp2D_sparse + PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse + PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse + MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse + MM2DZG_sparse = fkpl_arrays.MM2DZG_sparse + lu_obj_MM = fkpl_arrays.lu_obj_MM + lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG + lu_obj_LP = fkpl_arrays.lu_obj_LP + lu_obj_LV = fkpl_arrays.lu_obj_LV + + S_dummy = fkpl_arrays.S_dummy + Q_dummy = fkpl_arrays.Q_dummy + rhsc = fkpl_arrays.rhsc + rhqc = fkpl_arrays.rhqc + sc = fkpl_arrays.sc + qc = fkpl_arrays.qc + rhsvpavperp = fkpl_arrays.rhsvpavperp + YY_arrays = fkpl_arrays.YY_arrays + bwgt = fkpl_arrays.bwgt + rpbd = fkpl_arrays.rpbd + + CC = fkpl_arrays.CC + HH = fkpl_arrays.HH + dHdvpa = fkpl_arrays.dHdvpa + dHdvperp = fkpl_arrays.dHdvperp + dGdvperp = fkpl_arrays.dGdvperp + d2Gdvperp2 = fkpl_arrays.d2Gdvperp2 + d2Gdvpa2 = fkpl_arrays.d2Gdvpa2 + d2Gdvperpdvpa = fkpl_arrays.d2Gdvperpdvpa + + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # calculate the boundary data + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:,]),vpa,vperp,vpa_spectral,vperp_spectral) + # carry out the elliptic solves required + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] + end + elliptic_solve!(HH,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + + end + #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + end + # use the elliptic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + + # assemble the RHS of the collision operator matrix eq + if test_assembly_serial + assemble_explicit_collision_operator_rhs_serial!(rhsc,@view(ffs_in[:,:,]), + d2Gdvpa,d2Gdvperpdvpa,d2Gdvperp2, + dHdvpa,dHdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays) + else + assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,@view(ffs_in[:,:,]), + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2, + dHdvpa,dHdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays) + end + # solve the collision operator matrix eq + if impose_zero_gradient_BC + enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + fc = lu_obj_MMZG \ rhsc + else + enforce_zero_bc!(rhsc,vpa,vperp) + # invert mass matrix and fill fc + fc = lu_obj_MM \ rhsc + end + ravel_c_to_vpavperp_parallel!(CC,fc,vpa.n) + return nothing +end + """ function that initialises the arrays needed for Fokker Planck collisions """ diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index e566d00fd..569e66b2a 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -17,6 +17,7 @@ export YY_collision_operator_arrays, calculate_YY_arrays export calculate_rosenbluth_potential_boundary_data! export elliptic_solve! export fokkerplanck_arrays_struct +export fokkerplanck_weakform_arrays_struct # testing export calculate_rosenbluth_potential_boundary_data_exact! @@ -119,6 +120,22 @@ struct fokkerplanck_boundary_data_arrays_struct dfdvperp::MPISharedArray{mk_float,2} end +struct vpa_vperp_boundary_data + lower_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vpa::MPISharedArray{mk_float,1} + upper_boundary_vperp::MPISharedArray{mk_float,1} +end + +struct rosenbluth_potential_boundary_data + H_data::vpa_vperp_boundary_data + dHdvpa_data::vpa_vperp_boundary_data + dHdvperp_data::vpa_vperp_boundary_data + G_data::vpa_vperp_boundary_data + dGdvperp_data::vpa_vperp_boundary_data + d2Gdvperp2_data::vpa_vperp_boundary_data + d2Gdvperpdvpa_data::vpa_vperp_boundary_data + d2Gdvpa2_data::vpa_vperp_boundary_data +end struct YY_collision_operator_arrays # let phi_j(vperp) be the jth Lagrange basis function, @@ -149,6 +166,8 @@ for the weak-form Fokker-Planck collision operator struct fokkerplanck_weakform_arrays_struct{N} # boundary weights (Green's function) data bwgt::fokkerplanck_boundary_data_arrays_struct + # dummy arrays for boundary data calculation + rpbd::rosenbluth_potential_boundary_data # assembled 2D weak-form matrices MM2D_sparse::AbstractSparseArray{mk_float,mk_int,N} KKpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} @@ -175,6 +194,16 @@ struct fokkerplanck_weakform_arrays_struct{N} rhqc::MPISharedArray{mk_float,1} sc::MPISharedArray{mk_float,1} qc::MPISharedArray{mk_float,1} + # dummy array for the result of the calculation + CC::MPISharedArray{mk_float,2} + # dummy arrays for storing Rosenbluth potentials + HH::MPISharedArray{mk_float,2} + dHdvpa::MPISharedArray{mk_float,2} + dHdvperp::MPISharedArray{mk_float,2} + dGdvperp::MPISharedArray{mk_float,2} + d2Gdvperp2::MPISharedArray{mk_float,2} + d2Gdvpa2::MPISharedArray{mk_float,2} + d2Gdvperpdvpa::MPISharedArray{mk_float,2} end function allocate_boundary_integration_weight(vpa,vperp) @@ -928,22 +957,6 @@ end function create_sparse_matrix(data::sparse_matrix_constructor) return sparse(data.II,data.JJ,data.SS) end -struct vpa_vperp_boundary_data - lower_boundary_vpa::MPISharedArray{mk_float,1} - upper_boundary_vpa::MPISharedArray{mk_float,1} - upper_boundary_vperp::MPISharedArray{mk_float,1} -end - -struct rosenbluth_potential_boundary_data - H_data::vpa_vperp_boundary_data - dHdvpa_data::vpa_vperp_boundary_data - dHdvperp_data::vpa_vperp_boundary_data - G_data::vpa_vperp_boundary_data - dGdvperp_data::vpa_vperp_boundary_data - d2Gdvperp2_data::vpa_vperp_boundary_data - d2Gdvperpdvpa_data::vpa_vperp_boundary_data - d2Gdvpa2_data::vpa_vperp_boundary_data -end function allocate_boundary_data(vpa,vperp) lower_boundary_vpa = Array{mk_float,1}(undef,vperp.n) diff --git a/src/input_structs.jl b/src/input_structs.jl index 997be4497..0dd8838ee 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -66,6 +66,7 @@ mutable struct advance_info r_diffusion::Bool #flag to control how r bc is imposed when r diffusion terms are present vpa_diffusion::Bool #flag to control how vpa bc is imposed when vpa diffusion terms are present explicit_fp_collisions::Bool + explicit_weakform_fp_collisions::Bool explicit_fp_F_FM_collisions::Bool explicit_krook_collisions::Bool end @@ -302,6 +303,8 @@ mutable struct collisions_input ionization::mk_float # if constant_ionization_rate = true, use an ionization term that is constant in z constant_ionization_rate::Bool + # Fokker-Planck operator choice + weakform_fokker_planck::Bool # ion-ion self collision frequency nuii::mk_float # ion-ion self collision frequency with C[F_s,F_Ms'] operator diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 45c9fffce..6837bca62 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -182,6 +182,7 @@ function mk_input(scan_input=Dict()) collisions.charge_exchange = get(scan_input, "charge_exchange_frequency", 2.0*sqrt(species.charged[1].initial_temperature)) collisions.ionization = get(scan_input, "ionization_frequency", collisions.charge_exchange) collisions.constant_ionization_rate = get(scan_input, "constant_ionization_rate", false) + collisions.weakform_fokker_planck = get(scan_input, "weakform_fokker_planck", true) collisions.nuii = get(scan_input, "nuii", 0.0) collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) collisions.nuii_krook = get(scan_input, "nuii_krook", 0.0) @@ -881,11 +882,13 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # ionization collision frequency ionization = 0.0 constant_ionization_rate = false + weakform_fokker_planck = true nuii = 0.0 nuii_pitch = 0.0 nuii_krook = 0.0 numerical_conserving_terms = "density" - collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, nuii, nuii_pitch, nuii_krook, numerical_conserving_terms) + collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, weakform_fokker_planck, + nuii, nuii_pitch, nuii_krook, numerical_conserving_terms) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength diff --git a/src/time_advance.jl b/src/time_advance.jl index 6968923e1..bc04aa9b3 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -45,7 +45,8 @@ using ..continuity: continuity_equation! using ..force_balance: force_balance! using ..energy_equation: energy_equation! using ..em_fields: setup_em_fields, update_phi! -using ..fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions!, explicit_fokker_planck_collisions_Maxwellian_coefficients! +using ..fokker_planck: init_fokker_planck_collisions_weak_form, init_fokker_planck_collisions, explicit_fokker_planck_collisions! +using ..fokker_planck: explicit_fokker_planck_collisions_weak_form!, explicit_fokker_planck_collisions_Maxwellian_coefficients! using ..collision_models: explicit_krook_collisions! #using ..semi_lagrange: setup_semi_lagrange using Dates @@ -220,9 +221,16 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance_force_balance = false advance_energy = false if collisions.nuii > 0.0 && vperp.n > 1 - explicit_fp_collisions = true + if collisions.weakform_fokker_planck + explicit_fp_collisions = false + explicit_weakform_fp_collisions = true + else + explicit_fp_collisions = true + explicit_weakform_fp_collisions = false + end else explicit_fp_collisions = false + explicit_weakform_fp_collisions = false end if collisions.nuii_pitch > 0.0 && vperp.n > 1 explicit_fp_F_FM_collisions = true @@ -241,7 +249,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, advance = advance_info(advance_vpa_advection, advance_z_advection, advance_r_advection, advance_neutral_z_advection, advance_neutral_r_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, advance_ionization_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, advance_energy, rk_coefs, - manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_fp_F_FM_collisions, explicit_krook_collisions) + manufactured_solns_test, r_diffusion, vpa_diffusion, explicit_fp_collisions, explicit_weakform_fp_collisions, + explicit_fp_F_FM_collisions, explicit_krook_collisions) if z.discretization == "chebyshev_pseudospectral" && z.n > 1 @@ -340,7 +349,13 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, composition, 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 - fp_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=explicit_fp_collisions) + if explicit_fp_collisions + fp_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + elseif explicit_weakform_fp_collisions + fp_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true) + else + fp_arrays = init_fokker_planck_collisions(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false) + end # create the "fields" structure that contains arrays # for the electrostatic potential phi and eventually the electromagnetic fields fields = setup_em_fields(z.n, r.n, drive_input.force_phi, drive_input.amplitude, drive_input.frequency, drive_input.force_Er_zero_at_wall) @@ -1092,6 +1107,10 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, diagnose_entropy_production = update_entropy_diagnostic) #println(moments.charged.dSdt) end + if advance.explicit_weakform_fp_collisions + explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,composition,collisions,dt, + fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral) + end if advance.explicit_fp_F_FM_collisions explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, fvec_in.density, fvec_in.upar, moments.charged.vth, From 3a53caf73a15ef916d8fcfe1e8bb73d4400af6b0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 31 Oct 2023 15:47:14 +0000 Subject: [PATCH 197/331] Correct diagnostic L2 norm to have the previously missing sqrt(). --- src/post_processing.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/post_processing.jl b/src/post_processing.jl index 35202909d..06be82ae4 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -2044,8 +2044,9 @@ function plot_Maxwellian_diagnostic(ff, density, parallel_flow, thermal_speed, v ff_norm = copy(time) for is in 1:n_ion_species for it in 1:ntime - @views ff_norm[it] = integrate_over_vspace( (ff[:,:,is,it] .- ff_Maxwellian[:,:,is,it]).^2 , vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) - @views ff_norm[it] /= integrate_over_vspace(ff_ones[:,:,is,it], vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) + @views num = integrate_over_vspace( (ff[:,:,is,it] .- ff_Maxwellian[:,:,is,it]).^2 , vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) + @views denom = integrate_over_vspace(ff_ones[:,:,is,it], vpa_local, 0, vpa_local_wgts, vperp_local, 0, vperp_local_wgts) + ff_norm[it] = sqrt(num/denom) end iz0_string = string("_iz0", string(iz0)) ir0_string = string("_ir0", string(ir0)) From c0cc4a2af394b4b4220d787124d8c1a276e557a9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 31 Oct 2023 19:50:21 +0000 Subject: [PATCH 198/331] Corrected bugs in the usage of the shared-memory formalism. --- src/file_io.jl | 51 ++++++++++++++++------------------- src/fokker_planck.jl | 23 +++++++++------- src/fokker_planck_calculus.jl | 37 +++++++++++++++---------- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/file_io.jl b/src/file_io.jl index 7485eb7dc..58908356b 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -647,37 +647,37 @@ end append_to_dynamic_var(io_moments.time, t, t_idx) # add the electrostatic potential and electric field components at this time slice to the hdf5 file - append_to_dynamic_var(io_moments.phi.data, fields.phi, t_idx, z, r) - append_to_dynamic_var(io_moments.Er.data, fields.Er, t_idx, z, r) - append_to_dynamic_var(io_moments.Ez.data, fields.Ez, t_idx, z, r) + append_to_dynamic_var(io_moments.phi, fields.phi.data, t_idx, z, r) + append_to_dynamic_var(io_moments.Er, fields.Er.data, t_idx, z, r) + append_to_dynamic_var(io_moments.Ez, fields.Ez.data, t_idx, z, r) # add the density data at this time slice to the output file - append_to_dynamic_var(io_moments.density.data, moments.charged.dens, t_idx, z, + append_to_dynamic_var(io_moments.density, moments.charged.dens.data, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_flow.data, moments.charged.upar, + append_to_dynamic_var(io_moments.parallel_flow, moments.charged.upar.data, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_pressure.data, moments.charged.ppar, + append_to_dynamic_var(io_moments.parallel_pressure, moments.charged.ppar.data, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.perpendicular_pressure.data, moments.charged.pperp, + append_to_dynamic_var(io_moments.perpendicular_pressure, moments.charged.pperp.data, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_heat_flux.data, - moments.charged.qpar, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.thermal_speed.data, moments.charged.vth, + append_to_dynamic_var(io_moments.parallel_heat_flux, + moments.charged.qpar.data, t_idx, z, r, n_ion_species) + append_to_dynamic_var(io_moments.thermal_speed, moments.charged.vth.data, t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.entropy_production.data, moments.charged.dSdt, + append_to_dynamic_var(io_moments.entropy_production, moments.charged.dSdt.data, t_idx, z, r, n_ion_species) if n_neutral_species > 0 - append_to_dynamic_var(io_moments.density_neutral.data, - moments.neutral.dens, t_idx, z, r, + append_to_dynamic_var(io_moments.density_neutral, + moments.neutral.dens.data, t_idx, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.uz_neutral.data, moments.neutral.uz, + append_to_dynamic_var(io_moments.uz_neutral, moments.neutral.uz.data, t_idx, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.pz_neutral.data, moments.neutral.pz, + append_to_dynamic_var(io_moments.pz_neutral, moments.neutral.pz.data, t_idx, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.qz_neutral.data, moments.neutral.qz, + append_to_dynamic_var(io_moments.qz_neutral, moments.neutral.qz.data, t_idx, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.thermal_speed_neutral.data, - moments.neutral.vth, t_idx, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.thermal_speed_neutral, + moments.neutral.vth.data, t_idx, z, r, n_neutral_species) end end return nothing @@ -685,11 +685,8 @@ end end @debug_shared_array begin - # Special versions when using DebugMPISharedArray to avoid implicit conversion to - # Array, which is forbidden. - function write_dfns_data_to_binary(ff::DebugMPISharedArray, ff_neutral::DebugMPISharedArray, - t, n_ion_species, n_neutral_species, h5::hdf5_dfns_info, t_idx, r, z, vperp, - vpa, vzeta, vr, vz) + function write_dfns_data_to_binary(ff::DebugMPISharedArray, ff_neutral::DebugMPISharedArray, t, n_ion_species, n_neutral_species, + io_dfns, t_idx, r, z, vperp, vpa, vzeta, vr, vz) @serial_region begin # Only read/write from first process in each 'block' @@ -697,12 +694,10 @@ end append_to_dynamic_var(io_dfns.time, t, t_idx) # add the distribution function data at this time slice to the output file - # add the distribution function data at this time slice to the output file - append_to_dynamic_var(io_dfns.f, ff.data, t_idx, vpa, vperp, z, r, - n_ion_species) + append_to_dynamic_var(io_dfns.f, ff.data, t_idx, vpa, vperp, z, r, n_ion_species) if n_neutral_species > 0 - append_to_dynamic_var(io_dfns.f_neutral, ff_neutral.data, t_idx, vz, vr, - vzeta, z, r, n_neutral_species) + append_to_dynamic_var(io_dfns.f_neutral, ff_neutral.data, t_idx, vz, vr, vzeta, z, + r, n_neutral_species) end end return nothing diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 2c2a632ca..6aef825f8 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -176,7 +176,7 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,composition # N.B. parallelisation is only over vpa vperp # ensure s, r, z are local before initiating the s, r, z loop - begin_serial_region() + begin_vperp_vpa_region() @loop_s_r_z is ir iz begin # the functions within this loop will call # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays @@ -295,16 +295,19 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp vpa,vperp,YY_arrays) end # solve the collision operator matrix eq - if impose_zero_gradient_BC - enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ rhsc - else - enforce_zero_bc!(rhsc,vpa,vperp) - # invert mass matrix and fill fc - fc = lu_obj_MM \ rhsc + begin_serial_region() + @serial_region begin + if impose_zero_gradient_BC + enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) + # invert mass matrix and fill fc + sc .= lu_obj_MMZG \ rhsc + else + enforce_zero_bc!(rhsc,vpa,vperp) + # invert mass matrix and fill fc + sc .= lu_obj_MM \ rhsc + end end - ravel_c_to_vpavperp_parallel!(CC,fc,vpa.n) + ravel_c_to_vpavperp_parallel!(CC,sc,vpa.n) return nothing end diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 569e66b2a..1f592c8dc 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -959,9 +959,9 @@ function create_sparse_matrix(data::sparse_matrix_constructor) end function allocate_boundary_data(vpa,vperp) - lower_boundary_vpa = Array{mk_float,1}(undef,vperp.n) - upper_boundary_vpa = Array{mk_float,1}(undef,vperp.n) - upper_boundary_vperp = Array{mk_float,1}(undef,vpa.n) + lower_boundary_vpa = allocate_shared_float(vperp.n) + upper_boundary_vpa = allocate_shared_float(vperp.n) + upper_boundary_vperp = allocate_shared_float(vpa.n) return vpa_vperp_boundary_data(lower_boundary_vpa, upper_boundary_vpa,upper_boundary_vperp) end @@ -1932,6 +1932,7 @@ function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdf @loop_vperp_vpa ivperp ivpa begin rhsvpavperp[ivpa,ivperp] = 0.0 end + # loop over collocation points to benefit from shared-memory parallelism ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid @loop_vperp_vpa ivperp_global ivpa_global begin @@ -2002,11 +2003,13 @@ function elliptic_solve!(field,source,boundary_data::vpa_vperp_boundary_data, ravel_vpavperp_to_c_parallel!(sc,source,vpa.n) # assemble the rhs of the weak system begin_serial_region() - mul!(rhsc,matrix_rhs,sc) - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) - # solve the linear system - sc = lu_object_lhs \ rhsc + @serial_region begin + mul!(rhsc,matrix_rhs,sc) + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc,vpa,vperp,boundary_data) + # solve the linear system + sc .= lu_object_lhs \ rhsc + end # get data into the vpa vperp indices format begin_vperp_vpa_region() ravel_c_to_vpavperp_parallel!(field,sc,vpa.n) @@ -2023,16 +2026,22 @@ function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_bounda # assemble the rhs of the weak system begin_serial_region() - mul!(rhsc_1,matrix_rhs_1,sc_1) - mul!(rhsc_2,matrix_rhs_2,sc_2) + @serial_region begin + mul!(rhsc_1,matrix_rhs_1,sc_1) + mul!(rhsc_2,matrix_rhs_2,sc_2) + end + begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin ic = ic_func(ivpa,ivperp,vpa.n) rhsc_1[ic] += rhsc_2[ic] end - # enforce the boundary conditions - enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) - # solve the linear system - sc_1 = lu_object_lhs \ rhsc_1 + begin_serial_region() + @serial_region begin + # enforce the boundary conditions + enforce_dirichlet_bc!(rhsc_1,vpa,vperp,boundary_data) + # solve the linear system + sc_1 .= lu_object_lhs \ rhsc_1 + end # get data into the vpa vperp indices format begin_vperp_vpa_region() ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) From b3e7cc78b816fdbb00c4474a8fc48964fd9975f9 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 1 Nov 2023 08:40:32 +0000 Subject: [PATCH 199/331] Provided entropy production diagnostic for weak-form collision operator. For nelement_vpa = 8 and nelement_vperp = 4, with ngrid = 5 for both dimensions, we see a positive entropy production (up to the end of the test simulation at t(/Lref/cref)=1.0. --- src/fokker_planck.jl | 22 +++++++++++++++++++--- src/time_advance.jl | 6 ++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6aef825f8..6923eede3 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -152,10 +152,11 @@ end Function for advancing with the explicit, weak-form, self-collision operator """ -function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,composition,collisions,dt, +function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,composition,collisions,dt, fkpl_arrays::fokkerplanck_weakform_arrays_struct, r, z, vperp, vpa, vperp_spectral, vpa_spectral; - test_assembly_serial=false,impose_zero_gradient_BC=false) + test_assembly_serial=false,impose_zero_gradient_BC=false, + diagnose_entropy_production=false) # N.B. only self-collisions are currently supported # This can be modified by adding a loop over s' below n_ion_species = composition.n_ion_species @@ -169,6 +170,9 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,composition @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(dSdt,1) || throw(BoundsError(dSdt)) + @boundscheck r.n == size(dSdt,2) || throw(BoundsError(dSdt)) + @boundscheck n_ion_species == size(dSdt,3) || throw(BoundsError(dSdt)) # masses and collision frequencies ms, msp = 1.0, 1.0 # generalise! @@ -184,9 +188,21 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,composition @views fokker_planck_collision_operator_weak_form!(pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,is],ms,msp,nussp, fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) # advance this part of s,r,z with the resulting C[Fs,Fs] + Css = fkpl_arrays.CC begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin - pdf_out[ivpa,ivperp,iz,ir,is] += dt*fkpl_arrays.CC[ivpa,ivperp] + pdf_out[ivpa,ivperp,iz,ir,is] += dt*Css[ivpa,ivperp] + end + if diagnose_entropy_production + # assign dummy array + lnfC = fkpl_arrays.rhsvpavperp + @loop_vperp_vpa ivperp ivpa begin + lnfC[ivpa,ivperp] = log(abs(pdf_in[ivpa,ivperp,iz,ir,is]) + 1.0e-15)*Css[ivpa,ivperp] + end + begin_serial_region() + @serial_region begin + dSdt[iz,ir,is] = -get_density(lnfC,vpa,vperp) + end end end return nothing diff --git a/src/time_advance.jl b/src/time_advance.jl index bc04aa9b3..e58e9be69 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -1108,8 +1108,10 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, #println(moments.charged.dSdt) end if advance.explicit_weakform_fp_collisions - explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,composition,collisions,dt, - fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral) + update_entropy_diagnostic = (istage == 1) + explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition,collisions,dt, + fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral, + diagnose_entropy_production = update_entropy_diagnostic) end if advance.explicit_fp_F_FM_collisions explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, From 4c77b372cb2bdcc96953933df7bd4ced6b57aec7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 1 Nov 2023 09:08:06 +0000 Subject: [PATCH 200/331] Make initialisation print statements consistent with distributed memory MPI by only printing on the global_rank[] = 0. --- src/fokker_planck.jl | 10 ++++-- src/fokker_planck_calculus.jl | 68 +++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6923eede3..a77a6b1cc 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -21,7 +21,7 @@ using LinearAlgebra: lu using ..initial_conditions: enforce_boundary_conditions! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float -using ..communication: MPISharedArray +using ..communication: MPISharedArray, global_rank using ..velocity_moments: integrate_over_vspace using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom using ..calculus: derivative!, second_derivative! @@ -113,12 +113,16 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) @serial_region begin - println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) + end end YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) @serial_region begin - println("finished YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end end nvpa, nvperp = vpa.n, vperp.n nc = nvpa*nvperp diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 1f592c8dc..bac0becc9 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -31,7 +31,7 @@ using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..calculus: derivative! using ..communication -using ..communication: MPISharedArray +using ..communication: MPISharedArray, global_rank using ..looping using moment_kinetics.gauss_legendre: get_QQ_local! using Dates @@ -242,7 +242,9 @@ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0 x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) @serial_region begin - println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end end # precalculated weights, integrating over Lagrange polynomials @@ -279,7 +281,9 @@ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0 @serial_region begin - println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end end return nothing end @@ -291,7 +295,9 @@ Green's function. """ function setup_basic_quadratures(vpa,vperp) @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) + end end # get Gauss-Legendre points and weights on (-1,1) @@ -338,7 +344,9 @@ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) @serial_region begin - println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end end # precalculate weights, integrating over Lagrange polynomials @@ -449,7 +457,9 @@ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, # return the parallelisation status to serial begin_serial_region() @serial_region begin - println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) + end end return nothing end @@ -1320,7 +1330,9 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe impose_BC_at_zero_vperp = false @serial_region begin - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end end for ielement_vperp in 1:vperp.nelement_local get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") @@ -1444,8 +1456,9 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe end end @serial_region begin - println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) - + if global_rank[] == 0 + println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end if nc_global < 60 print_matrix(MM2D,"MM2D",nc_global,nc_global) #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) @@ -1454,7 +1467,9 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe #print_matrix(LV2D,"LV",nc_global,nc_global) end # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end end MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) @@ -1481,7 +1496,9 @@ function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vpe MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) @serial_region begin - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end end for ielement_vperp in 1:vperp.nelement_local get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") @@ -1536,12 +1553,16 @@ function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vpe end end @serial_region begin - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end if nc_global < 30 print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) end # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end end MM2DZG_sparse = sparse(MM2DZG) return MM2DZG_sparse @@ -1586,7 +1607,9 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp impose_BC_at_zero_vperp = false @serial_region begin - println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end end for ielement_vperp in 1:nelement_vperp get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") @@ -1733,8 +1756,9 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp PPpar2D_sparse = create_sparse_matrix(PPpar2D) MMparMNperp2D_sparse = create_sparse_matrix(MMparMNperp2D) @serial_region begin - println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) - + if global_rank[] == 0 + println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) + end if nc_global < 60 println("MM2D_sparse \n",MM2D_sparse) print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) @@ -1768,7 +1792,9 @@ function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse( MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) @serial_region begin - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end end for ielement_vperp in 1:vperp.nelement_local get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") @@ -1829,12 +1855,16 @@ function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse( end end @serial_region begin - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) + end #if nc_global < 30 # print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) #end # convert these matrices to sparse matrices - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + if global_rank[] == 0 + println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) + end end MM2DZG_sparse = create_sparse_matrix(MM2DZG) return MM2DZG_sparse From 922b0981c298a360ba619e90e63dce16224524fc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 2 Nov 2023 09:50:26 +0000 Subject: [PATCH 201/331] Changes which permit precompilation - still expect to be broken at runtime. --- src/chebyshev.jl | 15 ++------------- src/coordinates.jl | 4 ++-- src/post_processing.jl | 38 +++++++++++++++++++++----------------- src/time_advance.jl | 1 + 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index 6e1c4bbe5..516dd92ff 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -10,6 +10,7 @@ export scaled_chebyshev_radau_grid export chebyshev_spectral_derivative! export chebyshev_info export chebyshev_base_info +export chebyshev_derivative! using LinearAlgebra: mul! using FFTW @@ -199,11 +200,9 @@ function scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n, end """ - elementwise_derivative!(coord, ff, chebyshev::chebyshev_info) - Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f'. """ -function elementwise_derivative!(coord, ff, chebyshev::chebyshev_info) +function chebyshev_derivative!(df, ff, chebyshev, coord) df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local @@ -296,16 +295,6 @@ function elementwise_derivative!(coord, ff, chebyshev::chebyshev_info) return nothing end -""" - elementwise_derivative!(coord, ff, adv_fac, spectral::chebyshev_info) - -Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f'. - -Note: Chebyshev derivative does not make use of upwinding information within each element. -""" -function elementwise_derivative!(coord, ff, adv_fac, spectral::chebyshev_info) - return elementwise_derivative!(coord, ff, spectral) -end """ """ diff --git a/src/coordinates.jl b/src/coordinates.jl index b752c35f0..2fd1fa9c2 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -10,7 +10,7 @@ using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_int using ..calculus: derivative! using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid, setup_chebyshev_pseudospectral -using ..gauss_legendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid, setup_gauss_legendre_pseudospectral +using ..gauss_legendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid, setup_gausslegendre_pseudospectral using ..quadrature: composite_simpson_weights using ..input_structs: advection_input @@ -176,7 +176,7 @@ function define_coordinate(input, parallel_io::Bool=false) elseif input.discretization == "gausslegendre_pseudospectral" && coord.n > 1 # create arrays needed for explicit GaussLegendre pseudospectral treatment in this # coordinate and create the matrices for differentiation - spectral = setup_gauss_legendre_pseudospectral(coord) + spectral = setup_gausslegendre_pseudospectral(coord) # obtain the local derivatives of the uniform grid with respect to the used grid derivative!(coord.duniform_dgrid, coord.uniform_grid, coord, spectral) else diff --git a/src/post_processing.jl b/src/post_processing.jl index 47c8a6cde..93bff2ba3 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -3266,28 +3266,32 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end end # make a gif animation of f(vpa,vperp,t) at a given (z,r) location - if pp.animate_f_vs_vperp_vpa && vperp.n + if pp.animate_f_vs_vperp_vpa pdf = load_distributed_charged_pdf_slice(run_name, nblocks, itime_min:iskip:itime_max, n_species, r_local, z_local, vperp, vpa; iz=iz0, ir=ir0) - for is ∈ 1:n_species - anim = @animate for i ∈ itime_min:nwrite_movie:itime_max - @views heatmap(vperp.grid, vpa.grid, pdf[:,:,is,i], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) - end - outfile = string(run_name_label, "_pdf_vs_vperp_vpa", iz0_string, ir0_string, spec_string[is], ".gif") - trygif(anim, outfile, fps=5) - - @views heatmap(vperp.grid, vpa.grid, pdf[:,:,is,itime_max], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) - outfile = string(run_name_label, "_pdf_vs_vpa_vperp", ir0_string, iz0_string, spec_string, ".pdf") - savefig(outfile) - elseif pp.animate_f_vs_vperp_vpa && nvperp == 1 - # make a gif animation of ϕ(r) at different times - anim = @animate for i ∈ itime_min:nwrite_movie:itime_max - @views plot(vpa.grid, pdf[:,1,is,i], xlabel="vpa", ylabel="f") + if vperp.n > 1 + for is ∈ 1:n_species + anim = @animate for i ∈ itime_min:nwrite_movie:itime_max + @views heatmap(vperp.grid, vpa.grid, pdf[:,:,is,i], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) + end + outfile = string(run_name_label, "_pdf_vs_vperp_vpa", iz0_string, ir0_string, spec_string[is], ".gif") + trygif(anim, outfile, fps=5) + + @views heatmap(vperp.grid, vpa.grid, pdf[:,:,is,itime_max], xlabel="vperp", ylabel="vpa", c = :deep, interpolation = :cubic) + outfile = string(run_name_label, "_pdf_vs_vpa_vperp", ir0_string, iz0_string, spec_string[is], ".pdf") + savefig(outfile) + end + elseif vperp.n == 1 + for is ∈ 1:n_species + # make a gif animation of ϕ(r) at different times + anim = @animate for i ∈ itime_min:nwrite_movie:itime_max + @views plot(vpa.grid, pdf[:,1,is,i], xlabel="vpa", ylabel="f") + end + outfile = string(run_name_label, "_pdf_vs_vpa", ir0_string, iz0_string, spec_string[is], ".gif") + gif(anim, outfile, fps=5) end - outfile = string(run_name_label, "_pdf_vs_vpa", ir0_string, iz0_string, spec_string, ".gif") - gif(anim, outfile, fps=5) end end # make a gif animation of f(z,r,t) at a given (vpa,vperp) location diff --git a/src/time_advance.jl b/src/time_advance.jl index c2df29e2e..602dbb2c4 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -283,6 +283,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, @serial_region begin for is ∈ 1:n_ion_species @views update_speed_vperp!(vperp_advect[is], vpa, vperp, z, r) + end end end From 40984aed2ff3f2a4f4fa4ddeee3d48d056f7bac1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 2 Nov 2023 13:26:20 +0000 Subject: [PATCH 202/331] Upgrade the gausslegendre module to use the element_spacing and element_shift arrays, remove unused or duplicate functions. Update and include the calculus test script GaussLobattoLegendre_test.jl. Bring the main moment_kinetics source into a running operation for the collision operator d f /d t = C[f,f]. Undiagnosed at the program level. --- 2D_FEM_assembly_test.jl | 407 ++++++++++++------------- GaussLobattoLegendre_test.jl | 554 ++++++++++++---------------------- src/calculus.jl | 4 +- src/chebyshev.jl | 12 +- src/coordinates.jl | 19 +- src/file_io.jl | 11 +- src/gauss_legendre.jl | 556 +++++++++-------------------------- src/initial_conditions.jl | 6 +- src/input_structs.jl | 1 + src/load_data.jl | 3 +- src/time_advance.jl | 19 +- 11 files changed, 562 insertions(+), 1030 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 7967e7882..bab9053c6 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -1,3 +1,4 @@ +export run_assembly_test using Printf using Plots using LaTeXStrings @@ -32,9 +33,7 @@ using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_bou using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") + function print_matrix(matrix,name::String,n::mk_int,m::mk_int) @@ -164,43 +163,18 @@ if abspath(PROGRAM_FILE) == @__FILE__ comm = MPI.COMM_NULL # create the 'input' struct containing input info needed to create a # coordinate + element_spacing_option = "uniform" vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, - nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm) + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, - nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm) + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) # create the coordinate struct 'x' println("made inputs") println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - if vpa.discretization == "chebyshev_pseudospectral" && vpa.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vpa - # and create the plans for the forward and backward fast Chebyshev transforms - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - # obtain the local derivatives of the uniform vpa-grid with respect to the used vpa-grid - #chebyshev_derivative!(vpa.duniform_dgrid, vpa.uniform_grid, vpa_spectral, vpa) - elseif vpa.discretization == "gausslegendre_pseudospectral" && vpa.n > 1 - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - else - # create dummy Bool variable to return in place of the above struct - vpa_spectral = false - #vpa.duniform_dgrid .= 1.0 - end - - if vperp.discretization == "chebyshev_pseudospectral" && vperp.n > 1 - # create arrays needed for explicit Chebyshev pseudospectral treatment in vperp - # and create the plans for the forward and backward fast Chebyshev transforms - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - # obtain the local derivatives of the uniform vperp-grid with respect to the used vperp-grid - #chebyshev_derivative!(vperp.duniform_dgrid, vperp.uniform_grid, vperp_spectral, vperp) - elseif vperp.discretization == "gausslegendre_pseudospectral" && vperp.n > 1 - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - else - # create dummy Bool variable to return in place of the above struct - vperp_spectral = false - #vperp.duniform_dgrid .= 1.0 - end + vpa, vpa_spectral = define_coordinate(vpa_input) + vperp, vperp_spectral = define_coordinate(vperp_input) + # Set up MPI if standalone initialize_comms!() @@ -374,7 +348,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] H_M_num[ivpa,ivperp] = fkpl_arrays.HH[ivpa,ivperp] dHdvpa_M_num[ivpa,ivperp] = fkpl_arrays.dHdvpa[ivpa,ivperp] - dGdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dHdvperp[ivpa,ivperp] + dHdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dHdvperp[ivpa,ivperp] dGdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dGdvperp[ivpa,ivperp] d2Gdvperp2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperp2[ivpa,ivperp] d2Gdvpa2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvpa2[ivpa,ivperp] @@ -427,8 +401,8 @@ if abspath(PROGRAM_FILE) == @__FILE__ if test_self_operator delta_n = get_density(C_M_num, vpa, vperp) delta_upar = get_upar(C_M_num, vpa, vperp, dens) - delta_ppar = get_ppar(C_M_num, vpa, vperp, upar, msp) - delta_pperp = get_pperp(C_M_num, vpa, vperp, msp) + delta_ppar = msp*get_ppar(C_M_num, vpa, vperp, upar) + delta_pperp = msp*get_pperp(C_M_num, vpa, vperp) delta_pressure = get_pressure(delta_ppar,delta_pperp) @serial_region begin println("delta_n: ", delta_n) @@ -469,193 +443,202 @@ if abspath(PROGRAM_FILE) == @__FILE__ end end - initialize_comms!() - ngrid = 5 - plot_scan = true - plot_test_output = false - impose_zero_gradient_BC = false - test_parallelism = false - test_self_operator = true - test_dense_construction = false - #nelement_list = Int[8, 16, 32, 64, 128] - #nelement_list = Int[4, 8, 16, 32, 64] - #nelement_list = Int[2, 4, 8] - #nelement_list = Int[4, 8, 16, 32, 64] - #nelement_list = Int[2, 4, 8, 16, 32] - nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[100] - #nelement_list = Int[8] - nscan = size(nelement_list,1) - max_C_err = Array{mk_float,1}(undef,nscan) - max_H_err = Array{mk_float,1}(undef,nscan) - max_G_err = Array{mk_float,1}(undef,nscan) - max_dHdvpa_err = Array{mk_float,1}(undef,nscan) - max_dHdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_dGdvperp_err = Array{mk_float,1}(undef,nscan) - L2_C_err = Array{mk_float,1}(undef,nscan) - L2_H_err = Array{mk_float,1}(undef,nscan) - L2_G_err = Array{mk_float,1}(undef,nscan) - L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) - #max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) - #max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) - n_err = Array{mk_float,1}(undef,nscan) - u_err = Array{mk_float,1}(undef,nscan) - p_err = Array{mk_float,1}(undef,nscan) - calculate_times = Array{mk_float,1}(undef,nscan) - init_times = Array{mk_float,1}(undef,nscan) - - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - expected_integral = Array{mk_float,1}(undef,nscan) - expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - expected_label = L"(1/N_{el})^{n_g - 1}" - expected_integral_label = L"(1/N_{el})^{n_g +1}" - - expected_t_2 = Array{mk_float,1}(undef,nscan) - expected_t_3 = Array{mk_float,1}(undef,nscan) - expect_timing!(expected_t_2,nelement_list,nscan,2) - expect_timing!(expected_t_3,nelement_list,nscan,3) - expected_t_2_label = L"(N_{element})^2" - expected_t_3_label = L"(N_{element})^3" - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - nelement_vpa = 2*nelement - nelement_vperp = nelement - fkerr, calculate_times[iscan], init_times[iscan] = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, - plot_test_output=plot_test_output, - impose_zero_gradient_BC=impose_zero_gradient_BC, - test_parallelism=test_parallelism, - test_self_operator=test_self_operator, - test_dense_construction=test_dense_construction, - standalone=false) - max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 - max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 - max_dHdvpa_err[iscan], L2_dHdvpa_err[iscan] = fkerr.dHdvpa_M.max ,fkerr.dHdvpa_M.L2 - max_dHdvperp_err[iscan], L2_dHdvperp_err[iscan] = fkerr.dHdvperp_M.max ,fkerr.dHdvperp_M.L2 - max_G_err[iscan], L2_G_err[iscan] = fkerr.G_M.max ,fkerr.G_M.L2 - max_dGdvperp_err[iscan], L2_dGdvperp_err[iscan] = fkerr.dGdvperp_M.max ,fkerr.dGdvperp_M.L2 - max_d2Gdvpa2_err[iscan], L2_d2Gdvpa2_err[iscan] = fkerr.d2Gdvpa2_M.max ,fkerr.d2Gdvpa2_M.L2 - max_d2Gdvperpdvpa_err[iscan], L2_d2Gdvperpdvpa_err[iscan] = fkerr.d2Gdvperpdvpa_M.max ,fkerr.d2Gdvperpdvpa_M.L2 - max_d2Gdvperp2_err[iscan], L2_d2Gdvperp2_err[iscan] = fkerr.d2Gdvperp2_M.max ,fkerr.d2Gdvperp2_M.L2 - n_err[iscan] = abs(fkerr.moments.delta_density) - u_err[iscan] = abs(fkerr.moments.delta_upar) - p_err[iscan] = abs(fkerr.moments.delta_pressure) - end - if global_rank[]==0 && plot_scan - fontsize = 8 - #ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - ytick_sequence = Array([1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1]) - xlabel = L"N_{element}" - Clabel = L"\epsilon_{\infty}(C)" - Hlabel = L"\epsilon_{\infty}(H)" - Glabel = L"\epsilon_{\infty}(G)" - dHdvpalabel = L"\epsilon_{\infty}(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon_{\infty}(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon_{\infty}(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon_{\infty}(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon_{\infty}(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabel = L"\epsilon_{\infty}(dG/d v_{\perp})" + function run_assembly_test(; ngrid=5) + initialize_comms!() + #ngrid = 5 + plot_scan = true + plot_test_output = false + impose_zero_gradient_BC = false + test_parallelism = false + test_self_operator = true + test_dense_construction = false + #nelement_list = Int[8, 16, 32, 64, 128] + #nelement_list = Int[4, 8, 16, 32, 64] + #nelement_list = Int[2, 4, 8] + #nelement_list = Int[4, 8, 16, 32, 64] + #nelement_list = Int[2, 4, 8, 16, 32] + #nelement_list = Int[2, 4, 8, 16] + #nelement_list = Int[100] + nelement_list = Int[8] + nscan = size(nelement_list,1) + max_C_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_G_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + L2_C_err = Array{mk_float,1}(undef,nscan) + L2_H_err = Array{mk_float,1}(undef,nscan) + L2_G_err = Array{mk_float,1}(undef,nscan) + L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) + #max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) + #max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) + n_err = Array{mk_float,1}(undef,nscan) + u_err = Array{mk_float,1}(undef,nscan) + p_err = Array{mk_float,1}(undef,nscan) + calculate_times = Array{mk_float,1}(undef,nscan) + init_times = Array{mk_float,1}(undef,nscan) - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) - plot(nelement_list, [max_C_err,max_H_err,max_G_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel Hlabel Glabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_C_G_H_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - println([max_C_err,max_H_err,max_G_err, expected, expected_integral]) + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" - plot(nelement_list, [max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - println([max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral]) + expected_t_2 = Array{mk_float,1}(undef,nscan) + expected_t_3 = Array{mk_float,1}(undef,nscan) + expect_timing!(expected_t_2,nelement_list,nscan,2) + expect_timing!(expected_t_3,nelement_list,nscan,3) + expected_t_2_label = L"(N_{element})^2" + expected_t_3_label = L"(N_{element})^3" - - ClabelL2 = L"\epsilon_{L2}(C)" - HlabelL2 = L"\epsilon_{L2}(H)" - GlabelL2 = L"\epsilon_{L2}(G)" - dHdvpalabelL2 = L"\epsilon_{L2}(dH/d v_{\|\|})" - dHdvperplabelL2 = L"\epsilon_{L2}(dH/d v_{\perp})" - d2Gdvperp2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp}^2)" - d2Gdvpa2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\|\|}^2)" - d2GdvperpdvpalabelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabelL2 = L"\epsilon_{L2}(dG/d v_{\perp})" - - - plot(nelement_list, [L2_C_err,L2_H_err,L2_G_err, expected, expected_integral], - xlabel=xlabel, label=[ClabelL2 HlabelL2 GlabelL2 expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_C_G_H_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - println([L2_C_err,L2_H_err,L2_G_err, expected, expected_integral]) - - plot(nelement_list, [L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - println([L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral]) - - nlabel = L"|\Delta n|" - ulabel = L"|\Delta u_{\|\|}|" - plabel = L"|\Delta p|" - - if test_self_operator - plot(nelement_list, [max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel ClabelL2 nlabel ulabel plabel expected_label expected_integral_label], ylabel="", + for iscan in 1:nscan + local nelement = nelement_list[iscan] + nelement_vpa = 2*nelement + nelement_vperp = nelement + fkerr, calculate_times[iscan], init_times[iscan] = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, + plot_test_output=plot_test_output, + impose_zero_gradient_BC=impose_zero_gradient_BC, + test_parallelism=test_parallelism, + test_self_operator=test_self_operator, + test_dense_construction=test_dense_construction, + standalone=false) + max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 + max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 + max_dHdvpa_err[iscan], L2_dHdvpa_err[iscan] = fkerr.dHdvpa_M.max ,fkerr.dHdvpa_M.L2 + max_dHdvperp_err[iscan], L2_dHdvperp_err[iscan] = fkerr.dHdvperp_M.max ,fkerr.dHdvperp_M.L2 + max_G_err[iscan], L2_G_err[iscan] = fkerr.G_M.max ,fkerr.G_M.L2 + max_dGdvperp_err[iscan], L2_dGdvperp_err[iscan] = fkerr.dGdvperp_M.max ,fkerr.dGdvperp_M.L2 + max_d2Gdvpa2_err[iscan], L2_d2Gdvpa2_err[iscan] = fkerr.d2Gdvpa2_M.max ,fkerr.d2Gdvpa2_M.L2 + max_d2Gdvperpdvpa_err[iscan], L2_d2Gdvperpdvpa_err[iscan] = fkerr.d2Gdvperpdvpa_M.max ,fkerr.d2Gdvperpdvpa_M.L2 + max_d2Gdvperp2_err[iscan], L2_d2Gdvperp2_err[iscan] = fkerr.d2Gdvperp2_M.max ,fkerr.d2Gdvperp2_M.L2 + n_err[iscan] = abs(fkerr.moments.delta_density) + u_err[iscan] = abs(fkerr.moments.delta_upar) + p_err[iscan] = abs(fkerr.moments.delta_pressure) + end + if global_rank[]==0 && plot_scan + fontsize = 8 + #ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + ytick_sequence = Array([1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1]) + xlabel = L"N_{element}" + Clabel = L"\epsilon_{\infty}(C)" + Hlabel = L"\epsilon_{\infty}(H)" + Glabel = L"\epsilon_{\infty}(G)" + dHdvpalabel = L"\epsilon_{\infty}(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon_{\infty}(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon_{\infty}(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon_{\infty}(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon_{\infty}(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon_{\infty}(dG/d v_{\perp})" + + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) + plot(nelement_list, [max_C_err,max_H_err,max_G_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel Hlabel Glabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + outfile = "fkpl_C_G_H_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) println(outfile) - println([max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral]) - else - plot(nelement_list, [max_C_err, L2_C_err, n_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel ClabelL2 nlabel expected_label expected_integral_label], ylabel="", + println([max_C_err,max_H_err,max_G_err, expected, expected_integral]) + + plot(nelement_list, [max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + outfile = "fkpl_coeffs_max_test_ngrid_"*string(ngrid)*"_GLL.pdf" savefig(outfile) - println(outfile) - println([max_C_err, L2_C_err, n_err, expected, expected_integral]) + println(outfile) + println([max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err, max_dGdvperp_err, expected, expected_integral]) + + + ClabelL2 = L"\epsilon_{L2}(C)" + HlabelL2 = L"\epsilon_{L2}(H)" + GlabelL2 = L"\epsilon_{L2}(G)" + dHdvpalabelL2 = L"\epsilon_{L2}(dH/d v_{\|\|})" + dHdvperplabelL2 = L"\epsilon_{L2}(dH/d v_{\perp})" + d2Gdvperp2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp}^2)" + d2Gdvpa2labelL2 = L"\epsilon_{L2}(d^2G/d v_{\|\|}^2)" + d2GdvperpdvpalabelL2 = L"\epsilon_{L2}(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabelL2 = L"\epsilon_{L2}(dG/d v_{\perp})" + + + plot(nelement_list, [L2_C_err,L2_H_err,L2_G_err, expected, expected_integral], + xlabel=xlabel, label=[ClabelL2 HlabelL2 GlabelL2 expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_C_G_H_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + println([L2_C_err,L2_H_err,L2_G_err, expected, expected_integral]) + + plot(nelement_list, [L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabelL2 dHdvperplabelL2 d2Gdvperp2labelL2 d2Gdvpa2labelL2 d2GdvperpdvpalabelL2 dGdvperplabelL2 expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_coeffs_L2_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + println([L2_dHdvpa_err, L2_dHdvperp_err, L2_d2Gdvperp2_err, L2_d2Gdvpa2_err, L2_d2Gdvperpdvpa_err, L2_dGdvperp_err, expected, expected_integral]) + + nlabel = L"|\Delta n|" + ulabel = L"|\Delta u_{\|\|}|" + plabel = L"|\Delta p|" + + if test_self_operator + plot(nelement_list, [max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel ClabelL2 nlabel ulabel plabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + println([max_C_err, L2_C_err, n_err, u_err, p_err, expected, expected_integral]) + else + plot(nelement_list, [max_C_err, L2_C_err, n_err, expected, expected_integral], + xlabel=xlabel, label=[Clabel ClabelL2 nlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + outfile = "fkpl_conservation_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + println([max_C_err, L2_C_err, n_err, expected, expected_integral]) + end + + calculate_timeslabel = "time/step (ms)" + init_timeslabel = "time/init (ms)" + ytick_sequence_timing = Array([10^2,10^3,10^4,10^5,10^6]) + plot(nelement_list, [calculate_times, init_times, expected_t_2, expected_t_3], + xlabel=xlabel, label=[calculate_timeslabel init_timeslabel expected_t_2_label expected_t_3_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:topleft) + outfile = "fkpl_timing_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + println([calculate_times, init_times, expected_t_2, expected_t_3]) end - - calculate_timeslabel = "time/step (ms)" - init_timeslabel = "time/init (ms)" - ytick_sequence_timing = Array([10^2,10^3,10^4,10^5,10^6]) - plot(nelement_list, [calculate_times, init_times, expected_t_2, expected_t_3], - xlabel=xlabel, label=[calculate_timeslabel init_timeslabel expected_t_2_label expected_t_3_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:topleft) - outfile = "fkpl_timing_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - println([calculate_times, init_times, expected_t_2, expected_t_3]) + finalize_comms!() + return nothing end - finalize_comms!() + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + run_assembly_test() end diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 4728e153f..b6a76c34b 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -1,3 +1,5 @@ +export gausslegendre_test + using FastGaussQuadrature using LegendrePolynomials: Pl using LinearAlgebra: mul!, lu, inv, cond @@ -13,9 +15,6 @@ using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_derivative! -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") function print_matrix(matrix,name,n,m) println("\n ",name," \n") @@ -37,372 +36,189 @@ if abspath(PROGRAM_FILE) == @__FILE__ println("\n") end - # gauss lobatto test - ngrid = 4 - nelement = 1 - x, w = gausslobatto(ngrid) - println("Gauss Lobatto Legendre") - println("x: ",x) - println("w: ",w) - Lx = 2.0 - xx = (Lx/2.0)*copy(x) - ww = (Lx/2.0)*copy(w) - - f_exact = Array{Float64,1}(undef,ngrid) - df_exact = Array{Float64,1}(undef,ngrid) - df_num = Array{Float64,1}(undef,ngrid) - df_err = Array{Float64,1}(undef,ngrid) - d2f_exact = Array{Float64,1}(undef,ngrid) - d2f_num = Array{Float64,1}(undef,ngrid) - d2f_err = Array{Float64,1}(undef,ngrid) - - for ix in 1:ngrid - #f_exact = exp(-x[ix]^2) - #df_exact = -2.0*x[ix]*exp(-x[ix]^2) - - #f_exact[ix] = -2.0*xx[ix]*exp(-xx[ix]^2) - #df_exact[ix] = (4.0*xx[ix]^2 - 2.0)*exp(-xx[ix]^2) - #d2f_exact[ix] = (12.0 - 8.0*xx[ix]^2)*xx[ix]*exp(-xx[ix]^2) - f_exact[ix] = (xx[ix]^2 - 1.0)^2 - df_exact[ix] = 4.0*xx[ix]*(xx[ix]^2 - 1.0) - d2f_exact[ix] = 12.0*xx[ix]^2 - 4.0 - end - F_exact = f_exact[end] - f_exact[1] - # do a test integration - F_num = sum(ww.*df_exact) - F_err = abs(F_num - F_exact) - #for ix in 1:ngrid - # F_num += w[ix]*df_exact[ix] - #end - println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) - - Dmat = Array{Float64,2}(undef,ngrid,ngrid) - Dmat2 = Array{Float64,2}(undef,ngrid,ngrid) - D2mat = Array{Float64,2}(undef,ngrid,ngrid) - Dmat_test = Array{Float64,2}(undef,ngrid,ngrid) - Dmat_err = Array{Float64,2}(undef,ngrid,ngrid) - gausslobattolegendre_differentiation_matrix!(Dmat,x,ngrid,Lx,1) - mul!(Dmat2,Dmat,Dmat) - #print_matrix(Dmat,"Dmat",ngrid,ngrid) - - Mmat = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_mass_matrix!(Mmat,ngrid,x,w,Lx,nelement) - print_matrix(Mmat,"Mmat",ngrid,ngrid) - - Mmat_1 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_mass_matrix_1!(Mmat_1,ngrid,x,w,Lx,nelement) - print_matrix(Mmat_1,"Mmat_1",ngrid,ngrid) - - IMmat = Array{Float64,2}(undef,ngrid,ngrid) - II_test = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_inverse_mass_matrix!(IMmat,ngrid,x,w,Lx) - print_matrix(IMmat,"IMmat",ngrid,ngrid) - - mul!(II_test,Mmat,IMmat) - print_matrix(II_test,"II_test",ngrid,ngrid) - print_matrix(inv(Mmat),"inv(Mmat)",ngrid,ngrid) - - Smat = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_S_matrix!(Smat,ngrid,Dmat,w,Lx) - print_matrix(Smat,"Smat",ngrid,ngrid) - Smat_1 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_S_matrix_1!(Smat_1,ngrid,x,w,Lx,nelement) - print_matrix(Smat_1,"Smat_1",ngrid,ngrid) - - M0 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_weak_product_matrix!(M0,ngrid,x,w,Lx,nelement,"M0") - print_matrix(M0,"M0",ngrid,ngrid) - M1 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_weak_product_matrix!(M1,ngrid,x,w,Lx,nelement,"M1") - print_matrix(M1,"M1",ngrid,ngrid) - S0 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_weak_product_matrix!(S0,ngrid,x,w,Lx,nelement,"S0") - print_matrix(S0,"S0",ngrid,ngrid) - S1 = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendre_weak_product_matrix!(S1,ngrid,x,w,Lx,nelement,"S1") - print_matrix(S1,"S1",ngrid,ngrid) - - - #mul!(Dmat_test,IMmat,Smat) - mul!(Dmat_test,inv(Mmat),Smat) - #@. Dmat_test = Mmat\Smat - @. Dmat_err = abs(Dmat_test - Dmat ) - println("max_Dmat_err: ",maximum(Dmat_err)) - #print_matrix(Dmat_test,"Dmat_test",ngrid,ngrid) - - Kmat = Array{Float64,2}(undef,ngrid,ngrid) - GaussLegendreLobatto_K_matrix!(Kmat,ngrid,Dmat,w,Lx,nelement) - #print_matrix(Kmat,"Kmat",ngrid,ngrid) - mul!(D2mat,IMmat,Kmat) - #print_matrix(D2mat,"D2mat",ngrid,ngrid) - - - mul!(df_num,Dmat,f_exact) - @. df_err = abs(df_num - df_exact) - mul!(d2f_num,D2mat,f_exact) - @. d2f_err = abs(d2f_num - d2f_exact) - println(df_num) - println(df_exact) - println(d2f_num) - println(d2f_exact) - max_df_err = maximum(df_err) - max_d2f_err = maximum(d2f_err) - println("max df_err (Dmat): ",max_df_err) - println("max d2f_err (weak D2mat): ",max_d2f_err) - # test by differentiating twice - mul!(d2f_num,Dmat2,f_exact) - @. d2f_err = abs(d2f_num - d2f_exact) - max_d2f_err = maximum(d2f_err) - - println("max d2f_err (Dmat*Dmat): ",max_d2f_err) - println(d2f_num) - println(d2f_exact) - - - # gauss radau test - ngrid = 3 - xradau, wradau = gaussradau(ngrid) - println("Gauss Radau Legendre") - println("xradau: ",xradau) - println("wradau: ",wradau) - x = -reverse(xradau) - w = reverse(wradau) - println("x: ",x) - println("w: ",w) - - f_exact = Array{Float64,1}(undef,ngrid) - df_exact = Array{Float64,1}(undef,ngrid) - df_num = Array{Float64,1}(undef,ngrid) - df_err = Array{Float64,1}(undef,ngrid) - - for ix in 1:ngrid - #f_exact = exp(-x[ix]^2) - #df_exact = -2.0*x[ix]*exp(-x[ix]^2) - - f_exact[ix] = -2.0*x[ix]*exp(-x[ix]^2) - df_exact[ix] = (4.0*x[ix]^2 - 2.0)*exp(-x[ix]^2) - end - F_exact = f_exact[end] - f_exact[1] - # do a test integration - F_num = sum(w.*df_exact) - F_err = abs(F_num - F_exact) - #for ix in 1:ngrid - # F_num += w[ix]*df_exact[ix] - #end - println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) - - Dmat = Array{Float64,2}(undef,ngrid,ngrid) - gaussradaulegendre_differentiation_matrix!(Dmat,xradau,ngrid,2.0,1) - #print_matrix(Dmat,"Dmat",ngrid,ngrid) - - mul!(df_num,Dmat,f_exact) - @. df_err = abs(df_num - df_exact) - #println(df_num) - #println(df_exact) - max_df_err = maximum(df_err) - println("max df_err: ",max_df_err) - - # elemental grid tests - ngrid = 17 - nelement = 2 - y_ngrid = ngrid #number of points per element - y_nelement_local = nelement # number of elements per rank - y_nelement_global = y_nelement_local # total number of elements - y_L = 6.0 #physical box size in reference units - bc = "zero" - discretization = "gausslegendre_pseudospectral" - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0#1 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - #y_name = "y" - y_name = "vperp" - y_input = grid_input(y_name, y_ngrid, y_nelement_global, y_nelement_local, - nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - - # create the coordinate structs - y = define_coordinate(y_input) - y_spectral = setup_gausslegendre_pseudospectral(y) - Mmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) - x, w = gausslobatto(y.ngrid) - #print_vector(y.grid,"y.grid",y.n) - #print_vector(y.wgts,"y.wgts",y.n) - GaussLegendreLobatto_mass_matrix!(Mmat,y.ngrid,x,w,y.L,y.nelement_global) - #print_matrix(Mmat,"Mmat",y.ngrid,y.ngrid) - print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) - print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) - #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) - print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) - #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) - print_matrix(y_spectral.radau.K0,"local radau K matrix K0",y.ngrid,y.ngrid) - print_matrix(y_spectral.radau.K1,"local radau K matrix K1",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.K0,"local K matrix K0",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.K1,"local K matrix K1",y.ngrid,y.ngrid) - print_matrix(y_spectral.radau.P0,"local radau P matrix P0",y.ngrid,y.ngrid) - print_matrix(y_spectral.lobatto.P0,"local P matrix P0",y.ngrid,y.ngrid) - #print_matrix(y_spectral.K_matrix,"global K matrix",y.n,y.n) - #print_matrix(y_spectral.L_matrix,"global L matrix",y.n,y.n) - #@views y_spectral.K_matrix[1,:] *= (4.0/3.0) - #print_matrix(y_spectral.K_matrix,"global K matrix (hacked) ",y.n,y.n) - print_matrix(y_spectral.radau.Dmat,"local radau D matrix Dmat",y.ngrid,y.ngrid) - print_vector(y_spectral.radau.D0,"local radau D matrix D0",y.ngrid) - print_matrix(y_spectral.lobatto.Dmat,"local lobatto D matrix Dmat",y.ngrid,y.ngrid) - print_vector(y_spectral.lobatto.D0,"local lobatto D matrix D0",y.ngrid) - - Dmat = Array{Float64,2}(undef,y.ngrid,y.ngrid) - Dmat_test = Array{Float64,2}(undef,y.ngrid,y.ngrid) - Dmat_err = Array{Float64,2}(undef,y.ngrid,y.ngrid) - lu_M0 = lu(y_spectral.lobatto.M0) - mul!(Dmat_test,inv(y_spectral.lobatto.M0), y_spectral.lobatto.S0) - #Dmat_test = y_spectral.lobatto.M0 \ y_spectral.lobatto.S0 - #print_matrix(lu_M0 \ y_spectral.lobatto.S0, "local D matrix",y.ngrid,y.ngrid) - #print_matrix(Dmat_test, "local D matrix",y.ngrid,y.ngrid) - mul!(Dmat_err,y_spectral.lobatto.M0,Dmat_test) - #print_matrix(Dmat_err, "local S matrix?",y.ngrid,y.ngrid) - #print_matrix(y_spectral.lobatto.Dmat, "local D matrix (Dmat)",y.ngrid,y.ngrid) - - @. Dmat = y_spectral.lobatto.Dmat - - @. Dmat_err = Dmat_test - Dmat - #println("max_Dmat_err: ",maximum(Dmat_err)) - - #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) - #print_matrix(y_spectral.lobatto.Mmat,"local lobatto mass matrix",y.ngrid,y.ngrid) - #print_matrix(y_spectral.radau.Mmat,"local radau mass matrix",y.ngrid,y.ngrid) - #println("y.grid: ",y.grid) - #println("y.wgts: ",y.wgts) - x, w = gausslobatto(y.ngrid) - println("Gauss Lobatto Legendre") - #println("x: ",x) - #println("w: ",w) - - f_exact = Array{Float64,1}(undef,y.n) - df_exact = Array{Float64,1}(undef,y.n) - df_num = Array{Float64,1}(undef,y.n) - df_err = Array{Float64,1}(undef,y.n) - g_exact = Array{Float64,1}(undef,y.n) - h_exact = Array{Float64,1}(undef,y.n) - divg_exact = Array{Float64,1}(undef,y.n) - divg_num = Array{Float64,1}(undef,y.n) - divg_err = Array{Float64,1}(undef,y.n) - laph_exact = Array{Float64,1}(undef,y.n) - laph_num = Array{Float64,1}(undef,y.n) - laph_err = Array{Float64,1}(undef,y.n) - d2f_exact = Array{Float64,1}(undef,y.n) - d2f_num = Array{Float64,1}(undef,y.n) - d2f_err = Array{Float64,1}(undef,y.n) - b = Array{Float64,1}(undef,y.n) - for iy in 1:y.n - f_exact[iy] = exp(-y.grid[iy]^2) - df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) - d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) - g_exact[iy] = y.grid[iy]*exp(-y.grid[iy]^2) - divg_exact[iy] = 2.0*(1.0-y.grid[iy]^2)*exp(-y.grid[iy]^2) - h_exact[iy] = exp(-y.grid[iy]^2) - laph_exact[iy] = 4.0*(y.grid[iy]^2 - 1.0)*exp(-y.grid[iy]^2) - #h_exact[iy] = exp(-2.0*y.grid[iy]^2) - #laph_exact[iy] = 8.0*(2.0*y.grid[iy]^2 - 1.0)*exp(-2.0*y.grid[iy]^2) - #h_exact[iy] = exp(-y.grid[iy]^3) - #laph_exact[iy] = 9.0*y.grid[iy]*(y.grid[iy]^3 - 1.0)*exp(-y.grid[iy]^3) - #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) - - end - if y.name == "y" - F_exact = sqrt(pi) - elseif y.name == "vperp" - F_exact = 1.0 - end - # do a test integration - #println(f_exact) - F_num = sum(y.wgts.*f_exact) - F_err = abs(F_num - F_exact) - #for ix in 1:ngrid - # F_num += w[ix]*df_exact[ix] - #end - println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) - - derivative!(df_num, f_exact, y, y_spectral) - @. df_err = df_num - df_exact - println("max(df_err) (interpolation): ",maximum(df_err)) - derivative!(d2f_num, df_num, y, y_spectral) - @. d2f_err = d2f_num - d2f_exact - println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) - if y.name == "y" - mul!(b,y_spectral.S_matrix,f_exact) - gausslegendre_mass_matrix_solve!(df_num,b,y_spectral) - @. df_err = df_num - df_exact - #println("df_num (weak form): ",df_num) - #println("df_exact (weak form): ",df_exact) - println("max(df_err) (weak form): ",maximum(df_err)) - second_derivative!(d2f_num, f_exact, y, y_spectral) - #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) - @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* - #println(d2f_num) - #println(d2f_exact) - println("max(d2f_err) (weak form): ",maximum(d2f_err)) - plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") - outfile = "vpa_test.pdf" - savefig(outfile) - - elseif y.name == "vperp" - #println("condition: ",cond(y_spectral.mass_matrix)) - b = Array{Float64,1}(undef,y.n) - mul!(b,y_spectral.S_matrix,g_exact) - gausslegendre_mass_matrix_solve!(divg_num,b,y_spectral) - @. divg_err = abs(divg_num - divg_exact) - #println("divg_b (weak form): ",b) - #println("divg_num (weak form): ",divg_num) - #println("divg_exact (weak form): ",divg_exact) - println("max(divg_err) (weak form): ",maximum(divg_err)) - - second_derivative!(d2f_num, f_exact, y, y_spectral) - #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) - @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* - println(d2f_num[1:10]) - println(d2f_exact[1:10]) - println(d2f_err[1:10]) - println("max(d2f_err) (weak form): ",maximum(d2f_err)) - plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") - outfile = "vperp_second_derivative_test.pdf" - savefig(outfile) - - #mul!(b,y_spectral.L_matrix,h_exact) - laplacian_derivative!(laph_num, h_exact, y, y_spectral) - #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) - @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* - #println(b[1:10]) - println(laph_num[1:10]) - println(laph_exact[1:10]) - println(laph_err[1:10]) - println("max(laph_err) (weak form): ",maximum(laph_err)) - plot([y.grid, y.grid], [laph_num, laph_exact], xlabel="vperp", label=["num" "exact"], ylabel="") - outfile = "vperp_laplacian_test.pdf" - savefig(outfile) - - @. y.scratch = y.grid*g_exact - derivative!(y.scratch2, y.scratch, y, y_spectral) - @. divg_num = y.scratch2/y.grid - @. divg_err = abs(divg_num - divg_exact) - println("max(divg_err) (interpolation): ",maximum(divg_err)) - - derivative!(y.scratch, h_exact, y, y_spectral) - @. y.scratch2 = y.grid*y.scratch - derivative!(y.scratch, y.scratch2, y, y_spectral) - @. laph_num = y.scratch/y.grid - @. laph_err = abs(laph_num - laph_exact) - println("max(laph_err) (interpolation): ",maximum(laph_err)) + function gausslegendre_test() + # elemental grid tests + ngrid = 17 + nelement = 4 + y_ngrid = ngrid #number of points per element + y_nelement_local = nelement # number of elements per rank + y_nelement_global = y_nelement_local # total number of elements + bc = "zero" + discretization = "gausslegendre_pseudospectral" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0#1 + comm = MPI.COMM_NULL + element_spacing_option = "uniform" + # create the 'input' struct containing input info needed to create a + # coordinate + for y_name in ["vpa","vperp"] + println("") + println("$y_name test") + println("") + if y_name == "vperp" + y_L = 6.0 #physical box size in reference units + else + y_L = 12.0 + end + y_input = grid_input(y_name, y_ngrid, y_nelement_global, y_nelement_local, + nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + + # create the coordinate structs + y, y_spectral = define_coordinate(y_input) + #print_matrix(Mmat,"Mmat",y.n,y.n) + #print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.M0,"local mass matrix M0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.M1,"local mass matrix M1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.mass_matrix,"global mass matrix",y.n,y.n) + #print_matrix(y_spectral.lobatto.S0,"local S0 matrix",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.S1,"local S1 matrix",y.ngrid,y.ngrid) + #print_matrix(y_spectral.S_matrix,"global S matrix",y.n,y.n) + #print_matrix(y_spectral.radau.K0,"local radau K matrix K0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.K1,"local radau K matrix K1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.K0,"local K matrix K0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.K1,"local K matrix K1",y.ngrid,y.ngrid) + #print_matrix(y_spectral.radau.P0,"local radau P matrix P0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.lobatto.P0,"local P matrix P0",y.ngrid,y.ngrid) + #print_matrix(y_spectral.K_matrix,"global K matrix",y.n,y.n) + #print_matrix(y_spectral.L_matrix,"global L matrix",y.n,y.n) + #@views y_spectral.K_matrix[1,:] *= (4.0/3.0) + #print_matrix(y_spectral.K_matrix,"global K matrix (hacked) ",y.n,y.n) + #print_matrix(y_spectral.radau.Dmat,"local radau D matrix Dmat",y.ngrid,y.ngrid) + #print_vector(y_spectral.radau.D0,"local radau D matrix D0",y.ngrid) + #print_matrix(y_spectral.lobatto.Dmat,"local lobatto D matrix Dmat",y.ngrid,y.ngrid) + #print_vector(y_spectral.lobatto.D0,"local lobatto D matrix D0",y.ngrid) + + f_exact = Array{Float64,1}(undef,y.n) + df_exact = Array{Float64,1}(undef,y.n) + df_num = Array{Float64,1}(undef,y.n) + df_err = Array{Float64,1}(undef,y.n) + g_exact = Array{Float64,1}(undef,y.n) + h_exact = Array{Float64,1}(undef,y.n) + divg_exact = Array{Float64,1}(undef,y.n) + divg_num = Array{Float64,1}(undef,y.n) + divg_err = Array{Float64,1}(undef,y.n) + laph_exact = Array{Float64,1}(undef,y.n) + laph_num = Array{Float64,1}(undef,y.n) + laph_err = Array{Float64,1}(undef,y.n) + d2f_exact = Array{Float64,1}(undef,y.n) + d2f_num = Array{Float64,1}(undef,y.n) + d2f_err = Array{Float64,1}(undef,y.n) + b = Array{Float64,1}(undef,y.n) + for iy in 1:y.n + f_exact[iy] = exp(-y.grid[iy]^2) + df_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + d2f_exact[iy] = (4.0*y.grid[iy]^2 - 2.0)*exp(-y.grid[iy]^2) + g_exact[iy] = y.grid[iy]*exp(-y.grid[iy]^2) + divg_exact[iy] = 2.0*(1.0-y.grid[iy]^2)*exp(-y.grid[iy]^2) + h_exact[iy] = exp(-y.grid[iy]^2) + laph_exact[iy] = 4.0*(y.grid[iy]^2 - 1.0)*exp(-y.grid[iy]^2) + #h_exact[iy] = exp(-2.0*y.grid[iy]^2) + #laph_exact[iy] = 8.0*(2.0*y.grid[iy]^2 - 1.0)*exp(-2.0*y.grid[iy]^2) + #h_exact[iy] = exp(-y.grid[iy]^3) + #laph_exact[iy] = 9.0*y.grid[iy]*(y.grid[iy]^3 - 1.0)*exp(-y.grid[iy]^3) + #f_exact[iy] = -2.0*y.grid[iy]*exp(-y.grid[iy]^2) + + end + if y.name == "vpa" + F_exact = sqrt(pi) + elseif y.name == "vperp" + F_exact = 1.0 + end + # do a test integration + #println(f_exact) + F_num = sum(y.wgts.*f_exact) + F_err = abs(F_num - F_exact) + #for ix in 1:ngrid + # F_num += w[ix]*df_exact[ix] + #end + println("F_err: ", F_err, " F_exact: ",F_exact, " F_num: ", F_num) + + derivative!(df_num, f_exact, y, y_spectral) + @. df_err = df_num - df_exact + println("max(df_err) (interpolation): ",maximum(df_err)) + derivative!(d2f_num, df_num, y, y_spectral) + @. d2f_err = d2f_num - d2f_exact + println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) + if y.name == "vpa" + mul!(b,y_spectral.S_matrix,f_exact) + gausslegendre_mass_matrix_solve!(df_num,b,y.name,y_spectral) + @. df_err = df_num - df_exact + #println("df_num (weak form): ",df_num) + #println("df_exact (weak form): ",df_exact) + println("max(df_err) (weak form): ",maximum(df_err)) + second_derivative!(d2f_num, f_exact, y, y_spectral) + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* + #println(d2f_num) + #println(d2f_exact) + println("max(d2f_err) (weak form): ",maximum(d2f_err)) + plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") + outfile = "vpa_test.pdf" + savefig(outfile) + + elseif y.name == "vperp" + #println("condition: ",cond(y_spectral.mass_matrix)) + b = Array{Float64,1}(undef,y.n) + mul!(b,y_spectral.S_matrix,g_exact) + gausslegendre_mass_matrix_solve!(divg_num,b,y.name,y_spectral) + @. divg_err = abs(divg_num - divg_exact) + #println("divg_b (weak form): ",b) + #println("divg_num (weak form): ",divg_num) + #println("divg_exact (weak form): ",divg_exact) + println("max(divg_err) (weak form): ",maximum(divg_err)) + + second_derivative!(d2f_num, f_exact, y, y_spectral) + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* + #println(d2f_num) + #println(d2f_exact) + #println(d2f_err[1:10]) + println("max(d2f_err) (weak form): ",maximum(d2f_err)) + plot([y.grid, y.grid], [d2f_num, d2f_exact], xlabel="vpa", label=["num" "exact"], ylabel="") + outfile = "vperp_second_derivative_test.pdf" + savefig(outfile) + + #mul!(b,y_spectral.L_matrix,h_exact) + laplacian_derivative!(laph_num, h_exact, y, y_spectral) + #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) + @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* + #println(b[1:10]) + #println(laph_num) + #println(laph_exact) + #println(laph_err[1:10]) + println("max(laph_err) (weak form): ",maximum(laph_err)) + plot([y.grid, y.grid], [laph_num, laph_exact], xlabel="vperp", label=["num" "exact"], ylabel="") + outfile = "vperp_laplacian_test.pdf" + savefig(outfile) + + @. y.scratch = y.grid*g_exact + derivative!(y.scratch2, y.scratch, y, y_spectral) + @. divg_num = y.scratch2/y.grid + @. divg_err = abs(divg_num - divg_exact) + println("max(divg_err) (interpolation): ",maximum(divg_err)) + + derivative!(y.scratch, h_exact, y, y_spectral) + @. y.scratch2 = y.grid*y.scratch + derivative!(y.scratch, y.scratch2, y, y_spectral) + @. laph_num = y.scratch/y.grid + @. laph_err = abs(laph_num - laph_exact) + println("max(laph_err) (interpolation): ",maximum(laph_err)) + + end + end end - - + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + gausslegendre_test() end diff --git a/src/calculus.jl b/src/calculus.jl index 31ce17442..1aae008c6 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -44,7 +44,7 @@ function second_derivative!(d2f, f, coord, spectral::gausslegendre_info) # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,coord.name,spectral) end function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) @@ -54,7 +54,7 @@ function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,coord.name,spectral) end """ Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' diff --git a/src/chebyshev.jl b/src/chebyshev.jl index 516dd92ff..ca51548b4 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -224,6 +224,9 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) else #differentiate using the Lobatto scheme @views mul!(df[:,j],chebyshev.lobatto.Dmat[:,:],ff[imin:imax]) end + for i ∈ 1:coord.ngrid + df[i,j] /= coord.element_scale[j] + end # calculate the Chebyshev derivative on each element @inbounds for j ∈ 2:nelement # imin is the minimum index on the full grid for this (jth) element @@ -237,6 +240,9 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] @views mul!(df[:,j],chebyshev.lobatto.Dmat[:,:],ff[imin:imax]) + for i ∈ 1:coord.ngrid + df[i,j] /= coord.element_scale[j] + end end elseif coord.cheb_option == "FFT" # note that one must multiply by 1/element_scale[j] get derivative @@ -844,9 +850,6 @@ https://people.maths.ox.ac.uk/trefethen/pdetext.html for j in 1:n D[j,j] = -sum(D[j,:]) end - - #multiply by scale factor for element length - D .= (2.0*float(nelement)/L).*D end function Djk(x::Array{Float64,1},j::Int64,k::Int64,c_j::Float64,c_k::Float64) return (c_j/c_k)*((-1)^(k+j))/(x[j] - x[k]) @@ -871,9 +874,6 @@ https://people.maths.ox.ac.uk/trefethen/pdetext.html D[j,j] = 0.0 D[j,j] = -sum(D[j,:]) end - - #multiply by scale factor for element length - D .= (2.0*float(coord.nelement_global)/coord.L).*D end end diff --git a/src/coordinates.jl b/src/coordinates.jl index 2fd1fa9c2..daa99f721 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -119,7 +119,7 @@ function define_coordinate(input, parallel_io::Bool=false) imin, imax, igrid_full = elemental_to_full_grid_map(input.ngrid, input.nelement_local) # initialise the data used to construct the grid # boundaries for each element - element_boundaries = set_element_boundaries(input.nelement_global, input.L, input.element_spacing_option) + element_boundaries = set_element_boundaries(input.nelement_global, input.L, input.element_spacing_option, input.name) # shift and scale factors for each local element element_scale, element_shift = set_element_scale_and_shift(input.nelement_global, input.nelement_local, input.irank, element_boundaries) # initialize the grid and the integration weights associated with the grid @@ -188,8 +188,8 @@ function define_coordinate(input, parallel_io::Bool=false) return coord, spectral end -function set_element_boundaries(nelement_global, L, element_spacing_option) - # set global element boundaries +function set_element_boundaries(nelement_global, L, element_spacing_option, coord_name) + # set global element boundaries between [-L/2,L/2] element_boundaries = allocate_float(nelement_global+1) if element_spacing_option == "sqrt" && nelement_global > 3 # number of boundaries of sqrt grid @@ -218,6 +218,12 @@ function set_element_boundaries(nelement_global, L, element_spacing_option) else println("ERROR: element_spacing_option: ",element_spacing_option, " not supported") end + if coord_name == "vperp" + #shift so that the range of element boundaries is [0,L] + for j in 1:nelement_global+1 + element_boundaries[j] += L/2.0 + end + end return element_boundaries end @@ -255,7 +261,6 @@ function init_grid(ngrid, nelement_local, n_global, n_local, irank, L, element_s if name == "vperp" # initialize chebyshev grid defined on [-L/2,L/2] grid, wgts = scaled_chebyshev_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax) - grid .= grid .+ L/2.0 # shift to [0,L] appropriate to vperp variable wgts = 2.0 .* wgts .* grid # to include 2 vperp in jacobian of integral # see note above on normalisation else @@ -269,12 +274,12 @@ function init_grid(ngrid, nelement_local, n_global, n_local, irank, L, element_s end elseif discretization == "gausslegendre_pseudospectral" if name == "vperp" - grid, wgts = scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) - grid .= grid .+ L/2.0 # shift to [0,L] appropriate to vperp variable + # use a radau grid for the 1st element near the origin + grid, wgts = scaled_gauss_legendre_radau_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax, irank) wgts = 2.0 .* wgts .* grid # to include 2 vperp in jacobian of integral # see note above on normalisation else - grid, wgts = scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_local, n_local, irank, L, imin, imax) + grid, wgts = scaled_gauss_legendre_lobatto_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax) end elseif discretization == "finite_difference" if name == "vperp" diff --git a/src/file_io.jl b/src/file_io.jl index 24ac629ab..037e0df1e 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -521,6 +521,10 @@ function define_io_coordinate!(parent, coord, coord_name, description, parallel_ write_single_value!(group, "fd_option", coord.fd_option; parallel_io=parallel_io, description="type of finite difference for $coord_name, if used") + # write the finite-difference option for the coordinate + write_single_value!(group, "cheb_option", coord.fd_option; parallel_io=parallel_io, + description="type of finite difference for $coord_name, if used") + # write the boundary condition for the coordinate write_single_value!(group, "bc", coord.bc; parallel_io=parallel_io, description="boundary condition for $coord_name") @@ -800,7 +804,8 @@ function reopen_moments_io(file_info) getvar("Ez"), getvar("density"), getvar("parallel_flow"), getvar("parallel_pressure"), getvar("perpendicular_pressure"), getvar("parallel_heat_flux"), - getvar("thermal_speed"), getvar("density_neutral"), + getvar("thermal_speed"), getvar("entropy_production"), + getvar("density_neutral"), getvar("uz_neutral"), getvar("pz_neutral"), getvar("qz_neutral"), getvar("thermal_speed_neutral"), parallel_io) @@ -885,8 +890,8 @@ function reopen_dfns_io(file_info) getvar("Ez"), getvar("density"), getvar("parallel_flow"), getvar("parallel_pressure"), getvar("perpendicular_pressure"), - getvar("parallel_heat_flux"), - getvar("thermal_speed"), getvar("density_neutral"), + getvar("parallel_heat_flux"), getvar("thermal_speed"), + getvar("entropy_production"), getvar("density_neutral"), getvar("uz_neutral"), getvar("pz_neutral"), getvar("qz_neutral"), getvar("thermal_speed_neutral"), parallel_io) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index e68baaab1..d8f3beea7 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -37,10 +37,6 @@ the derivatives on Gauss-Legendre points in 1D struct gausslegendre_base_info{} # elementwise differentiation matrix (ngrid*ngrid) Dmat::Array{mk_float,2} - # local mass matrix - Mmat::Array{mk_float,2} - # local K matrix (for second derivatives) - Kmat::Array{mk_float,2} # local mass matrix type 0 M0::Array{mk_float,2} # local mass matrix type 1 @@ -108,7 +104,6 @@ function setup_gausslegendre_pseudospectral(coord) S_matrix = allocate_float(coord.n,coord.n) K_matrix = allocate_float(coord.n,coord.n) L_matrix = allocate_float(coord.n,coord.n) - setup_global_mass_matrix!(mass_matrix, lobatto, radau, coord) setup_global_weak_form_matrix!(mass_matrix, lobatto, radau, coord, "M") setup_global_weak_form_matrix!(S_matrix, lobatto, radau, coord, "S") @@ -122,55 +117,51 @@ end function setup_gausslegendre_pseudospectral_lobatto(coord) x, w = gausslobatto(coord.ngrid) Dmat = allocate_float(coord.ngrid, coord.ngrid) - gausslobattolegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) - Mmat = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) - Kmat = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendreLobatto_K_matrix!(Kmat,coord.ngrid,Dmat,w,coord.L,coord.nelement_global) + gausslobattolegendre_differentiation_matrix!(Dmat,x,coord.ngrid) M0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M0,coord.ngrid,x,w,coord.L,coord.nelement_global,"M0") + GaussLegendre_weak_product_matrix!(M0,coord.ngrid,x,w,"M0") M1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M1,coord.ngrid,x,w,coord.L,coord.nelement_global,"M1") + GaussLegendre_weak_product_matrix!(M1,coord.ngrid,x,w,"M1") M2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M2,coord.ngrid,x,w,coord.L,coord.nelement_global,"M2") + GaussLegendre_weak_product_matrix!(M2,coord.ngrid,x,w,"M2") S0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(S0,coord.ngrid,x,w,coord.L,coord.nelement_global,"S0") + GaussLegendre_weak_product_matrix!(S0,coord.ngrid,x,w,"S0") S1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(S1,coord.ngrid,x,w,coord.L,coord.nelement_global,"S1") + GaussLegendre_weak_product_matrix!(S1,coord.ngrid,x,w,"S1") K0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K0,coord.ngrid,x,w,coord.L,coord.nelement_global,"K0") + GaussLegendre_weak_product_matrix!(K0,coord.ngrid,x,w,"K0") K1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K1,coord.ngrid,x,w,coord.L,coord.nelement_global,"K1") + GaussLegendre_weak_product_matrix!(K1,coord.ngrid,x,w,"K1") K2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K2,coord.ngrid,x,w,coord.L,coord.nelement_global,"K2") + GaussLegendre_weak_product_matrix!(K2,coord.ngrid,x,w,"K2") P0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,coord.L,coord.nelement_global,"P0") + GaussLegendre_weak_product_matrix!(P0,coord.ngrid,x,w,"P0") P1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P1,coord.ngrid,x,w,coord.L,coord.nelement_global,"P1") + GaussLegendre_weak_product_matrix!(P1,coord.ngrid,x,w,"P1") P2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P2,coord.ngrid,x,w,coord.L,coord.nelement_global,"P2") + GaussLegendre_weak_product_matrix!(P2,coord.ngrid,x,w,"P2") D0 = allocate_float(coord.ngrid) #@. D0 = Dmat[1,:] # values at lower extreme of element - GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w,coord.L,coord.nelement_global) + GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w) Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y00") + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,x,w,"Y00") Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y01") + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,x,w,"Y01") Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y10") + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,x,w,"Y10") Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y11") + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,x,w,"Y11") Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y20") + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,x,w,"Y20") Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y21") + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,x,w,"Y21") Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y30") + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,x,w,"Y30") Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,x,w,coord.L,coord.nelement_global,"Y31") + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,x,w,"Y31") - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1, + return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end @@ -181,53 +172,49 @@ function setup_gausslegendre_pseudospectral_radau(coord) xreverse, wreverse = -reverse(x), reverse(w) # elemental differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) - gaussradaulegendre_differentiation_matrix!(Dmat,x,coord.ngrid,coord.L,coord.nelement_global) - # elemental mass matrix - Mmat = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendreLobatto_mass_matrix!(Mmat,coord.ngrid,x,w,coord.L,coord.nelement_global) - Kmat = allocate_float(coord.ngrid, coord.ngrid) + gaussradaulegendre_differentiation_matrix!(Dmat,x,coord.ngrid) M0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M0",radau=true) + GaussLegendre_weak_product_matrix!(M0,coord.ngrid,xreverse,wreverse,"M0",radau=true) M1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M1",radau=true) + GaussLegendre_weak_product_matrix!(M1,coord.ngrid,xreverse,wreverse,"M1",radau=true) M2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(M2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"M2",radau=true) + GaussLegendre_weak_product_matrix!(M2,coord.ngrid,xreverse,wreverse,"M2",radau=true) S0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(S0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S0",radau=true) + GaussLegendre_weak_product_matrix!(S0,coord.ngrid,xreverse,wreverse,"S0",radau=true) S1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(S1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"S1",radau=true) + GaussLegendre_weak_product_matrix!(S1,coord.ngrid,xreverse,wreverse,"S1",radau=true) K0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K0",radau=true) + GaussLegendre_weak_product_matrix!(K0,coord.ngrid,xreverse,wreverse,"K0",radau=true) K1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K1",radau=true) + GaussLegendre_weak_product_matrix!(K1,coord.ngrid,xreverse,wreverse,"K1",radau=true) K2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(K2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"K2",radau=true) + GaussLegendre_weak_product_matrix!(K2,coord.ngrid,xreverse,wreverse,"K2",radau=true) P0 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P0",radau=true) + GaussLegendre_weak_product_matrix!(P0,coord.ngrid,xreverse,wreverse,"P0",radau=true) P1 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P1,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P1",radau=true) + GaussLegendre_weak_product_matrix!(P1,coord.ngrid,xreverse,wreverse,"P1",radau=true) P2 = allocate_float(coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(P2,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"P2",radau=true) + GaussLegendre_weak_product_matrix!(P2,coord.ngrid,xreverse,wreverse,"P2",radau=true) D0 = allocate_float(coord.ngrid) - GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,radau=true) + GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,radau=true) Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y00",radau=true) + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,xreverse,wreverse,"Y00",radau=true) Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y01",radau=true) + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,xreverse,wreverse,"Y01",radau=true) Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y10",radau=true) + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,xreverse,wreverse,"Y10",radau=true) Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y11",radau=true) + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,xreverse,wreverse,"Y11",radau=true) Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y20",radau=true) + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,xreverse,wreverse,"Y20",radau=true) Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y21",radau=true) + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,xreverse,wreverse,"Y21",radau=true) Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y30",radau=true) + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,xreverse,wreverse,"Y30",radau=true) Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,xreverse,wreverse,coord.L,coord.nelement_global,"Y31",radau=true) - return gausslegendre_base_info(Dmat,Mmat,Kmat,M0,M1,M2,S0,S1, + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,xreverse,wreverse,"Y31",radau=true) + return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end """ @@ -250,13 +237,21 @@ function gausslegendre_derivative!(df, ff, gausslegendre, coord) else #differentiate using the Lobatto scheme @views mul!(df[:,j],gausslegendre.lobatto.Dmat[:,:],ff[imin:imax]) end + # transform back to the physical coordinate scale + for i in 1:coord.ngrid + df[i,j] /= coord.element_scale[j] + end # calculate the derivative on each element @inbounds for j ∈ 2:nelement k = 1 imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] - @views mul!(df[:,j],gausslegendre.lobatto.Dmat[:,:],ff[imin:imax]) + @views mul!(df[:,j],gausslegendre.lobatto.Dmat[:,:],ff[imin:imax]) + # transform back to the physical coordinate scale + for i in 1:coord.ngrid + df[i,j] /= coord.element_scale[j] + end end return nothing @@ -322,24 +317,11 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) zero_gradient_bc_lower_boundary = false#true - boundary_flux_terms = true#false if coord.name == "vperp" && zero_gradient_bc_lower_boundary # set the 1st point of the RHS vector to zero # consistent with use with the mass matrix with D f = 0 boundary conditions df[1,j] = 0.0 end - # boundary flux terms - if boundary_flux_terms - if coord.name=="vperp" # only include a flux term from the upper boundary - @. coord.scratch[imin:imax] = gausslegendre.radau.Dmat[coord.ngrid,:]*ff[imin:imax] - df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) - else - @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[1,:]*ff[imin:imax] - df[1,j] -= coord.jacobian[imin]*sum(coord.scratch[imin:imax]) - @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[coord.ngrid,:]*ff[imin:imax] - df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) - end - end # calculate the derivative on each element @inbounds for j ∈ 2:nelement k = 1 @@ -350,13 +332,6 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) - # boundary flux terms - if boundary_flux_terms - @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[1,:]*ff[imin:imax] - df[1,j] -= coord.jacobian[imin]*sum(coord.scratch[imin:imax]) - @. coord.scratch[imin:imax] = gausslegendre.lobatto.Dmat[coord.ngrid,:]*ff[imin:imax] - df[coord.ngrid,j] += coord.jacobian[imax]*sum(coord.scratch[imin:imax]) - end end #for j in 1:nelement # println(df[:,j]) @@ -364,7 +339,17 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) return nothing end -function gausslegendre_mass_matrix_solve!(f,b,spectral) +function gausslegendre_mass_matrix_solve!(f,b,coord_name,spectral) + if coord_name == "vperp" + # enforce zero (value or gradient) boundary conditions + #b[1] = 0.0 # uncomment if bc is imposed at vperp = 0 in mass matrix + b[end] = 0.0 + else + # enforce zero (value or gradient) boundary conditions + b[1] = 0.0 + b[end] = 0.0 + end + # invert mass matrix system y = spectral.mass_matrix_lu \ b @. f = y return nothing @@ -378,10 +363,10 @@ Or https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundame D -- differentiation matrix x -- Gauss-Legendre-Lobatto points in [-1,1] ngrid -- number of points per element (incl. boundary points) -L -- total length of full domain -nelement -- total number of elements + +Note that D has does not include a scaling factor """ -function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) +function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64) D[:,:] .= 0.0 for ix in 1:ngrid for ixp in 1:ngrid @@ -399,9 +384,7 @@ function gausslobattolegendre_differentiation_matrix!(D::Array{Float64,2},x::Arr # get diagonal values from sum of nonzero off diagonal values for ix in 1:ngrid D[ix,ix] = -sum(D[ix,:]) - end - #multiply by scale factor for element length - D .= (2.0*float(nelement)/L).*D + end return nothing end """ @@ -411,10 +394,10 @@ https://doc.nektar.info/tutorials/latest/fundamentals/differentiation/fundamenta D -- differentiation matrix x -- Gauss-Legendre-Radau points in [-1,1) ngrid -- number of points per element (incl. boundary points) -L -- total length of full domain -nelement -- total number of elements + +Note that D has does not include a scaling factor """ -function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64,L::Float64,nelement::Int64) +function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array{Float64,1},ngrid::Int64) D[:,:] .= 0.0 for ix in 1:ngrid for ixp in 1:ngrid @@ -432,8 +415,6 @@ function gaussradaulegendre_differentiation_matrix!(D::Array{Float64,2},x::Array for ix in 1:ngrid D[ix,ix] = -sum(D[ix,:]) end - #multiply by scale factor for element length - D .= (2.0*float(nelement)/L).*D # get into correct order for a grid on (-1,1] Dreverse = copy(D) @@ -452,9 +433,9 @@ D0 -- the vector xj -- the x location where the derivative is evaluated ngrid -- number of points in x x -- the grid from -1, 1 -L -- size of physical domain +Note that D0 is not scaled to the physical grid """ -function GaussLegendre_derivative_vector!(D0,xj,ngrid,x,wgts,L,nelement_global;radau=false) +function GaussLegendre_derivative_vector!(D0,xj,ngrid,x,wgts;radau=false) # coefficient in expansion of # lagrange polys in terms of Legendre polys gamma = allocate_float(ngrid) @@ -476,7 +457,7 @@ function GaussLegendre_derivative_vector!(D0,xj,ngrid,x,wgts,L,nelement_global;r # set `diagonal' value D0[1] = 0.0 D0[1] = -sum(D0[:]) - @. D0 *= 2.0*float(nelement_global)/L + #@. D0 *= 2.0*float(nelement_global)/L end """ @@ -486,100 +467,13 @@ function Legendre_h_n(k) h_n = 2.0/(2.0*k + 1) return h_n end -""" -difference prefac between Gauss-Legendre -and Gauss-Legendre-Lobatto points for the mass matrix -""" -function alpha_n(N) - gamma_n = 2.0/N - h_n = Legendre_h_n(N) - alpha = (h_n - gamma_n)/(gamma_n^2) - return alpha -end - -function beta_n(N) - gamma_n = 2.0/N - h_n = Legendre_h_n(N) - beta = (gamma_n - h_n)/(gamma_n*h_n) - return beta -end -""" -assign Gauss-Legendre-Lobatto mass matrix on a 1D line with Jacobian = 1 -""" -function GaussLegendreLobatto_mass_matrix!(MM,ngrid,x,wgts,L,nelement_global) - N = ngrid - 1 - alpha = alpha_n(N) - MM .= 0.0 - ## off diagonal components - for i in 1:ngrid - for j in 1:ngrid - MM[i,j] = alpha*wgts[i]*wgts[j]*Pl(x[i],N)*Pl(x[j],N) - end - end - ## diagonal components - for i in 1:ngrid - MM[i,i] += wgts[i] - end - @. MM *= (0.5*L/nelement_global) - return nothing -end -""" -exact inverse of Gauss-Legendre-Lobatto mass matrix for testing -""" -function GaussLegendreLobatto_inverse_mass_matrix!(MM,ngrid,x,wgts,L) - N = ngrid - 1 - beta = beta_n(N) - MM .= 0.0 - ## off diagonal components - for i in 1:ngrid - for j in 1:ngrid - MM[i,j] = beta*Pl(x[i],N)*Pl(x[j],N) - end - end - ## diagonal components - for i in 1:ngrid - MM[i,i] += 1.0/wgts[i] - end - @. MM *= 1.0/(L/2.0) - return nothing -end -""" -Gauss-Legendre-Lobatto S matrix Sjk = < lj | l'k > -Use that Djk = l'k(xj) -""" -function GaussLegendreLobatto_S_matrix!(SS,ngrid,DD,wgts,L) - N = ngrid - 1 - SS .= 0.0 - for j in 1:ngrid - for i in 1:ngrid - SS[i,j] += (L/2.0)*wgts[i]*DD[i,j] - end - end - return nothing -end -""" -Gauss-Legendre-Lobatto K matrix Kjk = -< l'j | l'k > -Use that Djk = l'k(xj) -""" -function GaussLegendreLobatto_K_matrix!(KK,ngrid,DD,wgts,L,nelement_global) - N = ngrid - 1 - KK .= 0.0 - for j in 1:ngrid - for i in 1:ngrid - for m in 1:ngrid - KK[i,j] -= (0.5*L/nelement_global)*wgts[m]*DD[m,i]*DD[m,j] - end - end - end - return nothing -end """ assign abitrary weak inner product matrix Q on a 1D line with Jacobian = 1 matrix Q acts on a single vector x such that y = Q * x is also a vector """ -function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,2},ngrid,x,wgts,L,nelement_global,option;radau=false) +function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,2},ngrid,x,wgts,option;radau=false) # coefficient in expansion of # lagrange polys in terms of Legendre polys gamma = allocate_float(ngrid) @@ -708,16 +602,6 @@ function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,2},ngrid,x,wgts,L end end end - #if option == "K0" || option == "K1" || option == "S0" || option == "P0" - # compute diagonal from off-diagonal values - # to ensure numerical stability - # for i in 1:ngrid - # QQ[i,i] = 0.0 - # QQ[i,i] = -sum(QQ[i,:]) - # end - #end - # return normalised Q (no scale factors) - #@. QQ *= (0.5*L/nelement_global) return nothing end @@ -726,7 +610,7 @@ assign abitrary weak inner product matrix Q on a 1D line with Jacobian = 1 matrix Q acts on two vectors x1 and x2 such that the quadratic form y = x1 * Q * x2 is also a vector """ -function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,3},ngrid,x,wgts,L,nelement_global,option;radau=false) +function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,3},ngrid,x,wgts,option;radau=false) # coefficient in expansion of # lagrange polys in terms of Legendre polys gamma = allocate_float(ngrid) @@ -851,84 +735,6 @@ function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,3},ngrid,x,wgts,L return nothing end -""" -assign mass matrix M1mn = < lm|x|ln > on a 1D line with Jacobian = 1 -""" -function GaussLegendre_mass_matrix_1!(MM,ngrid,x,wgts,L,nelement_global) - # coefficient in expansion of - # lagrange polys in terms of Legendre polys - gamma = allocate_float(ngrid) - for i in 1:ngrid-1 - gamma[i] = Legendre_h_n(i-1) - end - gamma[ngrid] = 2.0/(ngrid - 1) - # appropriate inner product of Legendre polys - # < P_i P_j x > - AA = allocate_float(ngrid,ngrid) - nquad = 2*ngrid - zz, wz = gausslegendre(nquad) - @. AA = 0.0 - for j in 1:ngrid - for i in 1:ngrid - for k in 1:nquad - AA[i,j] += zz[k]*wz[k]*Pl(zz[k],i-1)*Pl(zz[k],j-1) - end - end - end - - MM .= 0.0 - for i in 1:ngrid - for j in 1:ngrid - for k in 1:ngrid - for l in 1:ngrid - MM[i,j] += wgts[i]*wgts[j]*Pl(x[i],k-1)*Pl(x[j],l-1)*AA[k,l]/(gamma[k]*gamma[l]) - end - end - end - end - @. MM *= (0.5*L/nelement_global) - return nothing -end - -""" -assign derivative matrix S1mn = < l'm|x|ln > on a 1D line with Jacobian = 1 -""" -function GaussLegendre_S_matrix_1!(SS,ngrid,x,wgts,L,nelement_global) - # coefficient in expansion of - # lagrange polys in terms of Legendre polys - gamma = allocate_float(ngrid) - for i in 1:ngrid-1 - gamma[i] = Legendre_h_n(i-1) - end - gamma[ngrid] = 2.0/(ngrid - 1) - # appropriate inner product of Legendre polys - # < P'_i P_j x > - AA = allocate_float(ngrid,ngrid) - nquad = 2*ngrid - zz, wz = gausslegendre(nquad) - @. AA = 0.0 - for j in 1:ngrid - for i in 1:ngrid - for k in 1:nquad - AA[i,j] += zz[k]*wz[k]*dnPl(zz[k],i-1,1)*Pl(zz[k],j-1) - end - end - end - - SS .= 0.0 - for i in 1:ngrid - for j in 1:ngrid - for k in 1:ngrid - for l in 1:ngrid - SS[i,j] -= wgts[i]*wgts[j]*Pl(x[i],k-1)*Pl(x[j],l-1)*AA[k,l]/(gamma[k]*gamma[l]) - end - end - end - end - @. SS *= (0.5*L/nelement_global) - return nothing -end - function scale_factor_func(L,nelement_global) return 0.5*L/float(nelement_global) end @@ -948,30 +754,26 @@ end function for setting up the full Gauss-Legendre-Lobatto grid and collocation point weights """ -function scaled_gauss_legendre_lobatto_grid(ngrid, nelement_global, nelement_local, - n, irank, box_length, imin, imax) +function scaled_gauss_legendre_lobatto_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax) # get Gauss-Legendre-Lobatto points and weights on [-1,1] x, w = gausslobatto(ngrid) - # factor with maps [-1,1] -> [-L/2, L/2] - scale_factor = scale_factor_func(box_length,nelement_global) #0.5*box_length/float(nelement_global) # grid and weights arrays - grid = allocate_float(n) - wgts = allocate_float(n) + grid = allocate_float(n_local) + wgts = allocate_float(n_local) wgts .= 0.0 #integer to deal with the overlap of element boundaries k = 1 @inbounds for j in 1:nelement_local - # calculate the grid avoiding overlap - #iel_global = j + irank*nelement_local - #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) - shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) - @. grid[imin[j]:imax[j]] = scale_factor*x[k:ngrid] + shift + # element_scale[j] + # element_shift[j] + # factor with maps [-1,1] -> a subset of [-L/2, L/2] + @. grid[imin[j]:imax[j]] = element_scale[j]*x[k:ngrid] + element_shift[j] # calculate the weights # remembering on boundary points to include weights # from both left and right elements #println(imin[j]," ",imax[j]) - @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w[1:ngrid] + @. wgts[imin[j] - k + 1:imax[j]] += element_scale[j]*w[1:ngrid] k = 2 end @@ -983,121 +785,49 @@ function for setting up the full Gauss-Legendre-Radau grid and collocation point weights see comments of Gauss-Legendre-Lobatto routine above """ -function scaled_gauss_legendre_radau_grid(ngrid, nelement_global, nelement_local, - n, irank, box_length, imin, imax) +function scaled_gauss_legendre_radau_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax, irank) # get Gauss-Legendre-Lobatto points and weights on [-1,1] x_lob, w_lob = gausslobatto(ngrid) # get Gauss-Legendre-Radau points and weights on [-1,1) x_rad, w_rad = gaussradau(ngrid) # transform to a Gauss-Legendre-Radau grid on (-1,1] x_rad, w_rad = -reverse(x_rad), reverse(w_rad)# - - # factor with maps [-1,1] -> [-L/2, L/2] - scale_factor = scale_factor_func(box_length,nelement_global) - #scale_factor = 0.5*box_length/float(nelement_global) # grid and weights arrays - grid = allocate_float(n) - wgts = allocate_float(n) + grid = allocate_float(n_local) + wgts = allocate_float(n_local) wgts .= 0.0 if irank == 0 # for 1st element, fill in with Gauss-Legendre-Radau points j = 1 - #iel_global = j + irank*nelement_local - #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) - shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) - @. grid[imin[j]:imax[j]] = scale_factor*x_rad[1:ngrid] + shift - @. wgts[imin[j]:imax[j]] += scale_factor*w_rad[1:ngrid] - + # element_scale[j] + # element_shift[j] + # factor with maps [-1,1] -> a subset of [-L/2, L/2] + @. grid[imin[j]:imax[j]] = element_scale[j]*x_rad[1:ngrid] + element_shift[j] + @. wgts[imin[j]:imax[j]] += element_scale[j]*w_rad[1:ngrid] #integer to deal with the overlap of element boundaries k = 2 @inbounds for j in 2:nelement_local - # calculate the grid avoiding overlap - #iel_global = j + irank*nelement_local - #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) - shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) - @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift - @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] + # element_scale[j] + # element_shift[j] + # factor with maps [-1,1] -> a subset of [-L/2, L/2] + @. grid[imin[j]:imax[j]] = element_scale[j]*x_lob[k:ngrid] + element_shift[j] + @. wgts[imin[j] - k + 1:imax[j]] += element_scale[j]*w_lob[1:ngrid] end else # all elements are Gauss-Legendre-Lobatto #integer to deal with the overlap of element boundaries k = 1 @inbounds for j in 1:nelement_local - # calculate the grid avoiding overlap - #iel_global = j + irank*nelement_local - #shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) - shift = shift_factor_func(box_length,nelement_global,nelement_local,irank,j) - @. grid[imin[j]:imax[j]] = scale_factor*x_lob[k:ngrid] + shift - @. wgts[imin[j] - k + 1:imax[j]] += scale_factor*w_lob[1:ngrid] - + # element_scale[j] + # element_shift[j] + # factor with maps [-1,1] -> a subset of [-L/2, L/2] + @. grid[imin[j]:imax[j]] = element_scale[j]*x_lob[k:ngrid] + element_shift[j] + @. wgts[imin[j] - k + 1:imax[j]] += element_scale[j]*w_lob[1:ngrid] k = 2 end end return grid, wgts end -""" -function that assigns the local mass matrices to -a global array for later solving weak form of required -1D equation -""" -function setup_global_mass_matrix!(mass_matrix::Array{mk_float,2}, - lobatto::gausslegendre_base_info, - radau::gausslegendre_base_info, - coord) - ngrid = coord.ngrid - imin = coord.imin - imax = coord.imax - @. mass_matrix = 0.0 - if coord.name == "vperp" - # use the Radau mass matrix for the 1st element - Mmat_fel = radau.Mmat - else - # use the Lobatto mass matrix - Mmat_fel = lobatto.Mmat - end - zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" - zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" - - # fill in first element - j = 1 - if zero_bc_lower_boundary #x.bc == "zero" - mass_matrix[imin[j],imin[j]:imax[j]] .+= Mmat_fel[1,:]./2.0 #contributions from this element/2 - mass_matrix[imin[j],imin[j]] += Mmat_fel[ngrid,ngrid]/2.0 #contribution from missing `zero' element/2 - else - mass_matrix[imin[j],imin[j]:imax[j]] .+= Mmat_fel[1,:] - end - for k in 2:imax[j]-imin[j] - mass_matrix[k,imin[j]:imax[j]] .+= Mmat_fel[k,:] - end - if zero_bc_upper_boundary && coord.nelement_local == 1 - mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:]./2.0 #contributions from this element/2 - mass_matrix[imax[j],imax[j]] += lobatto.Mmat[1,1]/2.0 #contribution from missing `zero' element/2 - elseif coord.nelement_local > 1 #x.bc == "zero" - mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:]./2.0 - else - mass_matrix[imax[j],imin[j]:imax[j]] .+= Mmat_fel[ngrid,:] - end - # remaining elements recalling definitions of imax and imin - for j in 2:coord.nelement_local - #lower boundary condition on element - mass_matrix[imin[j]-1,imin[j]-1:imax[j]] .+= lobatto.Mmat[1,:]./2.0 - for k in 2:imax[j]-imin[j]+1 - mass_matrix[k+imin[j]-2,imin[j]-1:imax[j]] .+= lobatto.Mmat[k,:] - end - # upper boundary condition on element - if j == coord.nelement_local && !(zero_bc_upper_boundary) - mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:] - elseif j == coord.nelement_local && zero_bc_upper_boundary - mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:]./2.0 #contributions from this element/2 - mass_matrix[imax[j],imax[j]] += lobatto.Mmat[1,1]/2.0 #contribution from missing `zero' element/2 - else - mass_matrix[imax[j],imin[j]-1:imax[j]] .+= lobatto.Mmat[ngrid,:]./2.0 - end - end - - return nothing -end - """ function that assigns the local weak-form matrices to a global array QQ_global for later solving weak form of required @@ -1116,32 +846,26 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, imin = coord.imin imax = coord.imax @. QQ_global = 0.0 - + mass_matrix = option == "M" if coord.name == "vperp" - zero_bc_upper_boundary = true - zero_bc_lower_boundary = false - zero_gradient_bc_lower_boundary = false#true + zero_bc_upper_boundary = true && mass_matrix + zero_bc_lower_boundary = false && mass_matrix + zero_gradient_bc_lower_boundary = false && mass_matrix else - zero_bc_upper_boundary = coord.bc == "zero" || coord.bc == "zero_upper" - zero_bc_lower_boundary = coord.bc == "zero" || coord.bc == "zero_lower" - zero_gradient_bc_lower_boundary = false + zero_bc_upper_boundary = (coord.bc == "zero" || coord.bc == "zero_upper") && mass_matrix + zero_bc_lower_boundary = (coord.bc == "zero" || coord.bc == "zero_lower") && mass_matrix + zero_gradient_bc_lower_boundary = false && mass_matrix end # fill in first element j = 1 # N.B. QQ varies with ielement for vperp, but not vpa get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) - get_QQ_local!(QQ_jp1,j+1,lobatto,radau,coord,option) if zero_bc_lower_boundary #x.bc == "zero" - QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:]./2.0 #contributions from this element/2 - QQ_global[imin[j],imin[j]] += QQ_j[ngrid,ngrid]/2.0 #contribution from missing `zero' element/2 + QQ_global[imin[j],imin[j]:imax[j]] .= 0.0 + QQ_global[imin[j],imin[j]] = 1.0 elseif zero_gradient_bc_lower_boundary - if option == "M" && coord.name == "vperp" - #QQ_global[imin[j],imin[j]:imax[j]] .= lobatto.D0[:] QQ_global[imin[j],imin[j]:imax[j]] .= radau.D0[:] - else - QQ_global[imin[j],imin[j]:imax[j]] .= 0.0 - end else QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:] end @@ -1149,8 +873,8 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, QQ_global[k,imin[j]:imax[j]] .+= QQ_j[k,:] end if zero_bc_upper_boundary && coord.nelement_local == 1 - QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:]./2.0 #contributions from this element/2 - QQ_global[imax[j],imax[j]] += QQ_jp1[1,1]/2.0 #contribution from missing `zero' element/2 + QQ_global[imax[j],imin[j]:imax[j]] .= 0.0 + QQ_global[imax[j],imax[j]] = 1.0 elseif coord.nelement_local > 1 #x.bc == "zero" QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:]./2.0 else @@ -1159,8 +883,7 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, # remaining elements recalling definitions of imax and imin for j in 2:coord.nelement_local get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) - get_QQ_local!(QQ_jp1,j+1,lobatto,radau,coord,option) - + #lower boundary condition on element QQ_global[imin[j]-1,imin[j]-1:imax[j]] .+= QQ_j[1,:]./2.0 for k in 2:imax[j]-imin[j]+1 @@ -1170,8 +893,8 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, if j == coord.nelement_local && !(zero_bc_upper_boundary) QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:] elseif j == coord.nelement_local && zero_bc_upper_boundary - QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:]./2.0 #contributions from this element/2 - QQ_global[imax[j],imax[j]] += QQ_jp1[1,1]/2.0 #contribution from missing `zero' element/2 + QQ_global[imax[j],imin[j]-1:imax[j]] .= 0.0 #contributions from this element/2 + QQ_global[imax[j],imax[j]] = 1.0 else QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:]./2.0 end @@ -1212,8 +935,8 @@ function get_MM_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1232,8 +955,8 @@ function get_SS_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1252,8 +975,8 @@ function get_KK_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) @@ -1300,8 +1023,8 @@ function get_LL_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral # (1/vperp) d ( vperp d (.) / d vperp) @@ -1323,8 +1046,7 @@ function get_MN_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - #shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1345,8 +1067,8 @@ function get_MR_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1371,8 +1093,8 @@ function get_PP_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1394,8 +1116,8 @@ function get_PU_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1444,8 +1166,8 @@ function get_YY0_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1464,8 +1186,8 @@ function get_YY1_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1484,8 +1206,8 @@ function get_YY2_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @@ -1504,8 +1226,8 @@ function get_YY3_local!(QQ,ielement, radau::gausslegendre_base_info, coord) - scale_factor = scale_factor_func(coord.L,coord.nelement_global) - shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L + scale_factor = coord.element_scale[ielement] + shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index a3bab15cb..badbe2274 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1113,7 +1113,7 @@ 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_spectrla, vpa_adv, z_adv, r_adv, composition, scratch_dummy, + z_bc, r_bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_adv, z_adv, r_adv, composition, scratch_dummy, r_diffusion, vpa_diffusion) if vpa.n > 1 begin_s_r_z_vperp_region() @@ -1121,7 +1121,7 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v # enforce the vpa BC # use that adv.speed independent of vpa @views enforce_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, - vpa_adv[is].speed[:,ivperp,iz,ir], advance.vpa_diffusion, + vpa_adv[is].speed[:,ivperp,iz,ir], vpa_diffusion, vpa, vpa_spectral) end end @@ -1327,7 +1327,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, - scratch_dummy.buffer_vzvrvzetazrsn) + scratch_dummy.buffer_vzvrvzetazrsn_1) end if r.n > 1 begin_sn_z_vzeta_vr_vz_region() diff --git a/src/input_structs.jl b/src/input_structs.jl index d670061e8..aee9d7b72 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -322,6 +322,7 @@ mutable struct collisions_input # ion-ion self collision frequency with C[F_s,F_Ms'] operator nuii_pitch::mk_float # numerical conserving terms (for strong form Fokker-Planck operator only) + numerical_conserving_terms::String end """ diff --git a/src/load_data.jl b/src/load_data.jl index d6087c6f9..f9935b6a0 100644 --- a/src/load_data.jl +++ b/src/load_data.jl @@ -212,6 +212,7 @@ function load_coordinate_data(fid, name; printout=false) L = load_variable(coord_group, "L") discretization = load_variable(coord_group, "discretization") fd_option = load_variable(coord_group, "fd_option") + cheb_option = load_variable(coord_group, "cheb_option") bc = load_variable(coord_group, "bc") element_spacing_option = load_variable(coord_group, "element_spacing_option") @@ -228,7 +229,7 @@ function load_coordinate_data(fid, name; printout=false) end # Define input to create coordinate struct input = grid_input(name, ngrid, nelement_global, nelement_local, nrank, irank, L, - discretization, fd_option, bc, advection_input("", 0.0, 0.0, 0.0), + discretization, fd_option, cheb_option, bc, advection_input("", 0.0, 0.0, 0.0), MPI.COMM_NULL, element_spacing_option) coord, spectral = define_coordinate(input) diff --git a/src/time_advance.jl b/src/time_advance.jl index 602dbb2c4..a9551d428 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -43,7 +43,7 @@ using ..ionization: ionization_collisions_1V!, ionization_collisions_3V!, consta using ..krook_collisions: krook_collisions! using ..numerical_dissipation: vpa_boundary_buffer_decay!, vpa_boundary_buffer_diffusion!, vpa_dissipation!, - z_dissipation!, r_dissipation!, + z_dissipation!, r_dissipation!, vperp_dissipation! vz_dissipation_neutral!, z_dissipation_neutral!, r_dissipation_neutral!, vpa_boundary_force_decreasing!, force_minimum_pdf_value!, @@ -197,7 +197,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, # if no splitting of operators, all terms advanced concurrently; # else, will advance one term at a time. advance = setup_advance_flags(moments, composition, t_input, collisions, - num_diss_params, manufactured_solns_input, rk_coefs, r, + num_diss_params, manufactured_solns_input, rk_coefs, r, z, vperp, vpa, vzeta, vr, vz) begin_serial_region() @@ -215,7 +215,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, elseif advance.explicit_weakform_fp_collisions fp_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true) else - fp_arrays = init_fokker_planck_collisions(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false) + fp_arrays = init_fokker_planck_collisions(vpa,vperp; precompute_weights=false) end # create the "fields" structure that contains arrays # for the electrostatic potential phi and eventually the electromagnetic fields @@ -432,7 +432,7 @@ if no splitting of operators, all terms advanced concurrently; else, will advance one term at a time. """ function setup_advance_flags(moments, composition, t_input, collisions, num_diss_params, - manufactured_solns_input, rk_coefs, r, vperp, vpa, vzeta, vr, + manufactured_solns_input, rk_coefs, r, z, vperp, vpa, vzeta, vr, vz) # default is not to concurrently advance different operators advance_vpa_advection = false @@ -1295,14 +1295,14 @@ stages, if the quantities are evolved separately from the modified pdf; or update them by taking the appropriate velocity moment of the evolved pdf """ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, vr, vzeta, - vpa, vperp, z, r, advect_objects, rk_coefs, istage, composition, - geometry, num_diss_params, z_spectral, r_spectral, advance, - scratch_dummy) + vpa, vperp, z, r, spectral_objects, advect_objects, rk_coefs, istage, composition, + geometry, num_diss_params, advance, scratch_dummy) begin_s_r_z_region() new_scratch = scratch[istage+1] old_scratch = scratch[istage] + z_spectral, r_spectral, vpa_spectral, vperp_spectral = spectral_objects.z_spectral, spectral_objects.r_spectral, spectral_objects.vpa_spectral, spectral_objects.vperp_spectral vpa_advect, r_advect, z_advect = advect_objects.vpa_advect, advect_objects.r_advect, advect_objects.z_advect neutral_z_advect, neutral_r_advect, neutral_vz_advect = advect_objects.neutral_z_advect, advect_objects.neutral_r_advect, advect_objects.neutral_vz_advect @@ -1590,10 +1590,9 @@ function ssp_rk!(pdf, scratch, t, t_input, vz, vr, vzeta, vpa, vperp, gyrophase, collisions, geometry, scratch_dummy, manufactured_source_list, num_diss_params, advance, fp_arrays, istage) @views rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, vr, - vzeta, vpa, vperp, z, r, advect_objects, + vzeta, vpa, vperp, z, r, spectral_objects, advect_objects, advance.rk_coefs[:,istage], istage, composition, geometry, - num_diss_params, spectral_objects.z_spectral, - spectral_objects.r_spectral, advance, scratch_dummy) + num_diss_params, advance, scratch_dummy) end istage = n_rk_stages+1 From 99c17ffc93da824246a3a1a6117848fe0a8ca09a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 3 Nov 2023 09:29:09 +0000 Subject: [PATCH 203/331] Slightly relax the absolute tolerances to permit calculus tests to pass using the matrix differentiation option. --- test/calculus_tests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index b81e35140..9aae36ec8 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -675,7 +675,7 @@ function runtests() # differentiate f derivative!(df, f, x, adv_fac, spectral) - @test isapprox(df, expected_df, rtol=rtol, atol=1.e-14, + @test isapprox(df, expected_df, rtol=rtol, atol=1.e-12, norm=maxabs_norm) end end @@ -726,7 +726,7 @@ function runtests() # something like p*(round-off) for x^p (?) so error on expected_df would # be p*p*(round-off), or plausibly 1024*(round-off), so tolerance of # 2e-11 isn't unreasonable. - @test isapprox(df, expected_df, rtol=2.0e-11, atol=2.0e-12, + @test isapprox(df, expected_df, rtol=2.0e-11, atol=6.0e-12, norm=maxabs_norm) end end @@ -782,7 +782,7 @@ function runtests() # something like p*(round-off) for x^p (?) so error on expected_df # would be p*p*(round-off), or plausibly 1024*(round-off), so # tolerance of 3e-11 isn't unreasonable. - @test isapprox(df, expected_df, rtol=3.0e-11, atol=3.0e-12, + @test isapprox(df, expected_df, rtol=3.0e-11, atol=1.5e-11, norm=maxabs_norm) end end From 6f382b4d804ae84c5aede7207e4bf7ae68b75fa4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 3 Nov 2023 09:59:50 +0000 Subject: [PATCH 204/331] Minor merge corrections to make code run. --- src/makie_post_processing.jl | 2 +- src/numerical_dissipation.jl | 8 +++++--- src/post_processing.jl | 18 +++++++++++------- src/time_advance.jl | 2 +- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/makie_post_processing.jl b/src/makie_post_processing.jl index b4925486d..afbe2a9e8 100644 --- a/src/makie_post_processing.jl +++ b/src/makie_post_processing.jl @@ -816,7 +816,7 @@ function get_run_info(run_dir, restart_index=nothing; itime_min=1, itime_max=-1, dummy_adv_input = advection_input("default", 1.0, 0.0, 0.0) dummy_comm = MPI.COMM_NULL dummy_input = grid_input("dummy", 1, 1, 1, 1, 0, 1.0, - "chebyshev_pseudospectral", "", "periodic", + "chebyshev_pseudospectral", "", "", "periodic", dummy_adv_input, dummy_comm, "uniform") vzeta, vzeta_spectral = define_coordinate(dummy_input) vzeta_chunk_size = 1 diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index 163b7cecf..c6a4a34bb 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -3,9 +3,11 @@ module numerical_dissipation export setup_numerical_dissipation, vpa_boundary_buffer_decay!, - vpa_boundary_buffer_diffusion!, vpa_dissipation!, z_dissipation!, - r_dissipation!, force_minimum_pdf_value!, force_minimum_pdf_value_neutral!, - vperp_dissipation! + vpa_boundary_buffer_diffusion!, force_minimum_pdf_value!, force_minimum_pdf_value_neutral!, + vpa_dissipation!, vperp_dissipation!, + z_dissipation!, r_dissipation!, + vz_dissipation_neutral!, z_dissipation_neutral!, + r_dissipation_neutral! using Base.Iterators: flatten diff --git a/src/post_processing.jl b/src/post_processing.jl index 93bff2ba3..561d3e20a 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -199,7 +199,7 @@ function construct_global_zr_coords(r_local, z_local) function make_global_input(coord_local) return grid_input(coord_local.name, coord_local.ngrid, coord_local.nelement_global, coord_local.nelement_global, 1, 0, coord_local.L, - coord_local.discretization, coord_local.fd_option, coord_local.bc, + coord_local.discretization, coord_local.fd_option, coord_local.cheb_option, coord_local.bc, coord_local.advection, MPI.COMM_NULL, coord_local.element_spacing_option) end @@ -1038,6 +1038,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) perpendicular_pressure = perpendicular_pressure[1] parallel_heat_flux = parallel_heat_flux[1] thermal_speed = thermal_speed[1] + entropy_production = entropy_production[1] time = time[1] ntime = ntime[1] time_pdfs = time_pdfs[1] @@ -1076,7 +1077,8 @@ function analyze_and_plot_data(prefix...; run_index=nothing) if !is_1D1V # make plots and animations of the phi, Ez and Er - plot_charged_moments_2D(density, parallel_flow, parallel_pressure, time, + plot_charged_moments_2D(density, parallel_flow, parallel_pressure, + perpendicular_pressure, thermal_speed, entropy_production, 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 @@ -1093,9 +1095,11 @@ function analyze_and_plot_data(prefix...; run_index=nothing) if Maxwellian_diagnostic pressure = copy(parallel_pressure) @. pressure = (2.0*perpendicular_pressure + parallel_pressure)/3.0 - plot_Maxwellian_diagnostic(ff[:,:,iz0,ir0,:,:], density[iz0,ir0,:,:], - parallel_flow[iz0,ir0,:,:], thermal_speed[iz0,ir0,:,:], vpa_local, vpa_local_wgts, - vperp_local, vperp_local_wgts, time, iz0, ir0, run_name, n_ion_species) + ff = load_distributed_charged_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, + vperp.grid, vperp.wgts, time, iz0, ir0, run_name_label, n_ion_species) end # make plots and animations of the neutral pdf if n_neutral_species > 0 @@ -3194,7 +3198,6 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r ivperp0_string = string("_ivperp0", string(ivperp0)) iz0_string = string("_iz0", string(iz0)) ir0_string = string("_ir0", string(ir0)) - nvperp = size(vperp,1) # create animations of the ion pdf if n_species > 1 spec_string = [string("_", spec_type, "_spec", string(is)) for is ∈ 1:n_species] @@ -3465,7 +3468,8 @@ 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, perpendicular_pressure, thermal_speed, +function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, + perpendicular_pressure, thermal_speed, entropy_production, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) nr = size(r,1) diff --git a/src/time_advance.jl b/src/time_advance.jl index a9551d428..2c014c3a6 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -43,7 +43,7 @@ using ..ionization: ionization_collisions_1V!, ionization_collisions_3V!, consta using ..krook_collisions: krook_collisions! using ..numerical_dissipation: vpa_boundary_buffer_decay!, vpa_boundary_buffer_diffusion!, vpa_dissipation!, - z_dissipation!, r_dissipation!, vperp_dissipation! + z_dissipation!, r_dissipation!, vperp_dissipation!, vz_dissipation_neutral!, z_dissipation_neutral!, r_dissipation_neutral!, vpa_boundary_force_decreasing!, force_minimum_pdf_value!, From 993006f6e3c6fa0226820b3db460a05b9ac8082f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 3 Nov 2023 12:31:19 +0000 Subject: [PATCH 205/331] Slightly relax the absolute tolerances to permit calculus tests to pass using the matrix differentiation option. --- test/calculus_tests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index 9aae36ec8..89b1e54dc 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -782,7 +782,7 @@ function runtests() # something like p*(round-off) for x^p (?) so error on expected_df # would be p*p*(round-off), or plausibly 1024*(round-off), so # tolerance of 3e-11 isn't unreasonable. - @test isapprox(df, expected_df, rtol=3.0e-11, atol=1.5e-11, + @test isapprox(df, expected_df, rtol=3.0e-11, atol=3.0e-11, norm=maxabs_norm) end end From dd7c14f76289d52fb92d135b5cd83c7acae9bcd4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 10:29:14 +0000 Subject: [PATCH 206/331] Fix merge of 1D interpolation routines in chebyshev.jl --- src/chebyshev.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index ca51548b4..617dda3d4 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -17,7 +17,7 @@ using FFTW using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_complex using ..clenshaw_curtis: clenshawcurtisweights -import ..interpolation: interpolate_to_grid_1d +import ..interpolation: interpolate_to_grid_1d! """ """ @@ -400,12 +400,14 @@ coord : coordinate `coordinate` struct giving the coordinate along which f varies chebyshev : chebyshev_info struct containing information for Chebyshev transforms + +Note that this routine does not support Gauss-Chebyshev-Radau elements """ function interpolate_to_grid_1d!(result, newgrid, f, coord, chebyshev::chebyshev_info) # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds - @boundscheck nelement == size(chebyshev.f,2) || throw(BoundsError(chebyshev.f)) + @boundscheck nelement == size(chebyshev.lobatto.f,2) || throw(BoundsError(chebyshev.lobatto.f)) n_new = size(newgrid)[1] # Find which points belong to which element. @@ -440,7 +442,7 @@ function interpolate_to_grid_1d!(result, newgrid, f, coord, chebyshev::chebyshev @views chebyshev_interpolate_single_element!(result[kmin:kmax], newgrid[kmin:kmax], f[imin:imax], - imin, imax, coord, chebyshev) + imin, imax, coord, chebyshev.lobatto) end @inbounds for j ∈ 2:nelement kmin = kstart[j] @@ -451,7 +453,7 @@ function interpolate_to_grid_1d!(result, newgrid, f, coord, chebyshev::chebyshev @views chebyshev_interpolate_single_element!(result[kmin:kmax], newgrid[kmin:kmax], f[imin:imax], - imin, imax, coord, chebyshev) + imin, imax, coord, chebyshev.lobatto) end end @@ -464,7 +466,7 @@ end """ """ -function chebyshev_interpolate_single_element!(result, newgrid, f, imin, imax, coord, chebyshev) +function chebyshev_interpolate_single_element!(result, newgrid, f, imin, imax, coord, chebyshev::chebyshev_base_info) # Temporary buffer to store Chebyshev coefficients cheby_f = chebyshev.df @@ -475,7 +477,7 @@ function chebyshev_interpolate_single_element!(result, newgrid, f, imin, imax, c scale = 2.0 / (coord.grid[imax] - coord.grid[imin]) # Get Chebyshev coefficients - chebyshev_forward_transform!(cheby_f, chebyshev.lobatto.fext, f, chebyshev.lobatto.forward, coord.ngrid) + chebyshev_forward_transform!(cheby_f, chebyshev.fext, f, chebyshev.forward, coord.ngrid) for i ∈ 1:length(newgrid) x = newgrid[i] From a7bc2c03fdf67794bffcb5917907baf8700606a1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 10:34:38 +0000 Subject: [PATCH 207/331] Insert cheb_option into coordinate definitions in velocity_integral_tests.jl. --- test/velocity_integral_tests.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/velocity_integral_tests.jl b/test/velocity_integral_tests.jl index 07d0c5027..2ceaa7e86 100644 --- a/test/velocity_integral_tests.jl +++ b/test/velocity_integral_tests.jl @@ -26,6 +26,7 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant discretization = "chebyshev_pseudospectral" fd_option = "fourth_order_centered" + cheb_option = "FFT" adv_input = advection_input("default", 1.0, 0.0, 0.0) nrank = 1 irank = 0 @@ -33,15 +34,15 @@ function runtests() # create the 'input' struct containing input info needed to create a # coordinate vr_input = grid_input("vperp", 1, 1, 1, nrank, irank, 1.0, discretization, - fd_option, bc, adv_input, comm, "uniform") + fd_option, cheb_option, bc, adv_input, comm, "uniform") vz_input = grid_input("vpa", ngrid, nelement_global, nelement_local, nrank, irank, - Lvpa, discretization, fd_option, bc, adv_input, comm, + Lvpa, discretization, fd_option, cheb_option, bc, adv_input, comm, "uniform") vpa_input = grid_input("vpa", ngrid, nelement_global, nelement_local, nrank, - irank, Lvpa, discretization, fd_option, bc, adv_input, + irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input, comm, "uniform") vperp_input = grid_input("vperp", ngrid, nelement_global, nelement_local, nrank, - irank, Lvperp, discretization, fd_option, bc, adv_input, + irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input, comm, "uniform") # create the coordinate struct 'x' vpa, vpa_spectral = define_coordinate(vpa_input) From f45396295285c3735ab3db1b6dbbcd0c70be6da3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 10:39:40 +0000 Subject: [PATCH 208/331] Revert factor of 0.5 to 1.0 (removed) so that nonlinear sound wave tests pass with normalisation of pressure used in master branch. --- test/nonlinear_sound_wave_tests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/nonlinear_sound_wave_tests.jl b/test/nonlinear_sound_wave_tests.jl index 231a2f88a..471e5c1b2 100644 --- a/test/nonlinear_sound_wave_tests.jl +++ b/test/nonlinear_sound_wave_tests.jl @@ -420,7 +420,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) 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_ppar_charged = 0.5*interpolate_to_grid_z(expected.z, ppar_charged[:, :, tind], z, z_spectral) + 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_vth_charged = @. sqrt(2.0*newgrid_ppar_charged/newgrid_n_charged) From 417441e3a2e471f570c6498edced2de6718c1b2c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 10:57:08 +0000 Subject: [PATCH 209/331] Set width of ionization source back to value in master branch so that Harrison Thompson tests pass. --- src/ionization.jl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ionization.jl b/src/ionization.jl index c39f19e99..a921792e3 100644 --- a/src/ionization.jl +++ b/src/ionization.jl @@ -27,14 +27,13 @@ function constant_ionization_source!(f_out, vpa, vperp, z, r, moments, compositi # e.g., width=0.15. Possibly narrower widths would require more vpa # resolution, which then causes crashes due to overshoots giving # negative f?? - width = 1.0 - zwidth = 0.25 - rwidth = 0.25 - vperpwidth = 1.0 + width = 0.5 + vperpwidth = 0.5 + rwidth = 0.5 if vperp.n > 1 - prefac = 1.0/vperpwidth^2 + vperpprefac = 1.0/vperpwidth^2 else - prefac = 1.0 + vperpprefac = 1.0 end # loop below relies on vperp[1] = 0 when vperp.n = 1 @loop_s_r is ir begin @@ -58,7 +57,7 @@ function constant_ionization_source!(f_out, vpa, vperp, z, r, moments, compositi prefactor = 1.0 end @loop_vperp_vpa ivperp ivpa begin - vperpfac = exp( - (vperp.grid[ivperp]/vperpwidth)^2) + vperpfac = vperpprefac*exp( - (vperp.grid[ivperp]/vperpwidth)^2) f_out[ivpa,ivperp,iz,ir,is] += dt*rfac*vperpfac*collisions.ionization/width*prefactor*exp(-(vpa.scratch[ivpa]/width)^2) end end From 355120fc2f091a4aab45171d60584ce8b8f2691b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 11:57:56 +0000 Subject: [PATCH 210/331] Fix saving of the cheb_option. --- src/file_io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/file_io.jl b/src/file_io.jl index 037e0df1e..a6010ba42 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -522,8 +522,8 @@ function define_io_coordinate!(parent, coord, coord_name, description, parallel_ description="type of finite difference for $coord_name, if used") # write the finite-difference option for the coordinate - write_single_value!(group, "cheb_option", coord.fd_option; parallel_io=parallel_io, - description="type of finite difference for $coord_name, if used") + write_single_value!(group, "cheb_option", coord.cheb_option; parallel_io=parallel_io, + description="type of chebyshev differentiation used for $coord_name, if used") # write the boundary condition for the coordinate write_single_value!(group, "bc", coord.bc; parallel_io=parallel_io, From e98d2a3e931b20f734fcc0511b9e484799415e28 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 11:58:43 +0000 Subject: [PATCH 211/331] Fix the reconciliation of the internal element boundaries and global periodicity (when using distributed memory MPI). --- src/initial_conditions.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index badbe2274..c9210521c 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1218,9 +1218,10 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St 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] = f[ivpa,ivperp,1,ir,is] - end2[ivpa,ivperp,ir,is] = f[ivpa,ivperp,nz,ir,is] + 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, From f074f76de3ed35eca925915918deefdc31776b35 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 6 Nov 2023 13:01:06 +0000 Subject: [PATCH 212/331] Using Gauss-Chebyshev-Radau element for vperp grid in the element nearest the origin. Update these routines to use the element_scale and element_shift variables. --- src/chebyshev.jl | 36 ++++++++++++++++-------------------- src/coordinates.jl | 2 +- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index 617dda3d4..ab3d190b0 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -145,8 +145,8 @@ function scaled_chebyshev_grid(ngrid, nelement_local, n, return grid, wgts end -function scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n, - irank, box_length, imin, imax) +function scaled_chebyshev_radau_grid(ngrid, nelement_local, n, + element_scale, element_shift, imin, imax, irank) # initialize chebyshev grid defined on [1,-1] # with n grid points chosen to facilitate # the fast Chebyshev transform (aka the discrete cosine transform) @@ -159,34 +159,30 @@ function scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n, # setup the scale factor by which the Chebyshev grid on [-1,1] # is to be multiplied to account for the full domain [-L/2,L/2] # and the splitting into nelement elements with ngrid grid points - scale_factor = 0.5*box_length/float(nelement_global) if irank == 0 # use a Chebyshev-Gauss-Radau element for the lowest element on rank 0 - shift = box_length*(0.5/float(nelement_global) - 0.5) + scale_factor = element_scale[1] + shift = element_shift[1] grid[imin[1]:imax[1]] .= (chebyshev_radau_grid[1:ngrid] * scale_factor) .+ shift # account for the fact that the minimum index needed for the chebyshev_grid # within each element changes from 1 to 2 in going from the first element # to the remaining elements k = 2 @inbounds for j ∈ 2:nelement_local - #wgts[imin[j]:imax[j]] .= sqrt.(1.0 .- reverse(chebyshev_grid)[k:ngrid].^2) * scale_factor - # amount by which to shift the centre of this element from zero - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + scale_factor = element_scale[j] + shift = element_shift[j] # reverse the order of the original chebyshev_grid (ran from [1,-1]) # and apply the scale factor and shift grid[imin[j]:imax[j]] .= (reverse(chebyshev_grid)[k:ngrid] * scale_factor) .+ shift end - wgts = clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, scale_factor) + wgts = clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, element_scale) else # account for the fact that the minimum index needed for the chebyshev_grid # within each element changes from 1 to 2 in going from the first element # to the remaining elements k = 1 @inbounds for j ∈ 1:nelement_local - #wgts[imin[j]:imax[j]] .= sqrt.(1.0 .- reverse(chebyshev_grid)[k:ngrid].^2) * scale_factor - # amount by which to shift the centre of this element from zero - iel_global = j + irank*nelement_local - shift = box_length*((float(iel_global)-0.5)/float(nelement_global) - 0.5) + scale_factor = element_scale[j] + shift = element_shift[j] # reverse the order of the original chebyshev_grid (ran from [1,-1]) # and apply the scale factor and shift grid[imin[j]:imax[j]] .= (reverse(chebyshev_grid)[k:ngrid] * scale_factor) .+ shift @@ -194,7 +190,7 @@ function scaled_chebyshev_radau_grid(ngrid, nelement_global, nelement_local, n, # to avoid double-counting boundary element k = 2 end - wgts = clenshaw_curtis_weights(ngrid, nelement_local, n, imin, imax, scale_factor) + wgts = clenshaw_curtis_weights(ngrid, nelement_local, n, imin, imax, element_scale) end return grid, wgts end @@ -519,23 +515,23 @@ function clenshaw_curtis_weights(ngrid, nelement_local, n, imin, imax, element_s return wgts end -function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, scale_factor) +function clenshaw_curtis_radau_weights(ngrid, nelement_local, n, imin, imax, element_scale) # create array containing the integration weights wgts = zeros(mk_float, n) # calculate the modified Chebshev moments of the first kind μ = chebyshevmoments(ngrid) - wgts_lobatto = clenshawcurtisweights(μ)*scale_factor - wgts_radau = chebyshev_radau_weights(μ, ngrid)*scale_factor + wgts_lobatto = clenshawcurtisweights(μ) + wgts_radau = chebyshev_radau_weights(μ, ngrid) @inbounds begin # calculate the weights within a single element and # scale to account for modified domain (not [-1,1]) - wgts[1:ngrid] .= wgts_radau[1:ngrid] + wgts[1:ngrid] .= wgts_radau[1:ngrid]*element_scale[1] if nelement_local > 1 for j ∈ 2:nelement_local # account for double-counting of points at inner element boundaries - wgts[imin[j]-1] += wgts_lobatto[1] + wgts[imin[j]-1] += wgts_lobatto[1]*element_scale[j] # assign weights for interior of elements and one boundary point - wgts[imin[j]:imax[j]] .= wgts_lobatto[2:ngrid] + wgts[imin[j]:imax[j]] .= wgts_lobatto[2:ngrid]*element_scale[j] end end end diff --git a/src/coordinates.jl b/src/coordinates.jl index daa99f721..db4b006f6 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -260,7 +260,7 @@ function init_grid(ngrid, nelement_local, n_global, n_local, irank, L, element_s elseif discretization == "chebyshev_pseudospectral" if name == "vperp" # initialize chebyshev grid defined on [-L/2,L/2] - grid, wgts = scaled_chebyshev_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax) + grid, wgts = scaled_chebyshev_radau_grid(ngrid, nelement_local, n_local, element_scale, element_shift, imin, imax, irank) wgts = 2.0 .* wgts .* grid # to include 2 vperp in jacobian of integral # see note above on normalisation else From b6cebfdc56621caf806911eca725feb0d26e6bc4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 7 Nov 2023 19:55:01 +0000 Subject: [PATCH 213/331] Added test option where Rosenbluth-potential derived coefficients are computed using the test functions for a Maxwellian input distribution. The moments of the Maxwellian are computed numerically by integration. --- 2D_FEM_assembly_test.jl | 51 +++++++++++------- src/fokker_planck.jl | 107 +++++++++++++++++++++++--------------- src/fokker_planck_test.jl | 28 +++++----- 3 files changed, 109 insertions(+), 77 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index bab9053c6..8266ed7c3 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -23,8 +23,8 @@ using SparseArrays: sparse using LinearAlgebra: mul!, lu, cholesky using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian -using moment_kinetics.fokker_planck_test: d2Gdvpa2, d2Gdvperp2, d2Gdvperpdvpa, dGdvperp -using moment_kinetics.fokker_planck_test: dHdvperp, dHdvpa +using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian +using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! @@ -137,7 +137,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary function test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0,plot_test_output=false,impose_zero_gradient_BC=false, test_parallelism=false,test_self_operator=true, - test_dense_construction=false,standalone=false) + test_dense_construction=false,standalone=false, + use_Maxwellian_Rosenbluth_coefficients=false) # define inputs needed for the test #plot_test_output = false#true #impose_zero_gradient_BC = false#true @@ -315,12 +316,12 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp_M_exact[ivpa,ivperp] = dGdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa_M_exact[ivpa,ivperp] = dHdvpa(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp_M_exact[ivpa,ivperp] = dHdvperp(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, dens,upar,vth,msp, nussp,vpa,vperp,ivpa,ivperp) @@ -340,7 +341,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary fkpl_arrays, vperp, vpa, vperp_spectral, vpa_spectral, test_assembly_serial=test_parallelism, - impose_zero_gradient_BC=impose_zero_gradient_BC) + impose_zero_gradient_BC=impose_zero_gradient_BC, + use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients) # extract C[Fs,Fs'] result # and Rosenbluth potentials for testing begin_vperp_vpa_region() @@ -364,8 +366,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary elliptic_solve!(G_M_num,S_dummy,fkpl_arrays.rpbd.G_data, fkpl_arrays.lu_obj_LP,fkpl_arrays.MM2D_sparse,fkpl_arrays.rhsc, fkpl_arrays.sc,vpa,vperp) - - + init_time = Dates.value(finish_init_time - start_init_time) calculate_time = Dates.value(now() - finish_init_time) begin_serial_region() @@ -374,8 +375,9 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary println("finished C calculation ", Dates.format(now(), dateformat"H:MM:SS")) # test the boundary data calculation - test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) - + if !use_Maxwellian_Rosenbluth_coefficients + test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) + end fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) @@ -385,7 +387,14 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) - + + # calculate the entropy production + lnfC = fkpl_arrays.rhsvpavperp + @loop_vperp_vpa ivperp ivpa begin + lnfC[ivpa,ivperp] = Fs_M[ivpa,ivperp]*C_M_num[ivpa,ivperp] + end + dSdt = - get_density(lnfC,vpa,vperp) + println("dSdt: $dSdt should be >0.0") if plot_test_output plot_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) plot_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) @@ -443,12 +452,12 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary end end - function run_assembly_test(; ngrid=5) + function run_assembly_test(; ngrid=5, nelement_list = [8], impose_zero_gradient_BC= false, plot_scan=true, plot_test_output = false, use_Maxwellian_Rosenbluth_coefficients=false) initialize_comms!() #ngrid = 5 - plot_scan = true - plot_test_output = false - impose_zero_gradient_BC = false + #plot_scan = true + #plot_test_output = true#false + #impose_zero_gradient_BC = false test_parallelism = false test_self_operator = true test_dense_construction = false @@ -459,7 +468,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary #nelement_list = Int[2, 4, 8, 16, 32] #nelement_list = Int[2, 4, 8, 16] #nelement_list = Int[100] - nelement_list = Int[8] + #nelement_list = Int[8] + #nelement_list = Int[4] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_H_err = Array{mk_float,1}(undef,nscan) @@ -511,6 +521,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_parallelism=test_parallelism, test_self_operator=test_self_operator, test_dense_construction=test_dense_construction, + use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, standalone=false) max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index a77a6b1cc..384d83f70 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -40,7 +40,8 @@ using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_paralle using ..fokker_planck_calculus: calculate_YY_arrays using ..fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data! using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, ravel_c_to_vpavperp_parallel! -using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes +using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes, H_Maxwellian, dGdvperp_Maxwellian +using ..fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian, dHdvpa_Maxwellian, dHdvperp_Maxwellian """ allocate the required ancilliary arrays """ @@ -218,7 +219,8 @@ Function for evaluating Css' = Css'[Fs,Fs'] function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp, fkpl_arrays::fokkerplanck_weakform_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral; - test_assembly_serial=false,impose_zero_gradient_BC=false) + test_assembly_serial=false,impose_zero_gradient_BC=false, + use_Maxwellian_Rosenbluth_coefficients=false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @@ -260,48 +262,67 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp d2Gdvpa2 = fkpl_arrays.d2Gdvpa2 d2Gdvperpdvpa = fkpl_arrays.d2Gdvperpdvpa - # the functions within this loop will call - # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:,]),vpa,vperp,vpa_spectral,vperp_spectral) - # carry out the elliptic solves required - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] - end - elliptic_solve!(HH,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - - end - #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + if use_Maxwellian_Rosenbluth_coefficients + begin_serial_region() + dens = get_density(ffsp_in,vpa,vperp) + upar = get_upar(ffsp_in, vpa, vperp, dens) + ppar = get_ppar(ffsp_in, vpa, vperp, upar) + pperp = get_pperp(ffsp_in, vpa, vperp) + pressure = get_pressure(ppar,pperp) + vth = sqrt(2.0*pressure/dens) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + HH[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + else + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # calculate the boundary data + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:,]),vpa,vperp,vpa_spectral,vperp_spectral) + # carry out the elliptic solves required + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] + end + elliptic_solve!(HH,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + + end + #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + end + # use the elliptic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) end - # use the elliptic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - # assemble the RHS of the collision operator matrix eq if test_assembly_serial assemble_explicit_collision_operator_rhs_serial!(rhsc,@view(ffs_in[:,:,]), diff --git a/src/fokker_planck_test.jl b/src/fokker_planck_test.jl index d828002dd..186a9dcbb 100644 --- a/src/fokker_planck_test.jl +++ b/src/fokker_planck_test.jl @@ -6,8 +6,8 @@ the Full-F Fokker-Planck Collision Operator module fokker_planck_test export Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -export d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -export dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +export d2Gdvpa2_Maxwellian, dGdvperp_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian +export dHdvpa_Maxwellian, dHdvperp_Maxwellian, Cssp_Maxwellian_inputs export F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian export d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian export H_Maxwellian, G_Maxwellian @@ -85,7 +85,7 @@ function eta_func(upar::mk_float,vth::mk_float, return speed end -function d2Gdvpa2(dens::mk_float,upar::mk_float,vth::mk_float, +function d2Gdvpa2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = dGdeta(eta) + ddGddeta(eta)*((vpa.grid[ivpa] - upar)^2)/(vth^2) @@ -93,7 +93,7 @@ function d2Gdvpa2(dens::mk_float,upar::mk_float,vth::mk_float, return d2Gdvpa2_fac end -function d2Gdvperpdvpa(dens::mk_float,upar::mk_float,vth::mk_float, +function d2Gdvperpdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = ddGddeta(eta)*vperp.grid[ivperp]*(vpa.grid[ivpa] - upar)/(vth^2) @@ -101,7 +101,7 @@ function d2Gdvperpdvpa(dens::mk_float,upar::mk_float,vth::mk_float, return d2Gdvperpdvpa_fac end -function d2Gdvperp2(dens::mk_float,upar::mk_float,vth::mk_float, +function d2Gdvperp2_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = dGdeta(eta) + ddGddeta(eta)*(vperp.grid[ivperp]^2)/(vth^2) @@ -109,21 +109,21 @@ function d2Gdvperp2(dens::mk_float,upar::mk_float,vth::mk_float, return d2Gdvperp2_fac end -function dGdvperp(dens::mk_float,upar::mk_float,vth::mk_float, +function dGdvperp_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = dGdeta(eta)*vperp.grid[ivperp]*dens/(vth*eta) return fac end -function dHdvperp(dens::mk_float,upar::mk_float,vth::mk_float, +function dHdvperp_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = dHdeta(eta)*vperp.grid[ivperp]*dens/(eta*vth^3) return fac end -function dHdvpa(dens::mk_float,upar::mk_float,vth::mk_float, +function dHdvpa_Maxwellian(dens::mk_float,upar::mk_float,vth::mk_float, vpa,vperp,ivpa,ivperp) eta = eta_func(upar,vth,vpa,vperp,ivpa,ivperp) fac = dHdeta(eta)*(vpa.grid[ivpa]-upar)*dens/(eta*vth^3) @@ -183,12 +183,12 @@ function Cssp_Maxwellian_inputs(denss::mk_float,upars::mk_float,vths::mk_float,m dFsdvpa = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) Fs = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Gspdvpa2 = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gspdvperp2 = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gspdvperpdvpa = d2Gdvperpdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dGspdvperp = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHspdvperp = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHspdvpa = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvpa2 = d2Gdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperp2 = d2Gdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + d2Gspdvperpdvpa = d2Gdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dGspdvperp = dGdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvperp = dHdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) + dHspdvpa = dHdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) Fsp = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) ( Cssp_Maxwellian = From 2be57b84209893154d06ae23e1f5dc686fff966a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 7 Nov 2023 19:56:20 +0000 Subject: [PATCH 214/331] Added interactive features to GaussLobattoLegendre_test.jl so that ngrid, nelement, and L_in (the domain size in vperp, half the domain size in vpa) may be specified with optional arguments in an interactive session. --- GaussLobattoLegendre_test.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index b6a76c34b..38e32de45 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -36,11 +36,11 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("\n") end - function gausslegendre_test() + function gausslegendre_test(; ngrid=17, nelement=4, L_in=6.0) # elemental grid tests - ngrid = 17 - nelement = 4 + #ngrid = 17 + #nelement = 4 y_ngrid = ngrid #number of points per element y_nelement_local = nelement # number of elements per rank y_nelement_global = y_nelement_local # total number of elements @@ -61,9 +61,9 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("$y_name test") println("") if y_name == "vperp" - y_L = 6.0 #physical box size in reference units + y_L = L_in #physical box size in reference units else - y_L = 12.0 + y_L = 2*L_in end y_input = grid_input(y_name, y_ngrid, y_nelement_global, y_nelement_local, nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) From 587e435666ad04468cdf3015d08df965dd8a31be Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 8 Nov 2023 14:14:21 +0000 Subject: [PATCH 215/331] Added test of the weak method for a Maxwellian input field particle distribution where the first derivatives and the function are replaced by the analytical test functions. Only a small difference in the numerical error is observed compare to the full numerical differentiation, suggesting that the dominant numerical error comes from the numerical differentiation coming from treating the divergence of the fluxes (which here is still done numerically with the weak method). --- 2D_FEM_assembly_test.jl | 14 ++++++-- src/fokker_planck.jl | 42 ++++++++++++++++++---- src/fokker_planck_calculus.jl | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 8266ed7c3..08da1a7ca 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -138,7 +138,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary Lvpa=12.0,Lvperp=6.0,plot_test_output=false,impose_zero_gradient_BC=false, test_parallelism=false,test_self_operator=true, test_dense_construction=false,standalone=false, - use_Maxwellian_Rosenbluth_coefficients=false) + use_Maxwellian_Rosenbluth_coefficients=false, + use_Maxwellian_field_particle_distribution=false) # define inputs needed for the test #plot_test_output = false#true #impose_zero_gradient_BC = false#true @@ -342,7 +343,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary vperp, vpa, vperp_spectral, vpa_spectral, test_assembly_serial=test_parallelism, impose_zero_gradient_BC=impose_zero_gradient_BC, - use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients) + use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, + use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution) # extract C[Fs,Fs'] result # and Rosenbluth potentials for testing begin_vperp_vpa_region() @@ -452,7 +454,12 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary end end - function run_assembly_test(; ngrid=5, nelement_list = [8], impose_zero_gradient_BC= false, plot_scan=true, plot_test_output = false, use_Maxwellian_Rosenbluth_coefficients=false) + function run_assembly_test(; ngrid=5, nelement_list = [8], + impose_zero_gradient_BC= false, + plot_scan=true, + plot_test_output = false, + use_Maxwellian_Rosenbluth_coefficients=false, + use_Maxwellian_field_particle_distribution=false) initialize_comms!() #ngrid = 5 #plot_scan = true @@ -522,6 +529,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_self_operator=test_self_operator, test_dense_construction=test_dense_construction, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, + use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, standalone=false) max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 384d83f70..c8d1867e0 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -37,11 +37,13 @@ using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vper using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial! using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel! +using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! using ..fokker_planck_calculus: calculate_YY_arrays using ..fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data! using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, ravel_c_to_vpavperp_parallel! using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes, H_Maxwellian, dGdvperp_Maxwellian using ..fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian, dHdvpa_Maxwellian, dHdvperp_Maxwellian +using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian """ allocate the required ancilliary arrays """ @@ -144,12 +146,17 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + FF = allocate_shared_float(nvpa,nvperp) + dFdvpa = allocate_shared_float(nvpa,nvperp) + dFdvperp = allocate_shared_float(nvpa,nvperp) + fka = fokkerplanck_weakform_arrays_struct(bwgt,rpbd,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, LP2D_sparse,LV2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, PPpar2D_sparse,MMparMNperp2D_sparse,MM2DZG_sparse, lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV, YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc, - CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa) + CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa, + FF, dFdvpa, dFdvperp) return fka end @@ -220,7 +227,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp fkpl_arrays::fokkerplanck_weakform_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral; test_assembly_serial=false,impose_zero_gradient_BC=false, - use_Maxwellian_Rosenbluth_coefficients=false) + use_Maxwellian_Rosenbluth_coefficients=false, + use_Maxwellian_field_particle_distribution=false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @@ -261,6 +269,9 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp d2Gdvperp2 = fkpl_arrays.d2Gdvperp2 d2Gdvpa2 = fkpl_arrays.d2Gdvpa2 d2Gdvperpdvpa = fkpl_arrays.d2Gdvperpdvpa + FF = fkpl_arrays.FF + dFdvpa = fkpl_arrays.dFdvpa + dFdvperp = fkpl_arrays.dFdvperp if use_Maxwellian_Rosenbluth_coefficients begin_serial_region() @@ -284,7 +295,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp # the functions within this loop will call # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:,]),vpa,vperp,vpa_spectral,vperp_spectral) + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) # carry out the elliptic solves required begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -324,13 +335,32 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp rhsc,rhqc,sc,qc,vpa,vperp) end # assemble the RHS of the collision operator matrix eq - if test_assembly_serial - assemble_explicit_collision_operator_rhs_serial!(rhsc,@view(ffs_in[:,:,]), + if use_Maxwellian_field_particle_distribution + begin_serial_region() + dens = get_density(ffs_in,vpa,vperp) + upar = get_upar(ffs_in, vpa, vperp, dens) + ppar = get_ppar(ffs_in, vpa, vperp, upar) + pperp = get_pperp(ffs_in, vpa, vperp) + pressure = get_pressure(ppar,pperp) + vth = sqrt(2.0*pressure/dens) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + FF[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dFdvpa[ivpa,ivperp] = dFdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dFdvperp[ivpa,ivperp] = dFdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + assemble_explicit_collision_operator_rhs_parallel_analytical_inputs!(rhsc,rhsvpavperp, + FF,dFdvpa,dFdvperp, + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2, + dHdvpa,dHdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays) + elseif test_assembly_serial + assemble_explicit_collision_operator_rhs_serial!(rhsc,@view(ffs_in[:,:]), d2Gdvpa,d2Gdvperpdvpa,d2Gdvperp2, dHdvpa,dHdvperp,ms,msp,nussp, vpa,vperp,YY_arrays) else - assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,@view(ffs_in[:,:,]), + assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,@view(ffs_in[:,:]), d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2, dHdvpa,dHdvperp,ms,msp,nussp, vpa,vperp,YY_arrays) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index bac0becc9..b44257521 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -13,6 +13,7 @@ export assemble_matrix_operators_dirichlet_bc_sparse export assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse export assemble_explicit_collision_operator_rhs_serial! export assemble_explicit_collision_operator_rhs_parallel! +export assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! export YY_collision_operator_arrays, calculate_YY_arrays export calculate_rosenbluth_potential_boundary_data! export elliptic_solve! @@ -204,6 +205,9 @@ struct fokkerplanck_weakform_arrays_struct{N} d2Gdvperp2::MPISharedArray{mk_float,2} d2Gdvpa2::MPISharedArray{mk_float,2} d2Gdvperpdvpa::MPISharedArray{mk_float,2} + FF::MPISharedArray{mk_float,2} + dFdvpa::MPISharedArray{mk_float,2} + dFdvperp::MPISharedArray{mk_float,2} end function allocate_boundary_integration_weight(vpa,vperp) @@ -2017,6 +2021,68 @@ function assemble_explicit_collision_operator_rhs_parallel!(rhsc,rhsvpavperp,pdf return nothing end +function assemble_explicit_collision_operator_rhs_parallel_analytical_inputs!(rhsc,rhsvpavperp,pdfs,dpdfsdvpa,dpdfsdvperp,d2Gspdvpa2,d2Gspdvperpdvpa, + d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, + vpa,vperp,YY_arrays::YY_collision_operator_arrays) + # assemble RHS of collision operator + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + rhsvpavperp[ivpa,ivperp] = 0.0 + end + + # loop over collocation points to benefit from shared-memory parallelism + ngrid_vpa, ngrid_vperp = vpa.ngrid, vperp.ngrid + @loop_vperp_vpa ivperp_global ivpa_global begin + igrid_vpa, ielement_vpax, ielement_vpa_low, ielement_vpa_hi, igrid_vperp, ielement_vperpx, ielement_vperp_low, ielement_vperp_hi = get_element_limit_indices(ivpa_global,ivperp_global,vpa,vperp) + # loop over elements belonging to this collocation point + for ielement_vperp in ielement_vperp_low:ielement_vperp_hi + # correct local ivperp in the case that we on a boundary point + ivperp_local = igrid_vperp + (ielement_vperp - ielement_vperp_low)*(1-ngrid_vperp) + @views YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + @views YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + @views YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + @views YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + + for ielement_vpa in ielement_vpa_low:ielement_vpa_hi + # correct local ivpa in the case that we on a boundary point + ivpa_local = igrid_vpa + (ielement_vpa - ielement_vpa_low)*(1-ngrid_vpa) + @views YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + @views YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + @views YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + @views YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsvpavperp[ivpa_global,ivperp_global] += -nussp*(YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*dpdfsdvpa[jvpap,jvperpp]*d2Gspdvpa2[kvpap,kvperpp] + + YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*dpdfsdvperp[jvpap,jvperpp]*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfs[jvpap,jvperpp]*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*dpdfsdvpa[jvpap,jvperpp]*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*dpdfsdvperp[jvpap,jvperpp]*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfs[jvpap,jvperpp]*dHspdvperp[kvpap,kvperpp]) + end + end + end + end + end + end + end + # ravel to compound index + #begin_serial_region() + #ravel_vpavperp_to_c!(rhsc,rhsvpavperp,vpa.n,vperp.n) + ravel_vpavperp_to_c_parallel!(rhsc,rhsvpavperp,vpa.n) + return nothing +end + # Elliptic solve function. # field: the solution From 7b539cab2c695c84d643dad2243c10cbabb3b000 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 8 Nov 2023 16:06:43 +0000 Subject: [PATCH 216/331] Add "optimised" Fokker-Planck routine which differentiates F-F_Maxwellian, so that C[F,F] is implemented with C[F,F]=C[F-F_M,F-F_M] + C[F-F_M,F_M] + C[F_M,F-F_M]. An order unity difference in the number of steps that can be taken before reducing entropy behaviour is observed. --- src/fokker_planck.jl | 221 +++++++++++++++++++++++++++++++------------ src/time_advance.jl | 19 +++- 2 files changed, 174 insertions(+), 66 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index c8d1867e0..aa2597549 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -13,6 +13,7 @@ export get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing export symmetric_matrix_inverse export fokker_planck_collision_operator_weak_form! +export explicit_fokker_planck_collisions_weak_form_opt! using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature @@ -166,7 +167,7 @@ Function for advancing with the explicit, weak-form, self-collision operator function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,composition,collisions,dt, fkpl_arrays::fokkerplanck_weakform_arrays_struct, - r, z, vperp, vpa, vperp_spectral, vpa_spectral; + r, z, vperp, vpa, vperp_spectral, vpa_spectral, scratch_dummy; test_assembly_serial=false,impose_zero_gradient_BC=false, diagnose_entropy_production=false) # N.B. only self-collisions are currently supported @@ -189,7 +190,7 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos # masses and collision frequencies ms, msp = 1.0, 1.0 # generalise! nussp = collisions.nuii # generalise! - + Css = scratch_dummy.buffer_vpavperp_1 # N.B. parallelisation is only over vpa vperp # ensure s, r, z are local before initiating the s, r, z loop begin_vperp_vpa_region() @@ -200,9 +201,100 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos @views fokker_planck_collision_operator_weak_form!(pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,is],ms,msp,nussp, fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) # advance this part of s,r,z with the resulting C[Fs,Fs] - Css = fkpl_arrays.CC begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin + Css[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + pdf_out[ivpa,ivperp,iz,ir,is] += dt*Css[ivpa,ivperp] + end + if diagnose_entropy_production + # assign dummy array + lnfC = fkpl_arrays.rhsvpavperp + @loop_vperp_vpa ivperp ivpa begin + lnfC[ivpa,ivperp] = log(abs(pdf_in[ivpa,ivperp,iz,ir,is]) + 1.0e-15)*Css[ivpa,ivperp] + end + begin_serial_region() + @serial_region begin + dSdt[iz,ir,is] = -get_density(lnfC,vpa,vperp) + end + end + end + return nothing +end + +""" +Function for advancing with the explicit, weak-form, self-collision operator +using the optimized method where we only differentiate F - F_Maxwellian +""" + +function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,composition,collisions,dt, + fkpl_arrays::fokkerplanck_weakform_arrays_struct, + r, z, vperp, vpa, vperp_spectral, vpa_spectral, scratch_dummy; + test_assembly_serial=false,impose_zero_gradient_BC=false, + diagnose_entropy_production=false) + # N.B. only self-collisions are currently supported + # This can be modified by adding a loop over s' below + n_ion_species = composition.n_ion_species + @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) + @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) + @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) + @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) + @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) + @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) + @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) + @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) + @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) + @boundscheck z.n == size(dSdt,1) || throw(BoundsError(dSdt)) + @boundscheck r.n == size(dSdt,2) || throw(BoundsError(dSdt)) + @boundscheck n_ion_species == size(dSdt,3) || throw(BoundsError(dSdt)) + + # masses and collision frequencies + ms, msp = 1.0, 1.0 # generalise! + nussp = collisions.nuii # generalise! + Css = scratch_dummy.buffer_vpavperp_1 + delta_Fs = scratch_dummy.buffer_vpavperp_2 + Fs_M = scratch_dummy.buffer_vpavperp_3 + # N.B. parallelisation is only over vpa vperp + # ensure s, r, z are local before initiating the s, r, z loop + begin_vperp_vpa_region() + @loop_s_r_z is ir iz begin + dens = get_density(@view(pdf_in[:,:,iz,ir,is]),vpa,vperp) + upar = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, dens) + ppar = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar) + pperp = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) + pressure = get_pressure(ppar,pperp) + vth = sqrt(2.0*pressure/dens) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Fs_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + delta_Fs[ivpa,ivperp] = pdf_in[ivpa,ivperp,iz,ir,is] - Fs_M[ivpa,ivperp] + end + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # first argument is Fs, and second argument is Fs' in C[Fs,Fs'] + fokker_planck_collision_operator_weak_form!(delta_Fs,delta_Fs,ms,msp,nussp, + fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Css[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + end + fokker_planck_collision_operator_weak_form!(@view(pdf_in[:,:,iz,ir,is]),delta_Fs,ms,msp,nussp, + fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, + use_Maxwellian_Rosenbluth_coefficients=false, + use_Maxwellian_field_particle_distribution=true, + skip_Rosenbluth_potential_calculation=true) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Css[ivpa,ivperp] += fkpl_arrays.CC[ivpa,ivperp] + end + fokker_planck_collision_operator_weak_form!(delta_Fs,@view(pdf_in[:,:,iz,ir,is]),ms,msp,nussp, + fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, + use_Maxwellian_Rosenbluth_coefficients=true, + use_Maxwellian_field_particle_distribution=false) + # advance this part of s,r,z with the resulting C[Fs,Fs] + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + Css[ivpa,ivperp] += fkpl_arrays.CC[ivpa,ivperp] pdf_out[ivpa,ivperp,iz,ir,is] += dt*Css[ivpa,ivperp] end if diagnose_entropy_production @@ -228,7 +320,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp vperp, vpa, vperp_spectral, vpa_spectral; test_assembly_serial=false,impose_zero_gradient_BC=false, use_Maxwellian_Rosenbluth_coefficients=false, - use_Maxwellian_field_particle_distribution=false) + use_Maxwellian_field_particle_distribution=false, + skip_Rosenbluth_potential_calculation=false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @@ -273,66 +366,68 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp dFdvpa = fkpl_arrays.dFdvpa dFdvperp = fkpl_arrays.dFdvperp - if use_Maxwellian_Rosenbluth_coefficients - begin_serial_region() - dens = get_density(ffsp_in,vpa,vperp) - upar = get_upar(ffsp_in, vpa, vperp, dens) - ppar = get_ppar(ffsp_in, vpa, vperp, upar) - pperp = get_pperp(ffsp_in, vpa, vperp) - pressure = get_pressure(ppar,pperp) - vth = sqrt(2.0*pressure/dens) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - HH[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - end - else - # the functions within this loop will call - # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) - # carry out the elliptic solves required - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] - end - elliptic_solve!(HH,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - - end - #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + if !skip_Rosenbluth_potential_calculation + if use_Maxwellian_Rosenbluth_coefficients + begin_serial_region() + dens = get_density(ffsp_in,vpa,vperp) + upar = get_upar(ffsp_in, vpa, vperp, dens) + ppar = get_ppar(ffsp_in, vpa, vperp, upar) + pperp = get_pperp(ffsp_in, vpa, vperp) + pressure = get_pressure(ppar,pperp) + vth = sqrt(2.0*pressure/dens) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + HH[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + else + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # calculate the boundary data + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) + # carry out the elliptic solves required + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] + end + elliptic_solve!(HH,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + + end + #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + end + # use the elliptic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) end - # use the elliptic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) end # assemble the RHS of the collision operator matrix eq if use_Maxwellian_field_particle_distribution diff --git a/src/time_advance.jl b/src/time_advance.jl index 2c014c3a6..30ebb0ae7 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -55,6 +55,7 @@ using ..energy_equation: energy_equation!, neutral_energy_equation! using ..em_fields: setup_em_fields, update_phi! using ..fokker_planck: init_fokker_planck_collisions_weak_form, init_fokker_planck_collisions, explicit_fokker_planck_collisions! using ..fokker_planck: explicit_fokker_planck_collisions_weak_form!, explicit_fokker_planck_collisions_Maxwellian_coefficients! +using ..fokker_planck: explicit_fokker_planck_collisions_weak_form_opt! using ..manufactured_solns: manufactured_sources using ..advection: advection_info @debug_detect_redundant_block_synchronize using ..communication: debug_detect_redundant_is_active @@ -148,6 +149,10 @@ struct scratch_dummy_arrays # needs to be shared memory buffer_vzvrvzetazrsn_1::MPISharedArray{mk_float,6} buffer_vzvrvzetazrsn_2::MPISharedArray{mk_float,6} + + buffer_vpavperp_1::MPISharedArray{mk_float,2} + buffer_vpavperp_2::MPISharedArray{mk_float,2} + buffer_vpavperp_3::MPISharedArray{mk_float,2} end @@ -673,7 +678,11 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vzvrvzetazrsn_1 = allocate_shared_float(nvz,nvr,nvzeta,nz,nr,nspecies_neutral) buffer_vzvrvzetazrsn_2 = allocate_shared_float(nvz,nvr,nvzeta,nz,nr,nspecies_neutral) - + + buffer_vpavperp_1 = allocate_shared_float(nvpa,nvperp) + buffer_vpavperp_2 = allocate_shared_float(nvpa,nvperp) + buffer_vpavperp_3 = allocate_shared_float(nvpa,nvperp) + return scratch_dummy_arrays(dummy_s,dummy_sr,dummy_vpavperp,dummy_zrs,dummy_zrsn, buffer_z_1,buffer_z_2,buffer_z_3,buffer_z_4, buffer_r_1,buffer_r_2,buffer_r_3,buffer_r_4, @@ -687,7 +696,8 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5,buffer_vpavperpzrs_6, buffer_vzvrvzetazsn_1,buffer_vzvrvzetazsn_2,buffer_vzvrvzetazsn_3,buffer_vzvrvzetazsn_4,buffer_vzvrvzetazsn_5,buffer_vzvrvzetazsn_6, buffer_vzvrvzetarsn_1,buffer_vzvrvzetarsn_2,buffer_vzvrvzetarsn_3,buffer_vzvrvzetarsn_4,buffer_vzvrvzetarsn_5,buffer_vzvrvzetarsn_6, - buffer_vzvrvzetazrsn_1, buffer_vzvrvzetazrsn_2) + buffer_vzvrvzetazrsn_1, buffer_vzvrvzetazrsn_2, + buffer_vpavperp_1,buffer_vpavperp_2,buffer_vpavperp_3) end @@ -1807,8 +1817,11 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, 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, - fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral, + fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, diagnose_entropy_production = update_entropy_diagnostic) + #explicit_fokker_planck_collisions_weak_form_opt!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition,collisions,dt, + # fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, + # diagnose_entropy_production = update_entropy_diagnostic) end if advance.explicit_fp_F_FM_collisions explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, From 766ea87093bffc2c321d988d4e2a1f6e133fd594 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 9 Nov 2023 10:53:10 +0000 Subject: [PATCH 217/331] Included ad-hoc numerical conserving terms in "optimized" weak-form Fokker-Planck operator to see if this improves H-theorem properties. --- src/fokker_planck.jl | 52 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index aa2597549..c131e35d8 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -254,6 +254,7 @@ function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,co Css = scratch_dummy.buffer_vpavperp_1 delta_Fs = scratch_dummy.buffer_vpavperp_2 Fs_M = scratch_dummy.buffer_vpavperp_3 + dummy_vpavperp = scratch_dummy.dummy_vpavperp # N.B. parallelisation is only over vpa vperp # ensure s, r, z are local before initiating the s, r, z loop begin_vperp_vpa_region() @@ -273,13 +274,15 @@ function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,co # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays # first argument is Fs, and second argument is Fs' in C[Fs,Fs'] fokker_planck_collision_operator_weak_form!(delta_Fs,delta_Fs,ms,msp,nussp, - fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) + fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, + impose_zero_gradient_BC=true) begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin Css[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] end fokker_planck_collision_operator_weak_form!(@view(pdf_in[:,:,iz,ir,is]),delta_Fs,ms,msp,nussp, fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, + impose_zero_gradient_BC=true, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=true, skip_Rosenbluth_potential_calculation=true) @@ -289,6 +292,7 @@ function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,co end fokker_planck_collision_operator_weak_form!(delta_Fs,@view(pdf_in[:,:,iz,ir,is]),ms,msp,nussp, fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, + impose_zero_gradient_BC=true, use_Maxwellian_Rosenbluth_coefficients=true, use_Maxwellian_field_particle_distribution=false) # advance this part of s,r,z with the resulting C[Fs,Fs] @@ -308,6 +312,52 @@ function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,co dSdt[iz,ir,is] = -get_density(lnfC,vpa,vperp) end end + if collisions.numerical_conserving_terms == "density+u||+T" + # use an ad-hoc numerical model to conserve density, upar, vth + # a different model is required for inter-species collisions + # simply conserving particle density may be more appropriate in the multi-species case + begin_serial_region() + n_in = dens + upar_in = upar + pressure_in = pressure + + n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + upar_out = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, n_out) + ppar_out = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out) + pperp_out = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + pressure_out = get_pressure(ppar_out,pperp_out) + qpar_out = get_qpar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, dummy_vpavperp) + rmom_out = get_rmom(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, dummy_vpavperp) + + # form the appropriate matrix coefficients + b0, b1, b2 = n_in, n_in*(upar_in - upar_out), (3.0)*(pressure_in) + n_in*(upar_in - upar_out)^2 + A00, A02, A11, A12, A22 = n_out, (3.0)*(pressure_out), ppar_out, qpar_out, rmom_out + + # obtain the coefficients for the corrections + (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + + # fill with the corrected pdf + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa] - upar_out + pdf_out[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp,iz,ir,is] + end + elseif collisions.numerical_conserving_terms == "density" + # get density moment of incoming and outgoing distribution functions + n_in = dens + + n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) + + # obtain the coefficient for the corrections + x0 = n_in/n_out + + # update pdf_out with the corrections + @loop_vperp_vpa ivperp ivpa begin + pdf_out[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp,iz,ir,is] + end + elseif !(collisions.numerical_conserving_terms == "none") + println("ERROR: collisions.numerical_conserving_terms = ",collisions.numerical_conserving_terms," NOT SUPPORTED") + end + end return nothing end From e6a00dc3b0cc3ce0885c911f789dba862ed1afdc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 10 Nov 2023 08:48:21 +0000 Subject: [PATCH 218/331] Fix error plotting by using a dummy array instead of the *_err arrays to do the error calculations. --- 2D_FEM_assembly_test.jl | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 08da1a7ca..35a08cadc 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -79,15 +79,15 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary return max_err end - function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp) + function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy) @. func_err = abs(func_num - func_exact) max_err = maximum(func_err) - @. func_err = func_err^2 + @. dummy = func_err^2 # compute the numerator - num = get_density(func_err,vpa,vperp) + num = get_density(dummy,vpa,vperp) # compute the denominator - @. func_err = 1.0 - denom = get_density(func_err,vpa,vperp) + @. dummy = 1.0 + denom = get_density(dummy,vpa,vperp) L2norm = sqrt(num/denom) println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) return max_err, L2norm @@ -380,15 +380,16 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary if !use_Maxwellian_Rosenbluth_coefficients test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) end - fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp) - fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp) - fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp) - fkerr.G_M.max, fkerr.G_M.L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp) - fkerr.d2Gdvpa2_M.max, fkerr.d2Gdvpa2_M.L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp) - fkerr.dGdvperp_M.max, fkerr.dGdvperp_M.L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp) - fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp) - fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp) - fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp) + dummy_array = Array{mk_float,2}(undef,vpa.n,vperp.n) + fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array) + fkerr.dHdvpa_M.max, fkerr.dHdvpa_M.L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array) + fkerr.dHdvperp_M.max, fkerr.dHdvperp_M.L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp,dummy_array) + fkerr.G_M.max, fkerr.G_M.L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp,dummy_array) + fkerr.d2Gdvpa2_M.max, fkerr.d2Gdvpa2_M.L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp,dummy_array) + fkerr.dGdvperp_M.max, fkerr.dGdvperp_M.L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array) + fkerr.d2Gdvperpdvpa_M.max, fkerr.d2Gdvperpdvpa_M.L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array) + fkerr.d2Gdvperp2_M.max, fkerr.d2Gdvperp2_M.L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array) + fkerr.C_M.max, fkerr.C_M.L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp,dummy_array) # calculate the entropy production lnfC = fkpl_arrays.rhsvpavperp From f09aca89f5e774d8041e8df0af4516a0b87cdd93 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 10 Nov 2023 20:57:09 +0000 Subject: [PATCH 219/331] Make Lvpa and Lvperp interactive arguments of "run_assembly_test()". --- 2D_FEM_assembly_test.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 35a08cadc..a59232655 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -460,7 +460,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary plot_scan=true, plot_test_output = false, use_Maxwellian_Rosenbluth_coefficients=false, - use_Maxwellian_field_particle_distribution=false) + use_Maxwellian_field_particle_distribution=false, + Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 #plot_scan = true @@ -531,7 +532,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_dense_construction=test_dense_construction, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, - standalone=false) + standalone=false, Lvpa=Lvpa, Lvperp=Lvperp) max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 max_dHdvpa_err[iscan], L2_dHdvpa_err[iscan] = fkerr.dHdvpa_M.max ,fkerr.dHdvpa_M.L2 From 1360367dbb71a3c71dc4fefccdad1c744748cde7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sat, 11 Nov 2023 08:35:21 +0000 Subject: [PATCH 220/331] Changes suggested by @johnomotani to remove boundary conditions from mass matrix inversion when carrying out differentiation, and including explicit boundary terms. Here this is handled with explicit boundary terms in the test script GaussLobattoLegendre_test.jl for 1D differentiation operations only using assembled matrices (derivative functions not yet supported). Subject to testing, these changes will be implemented across the gauss_legendre.jl and fokker_planck*.jl modules. --- GaussLobattoLegendre_test.jl | 26 ++++++++++++++++---------- src/gauss_legendre.jl | 8 ++++---- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index 38e32de45..a1baf23a9 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -147,14 +147,18 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) if y.name == "vpa" mul!(b,y_spectral.S_matrix,f_exact) + b[1] -= f_exact[1] + b[y.n] += f_exact[y.n] gausslegendre_mass_matrix_solve!(df_num,b,y.name,y_spectral) @. df_err = df_num - df_exact #println("df_num (weak form): ",df_num) #println("df_exact (weak form): ",df_exact) println("max(df_err) (weak form): ",maximum(df_err)) - second_derivative!(d2f_num, f_exact, y, y_spectral) - #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + #second_derivative!(d2f_num, f_exact, y, y_spectral) + mul!(b,y_spectral.K_matrix,f_exact) + b[1] -= sum(y_spectral.lobatto.Dmat[1,:].*f_exact[1:y.ngrid])/y.element_scale[1] + b[y.n] += sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -165,8 +169,8 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv elseif y.name == "vperp" #println("condition: ",cond(y_spectral.mass_matrix)) - b = Array{Float64,1}(undef,y.n) mul!(b,y_spectral.S_matrix,g_exact) + b[y.n] += y.grid[y.n]*g_exact[y.n] gausslegendre_mass_matrix_solve!(divg_num,b,y.name,y_spectral) @. divg_err = abs(divg_num - divg_exact) #println("divg_b (weak form): ",b) @@ -174,9 +178,10 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv #println("divg_exact (weak form): ",divg_exact) println("max(divg_err) (weak form): ",maximum(divg_err)) - second_derivative!(d2f_num, f_exact, y, y_spectral) - #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + #second_derivative!(d2f_num, f_exact, y, y_spectral) + mul!(b,y_spectral.K_matrix,f_exact) + b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -186,9 +191,10 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv outfile = "vperp_second_derivative_test.pdf" savefig(outfile) - #mul!(b,y_spectral.L_matrix,h_exact) - laplacian_derivative!(laph_num, h_exact, y, y_spectral) - #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) + mul!(b,y_spectral.L_matrix,h_exact) + b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*h_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + #laplacian_derivative!(laph_num, h_exact, y, y_spectral) + gausslegendre_mass_matrix_solve!(laph_num,b,y.name,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* #println(b[1:10]) #println(laph_num) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index d8f3beea7..93a973ae4 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -343,11 +343,11 @@ function gausslegendre_mass_matrix_solve!(f,b,coord_name,spectral) if coord_name == "vperp" # enforce zero (value or gradient) boundary conditions #b[1] = 0.0 # uncomment if bc is imposed at vperp = 0 in mass matrix - b[end] = 0.0 + #b[end] = 0.0 else # enforce zero (value or gradient) boundary conditions - b[1] = 0.0 - b[end] = 0.0 + #b[1] = 0.0 + #b[end] = 0.0 end # invert mass matrix system y = spectral.mass_matrix_lu \ b @@ -846,7 +846,7 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, imin = coord.imin imax = coord.imax @. QQ_global = 0.0 - mass_matrix = option == "M" + mass_matrix = (option == "M") && false if coord.name == "vperp" zero_bc_upper_boundary = true && mass_matrix zero_bc_lower_boundary = false && mass_matrix From 464b07a5f911a6a013f78cd648b9534b5255b87e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 13 Nov 2023 16:21:39 +0000 Subject: [PATCH 221/331] Series of interlinked changes that have the effect of significantly improving the numerical H-theorem property of the weak-form Fokker-Planck operator. The changes are experimental and do not support all features. The boundary terms introduced by integration by parts are now includede at the elemental-matrix level, and are switched on and off by an optional logical flag (default is to use the boundary terms, appropriate for differentiation. It was found that the boundary terms should NOT be used for the definitions of the Laplacian and modified Laplacian operators LP2D and LV2D. It is observed that the errors are larger for these solvers when the source terms use the boundary terms in numerical differentiation (requires further investigation). The mass matrix MM2D now does not contain any boundary condition enforcement. As such, boundary conditions on the collision operator are enforced via the function "enforce_vpavperp_BCs!" from fokker_planck_calculus.jl. Numerical conserving terms that conserve n, upar, and pressure are included. With the boundary-condition-free mass matrix and the numerical conserving terms, we see much more stable long time solutions. --- 2D_FEM_assembly_test.jl | 4 +- GaussLobattoLegendre_test.jl | 14 ++--- src/fokker_planck.jl | 72 ++++++++++++++++++++++++-- src/fokker_planck_calculus.jl | 97 +++++++++++++++++++++++++++++------ src/gauss_legendre.jl | 53 ++++++++++++++++++- 5 files changed, 209 insertions(+), 31 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index a59232655..456ee951a 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -239,8 +239,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary gc = lu_obj_MMZG \ dgc else # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=false) - enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=false) + #enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=false) + #enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=false) # invert mass matrix and fill fc fc = lu_obj_MM \ dfc gc = lu_obj_MM \ dgc diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index a1baf23a9..f7e972f45 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -147,8 +147,8 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) if y.name == "vpa" mul!(b,y_spectral.S_matrix,f_exact) - b[1] -= f_exact[1] - b[y.n] += f_exact[y.n] + #b[1] -= f_exact[1] + #b[y.n] += f_exact[y.n] gausslegendre_mass_matrix_solve!(df_num,b,y.name,y_spectral) @. df_err = df_num - df_exact #println("df_num (weak form): ",df_num) @@ -156,8 +156,8 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("max(df_err) (weak form): ",maximum(df_err)) #second_derivative!(d2f_num, f_exact, y, y_spectral) mul!(b,y_spectral.K_matrix,f_exact) - b[1] -= sum(y_spectral.lobatto.Dmat[1,:].*f_exact[1:y.ngrid])/y.element_scale[1] - b[y.n] += sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + #b[1] -= sum(y_spectral.lobatto.Dmat[1,:].*f_exact[1:y.ngrid])/y.element_scale[1] + #b[y.n] += sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) @@ -170,7 +170,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv elseif y.name == "vperp" #println("condition: ",cond(y_spectral.mass_matrix)) mul!(b,y_spectral.S_matrix,g_exact) - b[y.n] += y.grid[y.n]*g_exact[y.n] + #b[y.n] += y.grid[y.n]*g_exact[y.n] gausslegendre_mass_matrix_solve!(divg_num,b,y.name,y_spectral) @. divg_err = abs(divg_num - divg_exact) #println("divg_b (weak form): ",b) @@ -180,7 +180,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv #second_derivative!(d2f_num, f_exact, y, y_spectral) mul!(b,y_spectral.K_matrix,f_exact) - b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + #b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) @@ -192,7 +192,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv savefig(outfile) mul!(b,y_spectral.L_matrix,h_exact) - b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*h_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] + #b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*h_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] #laplacian_derivative!(laph_num, h_exact, y, y_spectral) gausslegendre_mass_matrix_solve!(laph_num,b,y.name,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index c131e35d8..e54a733d2 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -39,9 +39,9 @@ using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vper using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial! using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel! using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! -using ..fokker_planck_calculus: calculate_YY_arrays +using ..fokker_planck_calculus: calculate_YY_arrays, enforce_vpavperp_BCs! using ..fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data! -using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, ravel_c_to_vpavperp_parallel! +using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, algebraic_solve!, ravel_c_to_vpavperp_parallel! using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes, H_Maxwellian, dGdvperp_Maxwellian using ..fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian, dHdvpa_Maxwellian, dHdvperp_Maxwellian using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian @@ -200,6 +200,11 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos # first argument is Fs, and second argument is Fs' in C[Fs,Fs'] @views fokker_planck_collision_operator_weak_form!(pdf_in[:,:,iz,ir,is],pdf_in[:,:,iz,ir,is],ms,msp,nussp, fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral) + # enforce the boundary conditions on CC before it is used for timestepping + enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) + # make ad-hoc conserving corrections + conserving_corrections!(fkpl_arrays.CC,pdf_in[:,:,iz,ir,is],vpa,vperp,scratch_dummy) + # advance this part of s,r,z with the resulting C[Fs,Fs] begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -474,7 +479,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp # use the elliptic solve function to find # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp # using a weak form - elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, rhsc,rhqc,sc,qc,vpa,vperp) end @@ -518,7 +523,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp # invert mass matrix and fill fc sc .= lu_obj_MMZG \ rhsc else - enforce_zero_bc!(rhsc,vpa,vperp) + #enforce_zero_bc!(rhsc,vpa,vperp) # invert mass matrix and fill fc sc .= lu_obj_MM \ rhsc end @@ -850,6 +855,65 @@ function symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) return x0, x1, x2 end +# solves A x = b for a matrix of the form +# A00 A01 A02 +# A01 A11 A12 +# A02 A12 A22 +# appropriate for the moment numerical conserving terms +function symmetric_matrix_inverse(A00,A01,A02,A11,A12,A22,b0,b1,b2) + # matrix determinant + detA = A00*(A11*A22 - A12^2) - A01*(A01*A22 - A12*A02) + A02*(A01*A12 - A11*A02) + # cofactors C (also a symmetric matrix) + C00 = A11*A22 - A12^2 + C01 = A12*A02 - A01*A22 + C02 = A01*A12 -A11*A02 + C11 = A00*A22 - A02^2 + C12 = A01*A02 -A00*A12 + C22 = A00*A11 - A01^2 + x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA + x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA + x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA + #println("b0: ",b0," b1: ",b1," b2: ",b2) + #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) + #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) + #println("x0: ",x0," x1: ",x1," x2: ",x2) + return x0, x1, x2 +end + +function conserving_corrections!(CC,pdf_in,vpa,vperp,scratch_dummy) + # define a dummy array + dummy_vpavperp = scratch_dummy.dummy_vpavperp + # compute moments of the input pdf + dens = get_density(@view(pdf_in[:,:]), vpa, vperp) + upar = get_upar(@view(pdf_in[:,:,]), vpa, vperp, dens) + ppar = get_ppar(@view(pdf_in[:,:,]), vpa, vperp, upar) + pperp = get_pperp(@view(pdf_in[:,:,]), vpa, vperp) + pressure = get_pressure(ppar,pperp) + qpar = get_qpar(@view(pdf_in[:,:,]), vpa, vperp, upar, dummy_vpavperp) + rmom = get_rmom(@view(pdf_in[:,:,]), vpa, vperp, upar, dummy_vpavperp) + + # compute moments of the numerical collision operator + dn = get_density(CC, vpa, vperp) + du = get_upar(CC, vpa, vperp, 1.0) + dppar = get_ppar(CC, vpa, vperp, upar) + dpperp = get_pperp(CC, vpa, vperp) + dp = get_pressure(dppar,dpperp) + + # form the appropriate matrix coefficients + b0, b1, b2 = dn, du - upar*dn, 3.0*dp + A00, A02, A11, A12, A22 = dens, 3.0*pressure, ppar, qpar, rmom + + # obtain the coefficients for the corrections + (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + + # correct CC + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa] - upar + CC[ivpa,ivperp] -= (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_in[ivpa,ivperp] + end +end + # applies the numerical conservation to pdf_out, the advanced distribution function # uses the low-level moment integration routines from velocity moments # conserves n, upar, total pressure of each species diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index b44257521..c3ef6c526 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -16,9 +16,10 @@ export assemble_explicit_collision_operator_rhs_parallel! export assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! export YY_collision_operator_arrays, calculate_YY_arrays export calculate_rosenbluth_potential_boundary_data! -export elliptic_solve! +export elliptic_solve!, algebraic_solve! export fokkerplanck_arrays_struct export fokkerplanck_weakform_arrays_struct +export enforce_vpavperp_BCs! # testing export calculate_rosenbluth_potential_boundary_data_exact! @@ -1602,6 +1603,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + KKpar_no_BC_terms = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) KJperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) @@ -1621,7 +1623,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L_no_BC_terms") get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) @@ -1636,6 +1638,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp for ielement_vpa in 1:nelement_vpa get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(KKpar_no_BC_terms,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K_no_BC_terms") get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) @@ -1665,64 +1668,68 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp if lower_boundary_row_vpa if ivpap_local == 1 && ivperp_local == ivperpp_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) end elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp if ivperpp_local == 1 && ivpa_local == ivpap_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) else - assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) + #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) end else # assign mass matrix data #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) + #assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, + # (MMpar[ivpa_local,ivpap_local]* + # MMperp[ivperp_local,ivperpp_local])) assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, - (KKpar[ivpa_local,ivpap_local]* + (KKpar_no_BC_terms[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* LLperp[ivperp_local,ivperpp_local])) assemble_constructor_data!(LV2D,icsc,ic_global,icp_global, - (KKpar[ivpa_local,ivpap_local]* + (KKpar_no_BC_terms[ivpa_local,ivpap_local]* MRperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* (KJperp[ivperp_local,ivperpp_local] - PPperp[ivperp_local,ivperpp_local] - MNperp[ivperp_local,ivperpp_local]))) end - + #assign mass matrix + assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + # assign K matrices assemble_constructor_data!(KKpar2D,icsc,ic_global,icp_global, (KKpar[ivpa_local,ivpap_local]* @@ -2144,4 +2151,62 @@ function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_bounda return nothing end +# same as above but source is made of two different terms +# with different weak matrices +function algebraic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, + lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) + # get data into the compound index format + begin_vperp_vpa_region() + ravel_vpavperp_to_c_parallel!(sc_1,source_1,vpa.n) + ravel_vpavperp_to_c_parallel!(sc_2,source_2,vpa.n) + + # assemble the rhs of the weak system + begin_serial_region() + @serial_region begin + mul!(rhsc_1,matrix_rhs_1,sc_1) + mul!(rhsc_2,matrix_rhs_2,sc_2) + end + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + ic = ic_func(ivpa,ivperp,vpa.n) + rhsc_1[ic] += rhsc_2[ic] + end + begin_serial_region() + @serial_region begin + # solve the linear system + sc_1 .= lu_object_lhs \ rhsc_1 + end + # get data into the vpa vperp indices format + begin_vperp_vpa_region() + ravel_c_to_vpavperp_parallel!(field,sc_1,vpa.n) + return nothing +end + +""" +function to enforce boundary conditions on the collision operator +result to be consistent with the boundary conditions imposed on the the pdf +""" +function enforce_vpavperp_BCs!(pdf,vpa,vperp,vpa_spectral,vperp_spectral) + nvpa = vpa.n + nvperp = vperp.n + ngrid_vperp = vperp.ngrid + D0 = vperp_spectral.radau.D0 + # vpa boundary conditions + # zero at infinity + begin_vperp_region() + @loop_vperp ivperp begin + pdf[1,ivperp] = 0.0 + pdf[nvpa,ivperp] = 0.0 + end + # vperp boundary conditions + # zero boundary condition at infinity + # set regularity condition d F / d vperp = 0 at vperp = 0 + # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 + begin_vpa_region() + @loop_vpa ivpa begin + pdf[ivpa,nvperp] = 0.0 + pdf[ivpa,1,] = -sum(D0[2:ngrid_vperp].*pdf[ivpa,2:ngrid_vperp])/D0[1] + end +end + end diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 93a973ae4..6455c9b36 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -922,10 +922,14 @@ function get_QQ_local!(QQ::Array{mk_float,2},ielement, get_SS_local!(QQ,ielement,lobatto,radau,coord) elseif option == "K" get_KK_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "K_no_BC_terms" + get_KK_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=false) elseif option == "J" get_KJ_local!(QQ,ielement,lobatto,radau,coord) elseif option == "L" get_LL_local!(QQ,ielement,lobatto,radau,coord) + elseif option == "L_no_BC_terms" + get_LL_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=false) end return nothing end @@ -961,11 +965,22 @@ function get_SS_local!(QQ,ielement, # extra scale and shift factors required because of vperp in integral if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = shift_factor*lobatto.S0 + scale_factor*lobatto.S1 + # boundary terms from integration by parts + imin = coord.imin[ielement] - 1 + imax = coord.imax[ielement] + QQ[1,1] -= coord.grid[imin] + QQ[coord.ngrid,coord.ngrid] += coord.grid[imax] else # radau points @. QQ = shift_factor*radau.S0 + scale_factor*radau.S1 + # boundary terms from integration by parts + imax = coord.imax[ielement] + QQ[coord.ngrid,coord.ngrid] += coord.grid[imax] end else # assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.S0 + # boundary terms from integration by parts + QQ[1,1] -= 1.0 + QQ[coord.ngrid,coord.ngrid] += 1.0 end return nothing end @@ -973,7 +988,7 @@ end function get_KK_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, - coord) + coord;explicit_BC_terms=true) scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] @@ -982,11 +997,28 @@ function get_KK_local!(QQ,ielement, # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 - lobatto.P0 + # boundary terms from integration by parts + if explicit_BC_terms + imin = coord.imin[ielement] - 1 + imax = coord.imax[ielement] + @. QQ[1,:] -= coord.grid[imin]*lobatto.Dmat[1,:]/scale_factor + @. QQ[coord.ngrid,:] += coord.grid[imax]*lobatto.Dmat[coord.ngrid,:]/scale_factor + end else # radau points @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 - radau.P0 + # boundary terms from integration by parts + if explicit_BC_terms + imax = coord.imax[ielement] + @. QQ[coord.ngrid,:] += coord.grid[imax]*radau.Dmat[coord.ngrid,:]/scale_factor + end end else # assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.K0/scale_factor + # boundary terms from integration by parts + if explicit_BC_terms + @. QQ[1,:] -= lobatto.Dmat[1,:]/scale_factor + @. QQ[coord.ngrid,:] += lobatto.Dmat[coord.ngrid,:]/scale_factor + end end return nothing end @@ -1021,7 +1053,7 @@ end function get_LL_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, - coord) + coord;explicit_BC_terms=true) scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] @@ -1030,11 +1062,28 @@ function get_LL_local!(QQ,ielement, # (1/vperp) d ( vperp d (.) / d vperp) if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 + # boundary terms from integration by parts + if explicit_BC_terms + imin = coord.imin[ielement] - 1 + imax = coord.imax[ielement] + @. QQ[1,:] -= coord.grid[imin]*lobatto.Dmat[1,:]/scale_factor + @. QQ[coord.ngrid,:] += coord.grid[imax]*lobatto.Dmat[coord.ngrid,:]/scale_factor + end else # radau points @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 + # boundary terms from integration by parts + if explicit_BC_terms + imax = coord.imax[ielement] + @. QQ[coord.ngrid,:] += coord.grid[imax]*radau.Dmat[coord.ngrid,:]/scale_factor + end end else # d^2 (.) d vpa^2 -- assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.K0/scale_factor + # boundary terms from integration by parts + if explicit_BC_terms + @. QQ[1,:] -= lobatto.Dmat[1,:]/scale_factor + @. QQ[coord.ngrid,:] += lobatto.Dmat[coord.ngrid,:]/scale_factor + end end return nothing end From da69ebab7f34a324e18dfa17e98009d19383a58f Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 14 Nov 2023 15:00:55 +0000 Subject: [PATCH 222/331] Modify choices for whether or not explicit boundary terms are included by default. Now new 2D matrices are created with the explicit boundary terms, using the matrices without boundary terms in the collision operator. It is noted that using explicit boundary terms in the elliptic solvers for the Rosenbluth potentials reduces the accuracy of the calculation. --- 2D_FEM_assembly_test.jl | 8 ++++---- src/fokker_planck.jl | 5 +++-- src/fokker_planck_calculus.jl | 31 ++++++++++++++++++++++++------- src/gauss_legendre.jl | 12 ++++++------ 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 456ee951a..3b25d336d 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -192,8 +192,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true, test_dense_matrix_construction=test_dense_construction) - KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse - KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse + KKpar2D_with_BC_terms_sparse = fkpl_arrays.KKpar2D_with_BC_terms_sparse + KKperp2D_with_BC_terms_sparse = fkpl_arrays.KKperp2D_with_BC_terms_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG finish_init_time = now() @@ -228,8 +228,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary end #print_vector(fc,"fc",nc_global) # multiply by KKpar2D and fill dfc - mul!(dfc,KKpar2D_sparse,fc) - mul!(dgc,KKperp2D_sparse,fc) + mul!(dfc,KKpar2D_with_BC_terms_sparse,fc) + mul!(dgc,KKperp2D_with_BC_terms_sparse,fc) if impose_zero_gradient_BC # enforce zero bc enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index e54a733d2..8d9298b56 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -102,12 +102,12 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp end rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) if test_dense_matrix_construction - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) else - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, LV2D_sparse, + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) @@ -152,6 +152,7 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp dFdvperp = allocate_shared_float(nvpa,nvperp) fka = fokkerplanck_weakform_arrays_struct(bwgt,rpbd,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, + KKpar2D_with_BC_terms_sparse,KKperp2D_with_BC_terms_sparse, LP2D_sparse,LV2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, PPpar2D_sparse,MMparMNperp2D_sparse,MM2DZG_sparse, lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV, diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index c3ef6c526..c9e09a571 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -174,6 +174,8 @@ struct fokkerplanck_weakform_arrays_struct{N} MM2D_sparse::AbstractSparseArray{mk_float,mk_int,N} KKpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} KKperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + KKpar2D_with_BC_terms_sparse::AbstractSparseArray{mk_float,mk_int,N} + KKperp2D_with_BC_terms_sparse::AbstractSparseArray{mk_float,mk_int,N} LP2D_sparse::AbstractSparseArray{mk_float,mk_int,N} LV2D_sparse::AbstractSparseArray{mk_float,mk_int,N} PUperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} @@ -1587,6 +1589,8 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp MM2D = allocate_sparse_matrix_constructor(nsparse) KKpar2D = allocate_sparse_matrix_constructor(nsparse) KKperp2D = allocate_sparse_matrix_constructor(nsparse) + KKpar2D_with_BC_terms = allocate_sparse_matrix_constructor(nsparse) + KKperp2D_with_BC_terms = allocate_sparse_matrix_constructor(nsparse) PUperp2D = allocate_sparse_matrix_constructor(nsparse) PPparPUperp2D = allocate_sparse_matrix_constructor(nsparse) PPpar2D = allocate_sparse_matrix_constructor(nsparse) @@ -1603,8 +1607,9 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) - KKpar_no_BC_terms = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) + KKpar_with_BC_terms = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + KKperp_with_BC_terms = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) KJperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) PPperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) @@ -1622,8 +1627,9 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KKperp_with_BC_terms,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K_with_BC_terms") get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") - get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L_no_BC_terms") + get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") get_QQ_local!(PUperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"U") #print_matrix(MMperp,"MMperp",vperp.ngrid,vperp.ngrid) @@ -1637,8 +1643,8 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp for ielement_vpa in 1:nelement_vpa get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") + get_QQ_local!(KKpar_with_BC_terms,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K_with_BC_terms") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") - get_QQ_local!(KKpar_no_BC_terms,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K_no_BC_terms") get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) @@ -1713,12 +1719,12 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp # (MMpar[ivpa_local,ivpap_local]* # MMperp[ivperp_local,ivperpp_local])) assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, - (KKpar_no_BC_terms[ivpa_local,ivpap_local]* + (KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* LLperp[ivperp_local,ivperpp_local])) assemble_constructor_data!(LV2D,icsc,ic_global,icp_global, - (KKpar_no_BC_terms[ivpa_local,ivpap_local]* + (KKpar[ivpa_local,ivpap_local]* MRperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* (KJperp[ivperp_local,ivperpp_local] - @@ -1730,13 +1736,21 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp (MMpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local])) - # assign K matrices + # assign K matrices (no explicit boundary terms) assemble_constructor_data!(KKpar2D,icsc,ic_global,icp_global, (KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local])) assemble_constructor_data!(KKperp2D,icsc,ic_global,icp_global, (MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local])) + + # assign K matrices (with explicit boundary terms from integration by parts) + assemble_constructor_data!(KKpar2D_with_BC_terms,icsc,ic_global,icp_global, + (KKpar_with_BC_terms[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(KKperp2D_with_BC_terms,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + KKperp_with_BC_terms[ivperp_local,ivperpp_local])) # assign PU matrix assemble_constructor_data!(PUperp2D,icsc,ic_global,icp_global, (MMpar[ivpa_local,ivpap_local]* @@ -1760,6 +1774,8 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp MM2D_sparse = create_sparse_matrix(MM2D) KKpar2D_sparse = create_sparse_matrix(KKpar2D) KKperp2D_sparse = create_sparse_matrix(KKperp2D) + KKpar2D_with_BC_terms_sparse = create_sparse_matrix(KKpar2D_with_BC_terms) + KKperp2D_with_BC_terms_sparse = create_sparse_matrix(KKperp2D_with_BC_terms) LP2D_sparse = create_sparse_matrix(LP2D) LV2D_sparse = create_sparse_matrix(LV2D) PUperp2D_sparse = create_sparse_matrix(PUperp2D) @@ -1781,7 +1797,8 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp # convert these matrices to sparse matrices #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end - return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, + KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse end diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 6455c9b36..b77b2064f 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -922,14 +922,14 @@ function get_QQ_local!(QQ::Array{mk_float,2},ielement, get_SS_local!(QQ,ielement,lobatto,radau,coord) elseif option == "K" get_KK_local!(QQ,ielement,lobatto,radau,coord) - elseif option == "K_no_BC_terms" - get_KK_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=false) + elseif option == "K_with_BC_terms" + get_KK_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=true) elseif option == "J" get_KJ_local!(QQ,ielement,lobatto,radau,coord) elseif option == "L" get_LL_local!(QQ,ielement,lobatto,radau,coord) - elseif option == "L_no_BC_terms" - get_LL_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=false) + elseif option == "L_with_BC_terms" + get_LL_local!(QQ,ielement,lobatto,radau,coord,explicit_BC_terms=true) end return nothing end @@ -988,7 +988,7 @@ end function get_KK_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, - coord;explicit_BC_terms=true) + coord;explicit_BC_terms=false) scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] @@ -1053,7 +1053,7 @@ end function get_LL_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, - coord;explicit_BC_terms=true) + coord;explicit_BC_terms=false) scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] From a7433d92d675c51e40e16f5252fc63c12e07e1ca Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 14 Nov 2023 15:17:12 +0000 Subject: [PATCH 223/331] Make test_dense_construction and test_parallelism options available for interactive use. --- 2D_FEM_assembly_test.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 3b25d336d..442076a63 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -461,15 +461,17 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary plot_test_output = false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, + test_dense_construction=false, + test_parallelism=false, Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 #plot_scan = true #plot_test_output = true#false #impose_zero_gradient_BC = false - test_parallelism = false + #test_parallelism = false test_self_operator = true - test_dense_construction = false + #test_dense_construction = false #nelement_list = Int[8, 16, 32, 64, 128] #nelement_list = Int[4, 8, 16, 32, 64] #nelement_list = Int[2, 4, 8] From 2fd44edd1ace26b21122db49ca453b6c31b5f50b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 14 Nov 2023 15:18:35 +0000 Subject: [PATCH 224/331] Update the function assemble_matrix_operators_dirichlet_bc() to output KKpar and KKperp with and without explicit boundary terms, in line with the corresponding function assemble_matrix_operators_dirichlet_bc_sparse(). --- src/fokker_planck_calculus.jl | 36 +++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index c9e09a571..53c623aed 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1305,6 +1305,10 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe KKpar2D .= 0.0 KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) KKperp2D .= 0.0 + KKpar2D_with_BC_terms = Array{mk_float,2}(undef,nc_global,nc_global) + KKpar2D_with_BC_terms .= 0.0 + KKperp2D_with_BC_terms = Array{mk_float,2}(undef,nc_global,nc_global) + KKperp2D_with_BC_terms .= 0.0 PUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) PUperp2D .= 0.0 PPparPUperp2D = Array{mk_float,2}(undef,nc_global,nc_global) @@ -1329,6 +1333,8 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + KKpar_with_BC_terms = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) + KKperp_with_BC_terms = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KJperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) @@ -1346,6 +1352,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe get_QQ_local!(MRperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"R") get_QQ_local!(MNperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"N") get_QQ_local!(KKperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K") + get_QQ_local!(KKperp_with_BC_terms,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"K_with_BC_terms") get_QQ_local!(KJperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"J") get_QQ_local!(LLperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"L") get_QQ_local!(PPperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"P") @@ -1362,6 +1369,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe for ielement_vpa in 1:vpa.nelement_local get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") get_QQ_local!(KKpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K") + get_QQ_local!(KKpar_with_BC_terms,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"K_with_BC_terms") get_QQ_local!(PPpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"P") #print_matrix(MMpar,"MMpar",vpa.ngrid,vpa.ngrid) #print_matrix(KKpar,"KKpar",vpa.ngrid,vpa.ngrid) @@ -1386,49 +1394,38 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe if lower_boundary_row_vpa if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 else - MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 else - MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 end elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp if ivperpp_local == 1 && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 else - MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2D[ic_global,icp_global] = 1.0 LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 else - MM2D[ic_global,icp_global] = 0.0 LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 end else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] + # assign Laplacian and modified Laplacian matrix data LP2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] + MMpar[ivpa_local,ivpap_local]* @@ -1440,12 +1437,20 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe PPperp[ivperp_local,ivperpp_local] - MNperp[ivperp_local,ivperpp_local])) end + # assign mass matrix data + MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] # assign K matrices KKpar2D[ic_global,icp_global] += KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local] + # assign K matrices with explicit boundary terms from integration by parts + KKpar2D_with_BC_terms[ic_global,icp_global] += KKpar_with_BC_terms[ivpa_local,ivpap_local]* + MMperp[ivperp_local,ivperpp_local] + KKperp2D_with_BC_terms[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + KKperp_with_BC_terms[ivperp_local,ivperpp_local] # assign PU matrix PUperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* PUperp[ivperp_local,ivperpp_local] @@ -1481,14 +1486,17 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe MM2D_sparse = sparse(MM2D) KKpar2D_sparse = sparse(KKpar2D) KKperp2D_sparse = sparse(KKperp2D) + KKpar2D_with_BC_terms_sparse = sparse(KKpar2D_with_BC_terms) + KKperp2D_with_BC_terms_sparse = sparse(KKperp2D_with_BC_terms) LP2D_sparse = sparse(LP2D) LV2D_sparse = sparse(LV2D) PUperp2D_sparse = sparse(PUperp2D) PPparPUperp2D_sparse = sparse(PPparPUperp2D) PPpar2D_sparse = sparse(PPpar2D) MMparMNperp2D_sparse = sparse(MMparMNperp2D) - return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, LP2D_sparse, - LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, + KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, + LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse end From 10c578f9663ad789aba51ad9ae204ea36eac252c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 14 Nov 2023 17:08:37 +0000 Subject: [PATCH 225/331] Add test of numerical conserving terms to 2D_FEM_assembly_test.jl as an interactive option. --- 2D_FEM_assembly_test.jl | 17 +++++++++++++---- src/fokker_planck.jl | 6 ++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 442076a63..782efa0aa 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -14,6 +14,7 @@ using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form using moment_kinetics.fokker_planck: fokker_planck_collision_operator_weak_form! +using moment_kinetics.fokker_planck: conserving_corrections! using moment_kinetics.calculus: derivative! using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.communication @@ -30,8 +31,7 @@ using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data!, calculate_rosenbluth_potential_boundary_data_exact! -using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data - +using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data, enforce_vpavperp_BCs! @@ -139,7 +139,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_parallelism=false,test_self_operator=true, test_dense_construction=false,standalone=false, use_Maxwellian_Rosenbluth_coefficients=false, - use_Maxwellian_field_particle_distribution=false) + use_Maxwellian_field_particle_distribution=false, + test_numerical_conserving_terms=false) # define inputs needed for the test #plot_test_output = false#true #impose_zero_gradient_BC = false#true @@ -267,7 +268,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary end end # test the Laplacian solve with a standard F_Maxwellian -> H_Maxwellian test - + dummy_vpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) @@ -345,6 +346,12 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary impose_zero_gradient_BC=impose_zero_gradient_BC, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution) + if test_numerical_conserving_terms && test_self_operator + # enforce the boundary conditions on CC before it is used for timestepping + enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) + # make ad-hoc conserving corrections + conserving_corrections!(fkpl_arrays.CC,Fs_M,vpa,vperp,dummy_vpavperp) + end # extract C[Fs,Fs'] result # and Rosenbluth potentials for testing begin_vperp_vpa_region() @@ -463,6 +470,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary use_Maxwellian_field_particle_distribution=false, test_dense_construction=false, test_parallelism=false, + test_numerical_conserving_terms=false, Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 @@ -534,6 +542,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_dense_construction=test_dense_construction, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, + test_numerical_conserving_terms=test_numerical_conserving_terms, standalone=false, Lvpa=Lvpa, Lvperp=Lvperp) max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 8d9298b56..be06ec219 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -204,7 +204,7 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos # enforce the boundary conditions on CC before it is used for timestepping enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) # make ad-hoc conserving corrections - conserving_corrections!(fkpl_arrays.CC,pdf_in[:,:,iz,ir,is],vpa,vperp,scratch_dummy) + conserving_corrections!(fkpl_arrays.CC,pdf_in[:,:,iz,ir,is],vpa,vperp,scratch_dummy.dummy_vpavperp) # advance this part of s,r,z with the resulting C[Fs,Fs] begin_vperp_vpa_region() @@ -881,9 +881,7 @@ function symmetric_matrix_inverse(A00,A01,A02,A11,A12,A22,b0,b1,b2) return x0, x1, x2 end -function conserving_corrections!(CC,pdf_in,vpa,vperp,scratch_dummy) - # define a dummy array - dummy_vpavperp = scratch_dummy.dummy_vpavperp +function conserving_corrections!(CC,pdf_in,vpa,vperp,dummy_vpavperp) # compute moments of the input pdf dens = get_density(@view(pdf_in[:,:]), vpa, vperp) upar = get_upar(@view(pdf_in[:,:,]), vpa, vperp, dens) From 95d3ee0674b3a6ae24bfd7c5b001de0f56b6f139 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 15 Nov 2023 09:51:10 +0000 Subject: [PATCH 226/331] Added option to solve a PDE for d2Gdvperp2 rather than using an algebraic relation, with the same numerical cost. Improbed accuracy in the calculation of d2Gdvperp2 is observed in the PDE solve is used. The effect on the overall calculation of C[F,F] is not significant. --- 2D_FEM_assembly_test.jl | 8 +++-- src/fokker_planck.jl | 51 +++++++++++++++++++--------- src/fokker_planck_calculus.jl | 63 ++++++++++++++++++++++++++++++++--- src/gauss_legendre.jl | 3 +- 4 files changed, 101 insertions(+), 24 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 782efa0aa..aa7404832 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -140,7 +140,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_dense_construction=false,standalone=false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, - test_numerical_conserving_terms=false) + test_numerical_conserving_terms=false, + algebraic_solve_for_d2Gdvperp2=true) # define inputs needed for the test #plot_test_output = false#true #impose_zero_gradient_BC = false#true @@ -345,7 +346,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_assembly_serial=test_parallelism, impose_zero_gradient_BC=impose_zero_gradient_BC, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, - use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution) + use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) if test_numerical_conserving_terms && test_self_operator # enforce the boundary conditions on CC before it is used for timestepping enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) @@ -471,6 +473,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_dense_construction=false, test_parallelism=false, test_numerical_conserving_terms=false, + algebraic_solve_for_d2Gdvperp2=true, Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 @@ -543,6 +546,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, test_numerical_conserving_terms=test_numerical_conserving_terms, + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2, standalone=false, Lvpa=Lvpa, Lvperp=Lvperp) max_C_err[iscan], L2_C_err[iscan] = fkerr.C_M.max ,fkerr.C_M.L2 max_H_err[iscan], L2_H_err[iscan] = fkerr.H_M.max ,fkerr.H_M.L2 diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index be06ec219..19cee1945 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -102,12 +102,16 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp end rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) if test_dense_matrix_construction - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, + KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, + LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) else - MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, + MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, + KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, + LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) @@ -116,6 +120,7 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp lu_obj_MMZG = lu(MM2DZG_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) + lu_obj_LB = lu(LB2D_sparse) @serial_region begin if global_rank[] == 0 println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) @@ -153,9 +158,9 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp fka = fokkerplanck_weakform_arrays_struct(bwgt,rpbd,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, KKpar2D_with_BC_terms_sparse,KKperp2D_with_BC_terms_sparse, - LP2D_sparse,LV2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, - PPpar2D_sparse,MMparMNperp2D_sparse,MM2DZG_sparse, - lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV, + LP2D_sparse,LV2D_sparse,LB2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, + PPpar2D_sparse,MMparMNperp2D_sparse,KPperp2D_sparse,MM2DZG_sparse, + lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV,lu_obj_LB, YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc, CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa, FF, dFdvpa, dFdvperp) @@ -377,7 +382,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp test_assembly_serial=false,impose_zero_gradient_BC=false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, - skip_Rosenbluth_potential_calculation=false) + skip_Rosenbluth_potential_calculation=false, + algebraic_solve_for_d2Gdvperp2 = true) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @@ -393,11 +399,13 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse + KPperp2D_sparse = fkpl_arrays.KPperp2D_sparse MM2DZG_sparse = fkpl_arrays.MM2DZG_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG lu_obj_LP = fkpl_arrays.lu_obj_LP lu_obj_LV = fkpl_arrays.lu_obj_LV + lu_obj_LB = fkpl_arrays.lu_obj_LB S_dummy = fkpl_arrays.S_dummy Q_dummy = fkpl_arrays.Q_dummy @@ -472,17 +480,28 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + if algebraic_solve_for_d2Gdvperp2 + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + end + # use the algebraic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + else + # solve a weak-form PDE for d2Gdvperp2 + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] + end + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) end - # use the elliptic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) end end # assemble the RHS of the collision operator matrix eq diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 53c623aed..5e3cb4816 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -178,16 +178,19 @@ struct fokkerplanck_weakform_arrays_struct{N} KKperp2D_with_BC_terms_sparse::AbstractSparseArray{mk_float,mk_int,N} LP2D_sparse::AbstractSparseArray{mk_float,mk_int,N} LV2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + LB2D_sparse::AbstractSparseArray{mk_float,mk_int,N} PUperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} PPparPUperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} PPpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} MMparMNperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} + KPperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} MM2DZG_sparse::AbstractSparseArray{mk_float,mk_int,N} # lu decomposition objects lu_obj_MM::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_MMZG::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_LP::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_LV::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} + lu_obj_LB::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} # elemental matrices for the assembly of C[Fs,Fsp] YY_arrays::YY_collision_operator_arrays # dummy arrays for elliptic solvers @@ -1305,6 +1308,8 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe KKpar2D .= 0.0 KKperp2D = Array{mk_float,2}(undef,nc_global,nc_global) KKperp2D .= 0.0 + KPperp2D = Array{mk_float,2}(undef,nc_global,nc_global) + KPperp2D .= 0.0 KKpar2D_with_BC_terms = Array{mk_float,2}(undef,nc_global,nc_global) KKpar2D_with_BC_terms .= 0.0 KKperp2D_with_BC_terms = Array{mk_float,2}(undef,nc_global,nc_global) @@ -1323,6 +1328,9 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe # Modified Laplacian matrix LV2D = Array{mk_float,2}(undef,nc_global,nc_global) LV2D .= 0.0 + # Modified Laplacian matrix + LB2D = Array{mk_float,2}(undef,nc_global,nc_global) + LB2D .= 0.0 #print_matrix(MM2D,"MM2D",nc_global,nc_global) # local dummy arrays @@ -1396,33 +1404,41 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe if ivpap_local == 1 && ivperp_local == ivperpp_local LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 + LB2D[ic_global,icp_global] = 1.0 else LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 + LB2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 + LB2D[ic_global,icp_global] = 1.0 else LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 + LB2D[ic_global,icp_global] = 0.0 end elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp if ivperpp_local == 1 && ivpa_local == ivpap_local LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 + LB2D[ic_global,icp_global] = 1.0 else LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 + LB2D[ic_global,icp_global] = 0.0 end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local LP2D[ic_global,icp_global] = 1.0 LV2D[ic_global,icp_global] = 1.0 + LB2D[ic_global,icp_global] = 1.0 else LP2D[ic_global,icp_global] = 0.0 LV2D[ic_global,icp_global] = 0.0 + LB2D[ic_global,icp_global] = 0.0 end else # assign Laplacian and modified Laplacian matrix data @@ -1436,6 +1452,12 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe (KJperp[ivperp_local,ivperpp_local] - PPperp[ivperp_local,ivperpp_local] - MNperp[ivperp_local,ivperpp_local])) + LB2D[ic_global,icp_global] += (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + 4.0*MNperp[ivperp_local,ivperpp_local])) end # assign mass matrix data MM2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* @@ -1446,6 +1468,10 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe MMperp[ivperp_local,ivperpp_local] KKperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local] + KPperp2D[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + 2.0*PPperp[ivperp_local,ivperpp_local] - + 2.0*MNperp[ivperp_local,ivperpp_local]) # assign K matrices with explicit boundary terms from integration by parts KKpar2D_with_BC_terms[ic_global,icp_global] += KKpar_with_BC_terms[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] @@ -1490,13 +1516,16 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe KKperp2D_with_BC_terms_sparse = sparse(KKperp2D_with_BC_terms) LP2D_sparse = sparse(LP2D) LV2D_sparse = sparse(LV2D) + LB2D_sparse = sparse(LB2D) + KPperp2D_sparse = sparse(KPperp2D) PUperp2D_sparse = sparse(PUperp2D) PPparPUperp2D_sparse = sparse(PPparPUperp2D) PPpar2D_sparse = sparse(PPpar2D) MMparMNperp2D_sparse = sparse(MMparMNperp2D) return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, - LP2D_sparse, LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + LP2D_sparse, LV2D_sparse, LB2D_sparse, + KPperp2D_sparse,PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse end @@ -1603,10 +1632,13 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp PPparPUperp2D = allocate_sparse_matrix_constructor(nsparse) PPpar2D = allocate_sparse_matrix_constructor(nsparse) MMparMNperp2D = allocate_sparse_matrix_constructor(nsparse) + KPperp2D = allocate_sparse_matrix_constructor(nsparse) # Laplacian matrix LP2D = allocate_sparse_matrix_constructor(nsparse) - # Modified Laplacian matrix + # Modified Laplacian matrix (for d / d vperp potentials) LV2D = allocate_sparse_matrix_constructor(nsparse) + # Modified Laplacian matrix (for d^2 / d vperp^2 potentials) + LB2D = allocate_sparse_matrix_constructor(nsparse) # local dummy arrays MMpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) @@ -1685,40 +1717,48 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp if ivperpp_local == 1 && ivpa_local == ivpap_local #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) + assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end else # assign mass matrix data @@ -1738,6 +1778,13 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp (KJperp[ivperp_local,ivperpp_local] - PPperp[ivperp_local,ivperpp_local] - MNperp[ivperp_local,ivperpp_local]))) + assemble_constructor_data!(LB2D,icsc,ic_global,icp_global, + (KKpar[ivpa_local,ivpap_local]* + MRperp[ivperp_local,ivperpp_local] + + MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + PPperp[ivperp_local,ivperpp_local] - + 4.0*MNperp[ivperp_local,ivperpp_local]))) end #assign mass matrix assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, @@ -1751,6 +1798,11 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp assemble_constructor_data!(KKperp2D,icsc,ic_global,icp_global, (MMpar[ivpa_local,ivpap_local]* KKperp[ivperp_local,ivperpp_local])) + assemble_constructor_data!(KPperp2D,icsc,ic_global,icp_global, + (MMpar[ivpa_local,ivpap_local]* + (KJperp[ivperp_local,ivperpp_local] - + 2.0*PPperp[ivperp_local,ivperpp_local] - + 2.0*MNperp[ivperp_local,ivperpp_local]))) # assign K matrices (with explicit boundary terms from integration by parts) assemble_constructor_data!(KKpar2D_with_BC_terms,icsc,ic_global,icp_global, @@ -1786,6 +1838,8 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp KKperp2D_with_BC_terms_sparse = create_sparse_matrix(KKperp2D_with_BC_terms) LP2D_sparse = create_sparse_matrix(LP2D) LV2D_sparse = create_sparse_matrix(LV2D) + LB2D_sparse = create_sparse_matrix(LB2D) + KPperp2D_sparse = create_sparse_matrix(KPperp2D) PUperp2D_sparse = create_sparse_matrix(PUperp2D) PPparPUperp2D_sparse = create_sparse_matrix(PPparPUperp2D) PPpar2D_sparse = create_sparse_matrix(PPpar2D) @@ -1806,8 +1860,9 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, - KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, - LV2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, + KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, + LP2D_sparse, LV2D_sparse, LB2D_sparse, + KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse end diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index b77b2064f..22d99e5f6 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -1033,8 +1033,7 @@ function get_KJ_local!(QQ,ielement, scale_factor = scale_factor_func(coord.L,coord.nelement_global) shift_factor = shift_factor_func(coord.L,coord.nelement_global,coord.nelement_local,coord.irank,ielement) + 0.5*coord.L if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp - # extra scale and shift factors required because of vperp in integral - # P0 factors make this a d^2 / dvperp^2 rather than (1/vperp) d ( vperp d (.) / d vperp) + # extra scale and shift factors required because of vperp^2 in integral if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (lobatto.K0*((shift_factor^2)/scale_factor) + lobatto.K1*2.0*shift_factor + From 4e20ded88489ea9c97dce8bcf9838f4c465bdba9 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Wed, 15 Nov 2023 12:18:36 +0000 Subject: [PATCH 227/331] Use allocate_shared_float() to create shared-memory arrays in 2D_FEM_assembly_test.jl --- 2D_FEM_assembly_test.jl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index aa7404832..6e0f1d228 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -6,6 +6,7 @@ using MPI using Measures using Dates import moment_kinetics +using moment_kinetics.array_allocation: allocate_float, allocate_shared_float using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral @@ -272,35 +273,35 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary dummy_vpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) Fs_M = Array{mk_float,2}(undef,vpa.n,vperp.n) F_M = Array{mk_float,2}(undef,vpa.n,vperp.n) - C_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + C_M_num = allocate_shared_float(vpa.n,vperp.n) C_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) C_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) #dFdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) #dFdvperp_M = Array{mk_float,2}(undef,vpa.n,vperp.n) #d2Fdvperpdvpa_M = Array{mk_float,2}(undef,vpa.n,vperp.n) H_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - H_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + H_M_num = allocate_shared_float(vpa.n,vperp.n) H_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) G_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - G_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + G_M_num = allocate_shared_float(vpa.n,vperp.n) G_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvpa2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvpa2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvpa2_M_num = allocate_shared_float(vpa.n,vperp.n) d2Gdvpa2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperp2_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperp2_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperp2_M_num = allocate_shared_float(vpa.n,vperp.n) d2Gdvperp2_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dGdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dGdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dGdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) dGdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) d2Gdvperpdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2Gdvperpdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) d2Gdvperpdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvpa_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvpa_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dHdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) dHdvpa_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) dHdvperp_M_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - dHdvperp_M_num = MPISharedArray{mk_float,2}(undef,vpa.n,vperp.n) + dHdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) dHdvperp_M_err = Array{mk_float,2}(undef,vpa.n,vperp.n) if test_self_operator From 0ce58d9d8687781a468c0472d445522a6d1b32b3 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Wed, 15 Nov 2023 12:19:19 +0000 Subject: [PATCH 228/331] Put some serial operations in @serial_region --- 2D_FEM_assembly_test.jl | 35 +++++++++++++++++++---------------- src/fokker_planck_calculus.jl | 15 +++++++++------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 6e0f1d228..06803c941 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -314,23 +314,26 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary ms = 1.0 msp = 1.0 nussp = 1.0 - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - dens,upar,vth,msp, - nussp,vpa,vperp,ivpa,ivperp) + begin_serial_region() + @serial_region begin + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + dens,upar,vth,msp, + nussp,vpa,vperp,ivpa,ivperp) + end end - end + end rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) # use known test function to provide exact data calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 5e3cb4816..77bb9dccc 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -989,14 +989,17 @@ end function assign_exact_boundary_data!(func_data::vpa_vperp_boundary_data, func_exact,vpa,vperp) + begin_serial_region() nvpa = vpa.n nvperp = vperp.n - for ivperp in 1:nvperp - func_data.lower_boundary_vpa[ivperp] = func_exact[1,ivperp] - func_data.upper_boundary_vpa[ivperp] = func_exact[nvpa,ivperp] - end - for ivpa in 1:nvpa - func_data.upper_boundary_vperp[ivpa] = func_exact[ivpa,nvperp] + @serial_region begin + for ivperp in 1:nvperp + func_data.lower_boundary_vpa[ivperp] = func_exact[1,ivperp] + func_data.upper_boundary_vpa[ivperp] = func_exact[nvpa,ivperp] + end + for ivpa in 1:nvpa + func_data.upper_boundary_vperp[ivpa] = func_exact[ivpa,nvperp] + end end return nothing end From 44ded01a5ade87cbcefed6b25cf81cd3dd029f9b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 15 Nov 2023 14:20:24 +0000 Subject: [PATCH 229/331] Remove inappropriate @serial_region marco. Exact arrays are standard Array types which are not MPISharedArrays, and so require filling on every process. --- 2D_FEM_assembly_test.jl | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 06803c941..f1e3cfd55 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -315,25 +315,23 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary msp = 1.0 nussp = 1.0 begin_serial_region() - @serial_region begin - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - dens,upar,vth,msp, - nussp,vpa,vperp,ivpa,ivperp) - end + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + dens,upar,vth,msp, + nussp,vpa,vperp,ivpa,ivperp) end - end + end rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) # use known test function to provide exact data calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, From f8b1465373991e831107b66d6d19e6086589f641 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 15 Nov 2023 14:37:48 +0000 Subject: [PATCH 230/331] Apparent resolution of Fokker-Planck shared-memory bug. The elliptic solvers fill in their appropriate [ivpa,ivperp] data on each process. The next function is the assembly function, which fills in [ivpa,ivperp] data for the RHS of the collision operator weak form. BUT, the assembly potentially requires non-local data for the assembly, so we should make sure that all the Rosenbluth potentials coming from the elliptic solver are synchronised before entering the assembly. --- src/fokker_planck.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 19cee1945..7c126e8be 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -494,6 +494,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp rhsc,rhqc,sc,qc,vpa,vperp) else # solve a weak-form PDE for d2Gdvperp2 + begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] @@ -502,6 +503,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, rhsc,rhqc,sc,qc,vpa,vperp) end + begin_serial_region() end end # assemble the RHS of the collision operator matrix eq From 58b2494ad77c8e9608092dd90d10a4830311cfa8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 15 Nov 2023 16:30:16 +0000 Subject: [PATCH 231/331] Make elliptic solve for d2Gdvperp2 the default option. --- 2D_FEM_assembly_test.jl | 4 ++-- src/fokker_planck.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index f1e3cfd55..7f99dfe8c 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -142,7 +142,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, test_numerical_conserving_terms=false, - algebraic_solve_for_d2Gdvperp2=true) + algebraic_solve_for_d2Gdvperp2=false) # define inputs needed for the test #plot_test_output = false#true #impose_zero_gradient_BC = false#true @@ -475,7 +475,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_dense_construction=false, test_parallelism=false, test_numerical_conserving_terms=false, - algebraic_solve_for_d2Gdvperp2=true, + algebraic_solve_for_d2Gdvperp2=false, Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 7c126e8be..d7eed9f93 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -383,7 +383,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, skip_Rosenbluth_potential_calculation=false, - algebraic_solve_for_d2Gdvperp2 = true) + algebraic_solve_for_d2Gdvperp2 = false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) From 9c8b1d370ef60a58afbd7c5d23d1e81230864c47 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 15 Nov 2023 17:15:04 +0000 Subject: [PATCH 232/331] A commit with a series of linked changes. It was found that imposing the boundary conditions that the distribution function must satisfy in the mass matrix led to numerical instability at long times. This was confirmed by removing the boundary condition imposition from the mass matrix. The addition of numerical conserving terms to the weak-form operator then provided a numerical operator that appears to have an approximate H-theorem and a stable long-time solution. Imposing numerical conserving terms alone on the old form of the mass matrix was not adequate. Here, we remove all code associated with the option impose_zero_gradient_BC, including unused functions in fokker_planck_calculus.jl. In addition to this, we also make changes in the calculus.jl and gauss_legendre.jl modules so that the comments regarding imposing boundary conditions via the 1D mass matrix are removed. We also make sure that weak-form matrices that are used only in numerical differentiation (i.e., K in M.f" = K.f, with M the mass matrix) include explicit integration-by-parts boundary condition terms so that the weak-form differentiation gives comparable or better results to the interpolation derivative method for a first and second order derivative, even when the function f has not gone to 0 by the end of the domain. This functionality can be switched by changing "K_with_BC_terms" -> "K", "L_with_BC_terms" -> "L" and toggling explicit_BC_terms = true -> explicit_BC_terms = false in the appropriate places. --- 2D_FEM_assembly_test.jl | 27 +---- GaussLobattoLegendre_test.jl | 29 ++--- src/calculus.jl | 4 +- src/fokker_planck.jl | 24 +--- src/fokker_planck_calculus.jl | 199 +--------------------------------- src/gauss_legendre.jl | 23 ++-- 6 files changed, 30 insertions(+), 276 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 7f99dfe8c..1360bdd1a 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -136,7 +136,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary function test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp; - Lvpa=12.0,Lvperp=6.0,plot_test_output=false,impose_zero_gradient_BC=false, + Lvpa=12.0,Lvperp=6.0,plot_test_output=false, test_parallelism=false,test_self_operator=true, test_dense_construction=false,standalone=false, use_Maxwellian_Rosenbluth_coefficients=false, @@ -145,7 +145,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary algebraic_solve_for_d2Gdvperp2=false) # define inputs needed for the test #plot_test_output = false#true - #impose_zero_gradient_BC = false#true #test_parallelism = false#true #test_self_operator = true #test_dense_construction = false#true @@ -198,7 +197,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary KKpar2D_with_BC_terms_sparse = fkpl_arrays.KKpar2D_with_BC_terms_sparse KKperp2D_with_BC_terms_sparse = fkpl_arrays.KKperp2D_with_BC_terms_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM - lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG finish_init_time = now() fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) @@ -233,22 +231,9 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary # multiply by KKpar2D and fill dfc mul!(dfc,KKpar2D_with_BC_terms_sparse,fc) mul!(dgc,KKperp2D_with_BC_terms_sparse,fc) - if impose_zero_gradient_BC - # enforce zero bc - enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=true) - enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - fc = lu_obj_MMZG \ dfc - gc = lu_obj_MMZG \ dgc - else - # enforce zero bc - #enforce_zero_bc!(fc,vpa,vperp,impose_BC_at_zero_vperp=false) - #enforce_zero_bc!(gc,vpa,vperp,impose_BC_at_zero_vperp=false) - # invert mass matrix and fill fc - fc = lu_obj_MM \ dfc - gc = lu_obj_MM \ dgc - end - #fc = cholesky_obj \ dfc + # invert mass matrix and fill fc + fc = lu_obj_MM \ dfc + gc = lu_obj_MM \ dgc #print_vector(fc,"fc",nc_global) # unravel ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) @@ -346,7 +331,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary fkpl_arrays, vperp, vpa, vperp_spectral, vpa_spectral, test_assembly_serial=test_parallelism, - impose_zero_gradient_BC=impose_zero_gradient_BC, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) @@ -467,7 +451,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary end function run_assembly_test(; ngrid=5, nelement_list = [8], - impose_zero_gradient_BC= false, plot_scan=true, plot_test_output = false, use_Maxwellian_Rosenbluth_coefficients=false, @@ -481,7 +464,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary #ngrid = 5 #plot_scan = true #plot_test_output = true#false - #impose_zero_gradient_BC = false #test_parallelism = false test_self_operator = true #test_dense_construction = false @@ -541,7 +523,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary nelement_vperp = nelement fkerr, calculate_times[iscan], init_times[iscan] = test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp, plot_test_output=plot_test_output, - impose_zero_gradient_BC=impose_zero_gradient_BC, test_parallelism=test_parallelism, test_self_operator=test_self_operator, test_dense_construction=test_dense_construction, diff --git a/GaussLobattoLegendre_test.jl b/GaussLobattoLegendre_test.jl index f7e972f45..e60ece8d8 100644 --- a/GaussLobattoLegendre_test.jl +++ b/GaussLobattoLegendre_test.jl @@ -147,18 +147,14 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) if y.name == "vpa" mul!(b,y_spectral.S_matrix,f_exact) - #b[1] -= f_exact[1] - #b[y.n] += f_exact[y.n] - gausslegendre_mass_matrix_solve!(df_num,b,y.name,y_spectral) + gausslegendre_mass_matrix_solve!(df_num,b,y_spectral) @. df_err = df_num - df_exact #println("df_num (weak form): ",df_num) #println("df_exact (weak form): ",df_exact) println("max(df_err) (weak form): ",maximum(df_err)) - #second_derivative!(d2f_num, f_exact, y, y_spectral) - mul!(b,y_spectral.K_matrix,f_exact) - #b[1] -= sum(y_spectral.lobatto.Dmat[1,:].*f_exact[1:y.ngrid])/y.element_scale[1] - #b[y.n] += sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] - gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + second_derivative!(d2f_num, f_exact, y, y_spectral) + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -170,18 +166,16 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv elseif y.name == "vperp" #println("condition: ",cond(y_spectral.mass_matrix)) mul!(b,y_spectral.S_matrix,g_exact) - #b[y.n] += y.grid[y.n]*g_exact[y.n] - gausslegendre_mass_matrix_solve!(divg_num,b,y.name,y_spectral) + gausslegendre_mass_matrix_solve!(divg_num,b,y_spectral) @. divg_err = abs(divg_num - divg_exact) #println("divg_b (weak form): ",b) #println("divg_num (weak form): ",divg_num) #println("divg_exact (weak form): ",divg_exact) println("max(divg_err) (weak form): ",maximum(divg_err)) - #second_derivative!(d2f_num, f_exact, y, y_spectral) - mul!(b,y_spectral.K_matrix,f_exact) - #b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*f_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] - gausslegendre_mass_matrix_solve!(d2f_num,b,y.name,y_spectral) + second_derivative!(d2f_num, f_exact, y, y_spectral) + #mul!(b,y_spectral.K_matrix,f_exact) + #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -191,10 +185,9 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv outfile = "vperp_second_derivative_test.pdf" savefig(outfile) - mul!(b,y_spectral.L_matrix,h_exact) - #b[y.n] += y.grid[y.n]*sum(y_spectral.lobatto.Dmat[y.ngrid,:].*h_exact[y.n+1-y.ngrid:y.n])/y.element_scale[y.nelement_local] - #laplacian_derivative!(laph_num, h_exact, y, y_spectral) - gausslegendre_mass_matrix_solve!(laph_num,b,y.name,y_spectral) + laplacian_derivative!(laph_num, h_exact, y, y_spectral) + #mul!(b,y_spectral.L_matrix,h_exact) + #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* #println(b[1:10]) #println(laph_num) diff --git a/src/calculus.jl b/src/calculus.jl index 1aae008c6..31ce17442 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -44,7 +44,7 @@ function second_derivative!(d2f, f, coord, spectral::gausslegendre_info) # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,coord.name,spectral) + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) end function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) @@ -54,7 +54,7 @@ function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,coord.name,spectral) + gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) end """ Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index d7eed9f93..5f0c3ddea 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -34,8 +34,6 @@ using ..fokker_planck_calculus: allocate_rosenbluth_potential_boundary_data using ..fokker_planck_calculus: fokkerplanck_arrays_struct, fokkerplanck_weakform_arrays_struct using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse -using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient -using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial! using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel! using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! @@ -107,17 +105,14 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) else MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - MM2DZG_sparse = assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) end lu_obj_MM = lu(MM2D_sparse) - lu_obj_MMZG = lu(MM2DZG_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) lu_obj_LB = lu(LB2D_sparse) @@ -159,8 +154,8 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp fka = fokkerplanck_weakform_arrays_struct(bwgt,rpbd,MM2D_sparse,KKpar2D_sparse,KKperp2D_sparse, KKpar2D_with_BC_terms_sparse,KKperp2D_with_BC_terms_sparse, LP2D_sparse,LV2D_sparse,LB2D_sparse,PUperp2D_sparse,PPparPUperp2D_sparse, - PPpar2D_sparse,MMparMNperp2D_sparse,KPperp2D_sparse,MM2DZG_sparse, - lu_obj_MM,lu_obj_MMZG,lu_obj_LP,lu_obj_LV,lu_obj_LB, + PPpar2D_sparse,MMparMNperp2D_sparse,KPperp2D_sparse, + lu_obj_MM,lu_obj_LP,lu_obj_LV,lu_obj_LB, YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc, CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa, FF, dFdvpa, dFdvperp) @@ -379,7 +374,7 @@ Function for evaluating Css' = Css'[Fs,Fs'] function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp, fkpl_arrays::fokkerplanck_weakform_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral; - test_assembly_serial=false,impose_zero_gradient_BC=false, + test_assembly_serial=false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, skip_Rosenbluth_potential_calculation=false, @@ -400,9 +395,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse KPperp2D_sparse = fkpl_arrays.KPperp2D_sparse - MM2DZG_sparse = fkpl_arrays.MM2DZG_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM - lu_obj_MMZG = fkpl_arrays.lu_obj_MMZG lu_obj_LP = fkpl_arrays.lu_obj_LP lu_obj_LV = fkpl_arrays.lu_obj_LV lu_obj_LB = fkpl_arrays.lu_obj_LB @@ -540,15 +533,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp # solve the collision operator matrix eq begin_serial_region() @serial_region begin - if impose_zero_gradient_BC - enforce_zero_bc!(rhsc,vpa,vperp,impose_BC_at_zero_vperp=true) - # invert mass matrix and fill fc - sc .= lu_obj_MMZG \ rhsc - else - #enforce_zero_bc!(rhsc,vpa,vperp) - # invert mass matrix and fill fc - sc .= lu_obj_MM \ rhsc - end + # invert mass matrix and fill fc + sc .= lu_obj_MM \ rhsc end ravel_c_to_vpavperp_parallel!(CC,sc,vpa.n) return nothing diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 77bb9dccc..af3445376 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -8,9 +8,7 @@ the Full-F Fokker-Planck Collision Operator module fokker_planck_calculus export assemble_matrix_operators_dirichlet_bc -export assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient export assemble_matrix_operators_dirichlet_bc_sparse -export assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse export assemble_explicit_collision_operator_rhs_serial! export assemble_explicit_collision_operator_rhs_parallel! export assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! @@ -184,10 +182,8 @@ struct fokkerplanck_weakform_arrays_struct{N} PPpar2D_sparse::AbstractSparseArray{mk_float,mk_int,N} MMparMNperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} KPperp2D_sparse::AbstractSparseArray{mk_float,mk_int,N} - MM2DZG_sparse::AbstractSparseArray{mk_float,mk_int,N} # lu decomposition objects lu_obj_MM::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} - lu_obj_MMZG::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_LP::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_LV::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} lu_obj_LB::SuiteSparse.UMFPACK.UmfpackLU{mk_float,mk_int} @@ -1341,7 +1337,6 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) MNperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) MRperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - MMperp_p1 = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) KKperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) KKpar_with_BC_terms = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) @@ -1532,89 +1527,6 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe PPpar2D_sparse, MMparMNperp2D_sparse end -function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient(vpa,vperp,vpa_spectral,vperp_spectral) - nc_global = vpa.n*vperp.n - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - MM2DZG = Array{mk_float,2}(undef,nc_global,nc_global) - MM2DZG .= 0.0 - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - @serial_region begin - if global_rank[] == 0 - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp - if ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = vperp_spectral.radau.D0[ivperpp_local] - else - MM2DZG[ic_global,icp_global] = 0.0 - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - MM2DZG[ic_global,icp_global] = 1.0 - else - MM2DZG[ic_global,icp_global] = 0.0 - end - else - # assign mass matrix data - MM2DZG[ic_global,icp_global] += MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local] - end - end - end - end - end - end - end - @serial_region begin - if global_rank[] == 0 - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - if nc_global < 30 - print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) - end - # convert these matrices to sparse matrices - if global_rank[] == 0 - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - MM2DZG_sparse = sparse(MM2DZG) - return MM2DZG_sparse -end - function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n @@ -1648,7 +1560,6 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp MMperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) MNperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) MRperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - MMperp_p1 = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) KKpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) KKpar_with_BC_terms = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) KKperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) @@ -1717,58 +1628,46 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp if lower_boundary_row_vpa if ivpap_local == 1 && ivperp_local == ivperpp_local - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vpa if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif lower_boundary_row_vperp && impose_BC_at_zero_vperp if ivperpp_local == 1 && ivpa_local == ivpap_local - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end elseif upper_boundary_row_vperp if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,1.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,1.0) else - #assign_constructor_data!(MM2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LP2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LV2D,icsc,ic_global,icp_global,0.0) assign_constructor_data!(LB2D,icsc,ic_global,icp_global,0.0) end else - # assign mass matrix data - #println("MM2D += ", MMpar[ivpa_local,ivpap_local]*MMperp[ivperp_local,ivperpp_local]) - #assemble_constructor_data!(MM2D,icsc,ic_global,icp_global, - # (MMpar[ivpa_local,ivpap_local]* - # MMperp[ivperp_local,ivperpp_local])) + # assign Laplacian matrix data assemble_constructor_data!(LP2D,icsc,ic_global,icp_global, (KKpar[ivpa_local,ivpap_local]* MMperp[ivperp_local,ivperpp_local] + @@ -1869,102 +1768,6 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp PPpar2D_sparse, MMparMNperp2D_sparse end -function assemble_matrix_operators_dirichlet_bc_plus_vperp_zero_gradient_sparse(vpa,vperp,vpa_spectral,vperp_spectral) - # Assemble a 2D mass matrix in the global compound coordinate - nc_global = vpa.n*vperp.n - ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 - ntot_vperp = (vperp.nelement_local - 1)*(vperp.ngrid^2 - 1) + vperp.ngrid^2 - nsparse = ntot_vpa*ntot_vperp - ngrid_vpa = vpa.ngrid - nelement_vpa = vpa.nelement_local - ngrid_vperp = vperp.ngrid - nelement_vperp = vperp.nelement_local - - MM2DZG = allocate_sparse_matrix_constructor(nsparse) - # local dummy arrays - MMpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) - MMperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - - @serial_region begin - if global_rank[] == 0 - println("begin elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - for ielement_vperp in 1:vperp.nelement_local - get_QQ_local!(MMperp,ielement_vperp,vperp_spectral.lobatto,vperp_spectral.radau,vperp,"M") - for ielement_vpa in 1:vpa.nelement_local - get_QQ_local!(MMpar,ielement_vpa,vpa_spectral.lobatto,vpa_spectral.radau,vpa,"M") - for ivperpp_local in 1:vperp.ngrid - for ivperp_local in 1:vperp.ngrid - for ivpap_local in 1:vpa.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - icp_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpap_local,ivperpp_local) #get_indices(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivpap_local,ivperp_local,ivperpp_local) - icsc = icsc_func(ivpa_local,ivpap_local,ielement_vpa::mk_int, - ngrid_vpa,nelement_vpa, - ivperp_local,ivperpp_local, - ielement_vperp, - ngrid_vperp,nelement_vperp) - # boundary condition possibilities - lower_boundary_row_vpa = (ielement_vpa == 1 && ivpa_local == 1) - upper_boundary_row_vpa = (ielement_vpa == vpa.nelement_local && ivpa_local == vpa.ngrid) - lower_boundary_row_vperp = (ielement_vperp == 1 && ivperp_local == 1) - upper_boundary_row_vperp = (ielement_vperp == vperp.nelement_local && ivperp_local == vperp.ngrid) - - - if lower_boundary_row_vpa - if ivpap_local == 1 && ivperp_local == ivperpp_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vpa - if ivpap_local == vpa.ngrid && ivperp_local == ivperpp_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif lower_boundary_row_vperp && !lower_boundary_row_vpa && !upper_boundary_row_vperp - if ivpa_local == ivpap_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,vperp_spectral.radau.D0[ivperpp_local]) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - elseif upper_boundary_row_vperp - if ivperpp_local == vperp.ngrid && ivpa_local == ivpap_local - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,1.0) - else - assign_constructor_data!(MM2DZG,icsc,ic_global,icp_global,0.0) - end - else - # assign mass matrix data - assemble_constructor_data!(MM2DZG,icsc,ic_global,icp_global, - (MMpar[ivpa_local,ivpap_local]* - MMperp[ivperp_local,ivperpp_local])) - end - end - end - end - end - end - end - @serial_region begin - if global_rank[] == 0 - println("finished elliptic operator assignment (zero gradient at vperp = 0) ", Dates.format(now(), dateformat"H:MM:SS")) - end - #if nc_global < 30 - # print_matrix(MM2DZG,"MM2DZG",nc_global,nc_global) - #end - # convert these matrices to sparse matrices - if global_rank[] == 0 - println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) - end - end - MM2DZG_sparse = create_sparse_matrix(MM2DZG) - return MM2DZG_sparse -end - - function calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) YY0perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) YY1perp = Array{mk_float,4}(undef,vperp.ngrid,vperp.ngrid,vperp.ngrid,vperp.nelement_local) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 22d99e5f6..af3476cce 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -107,8 +107,8 @@ function setup_gausslegendre_pseudospectral(coord) setup_global_weak_form_matrix!(mass_matrix, lobatto, radau, coord, "M") setup_global_weak_form_matrix!(S_matrix, lobatto, radau, coord, "S") - setup_global_weak_form_matrix!(K_matrix, lobatto, radau, coord, "K") - setup_global_weak_form_matrix!(L_matrix, lobatto, radau, coord, "L") + setup_global_weak_form_matrix!(K_matrix, lobatto, radau, coord, "K_with_BC_terms") + setup_global_weak_form_matrix!(L_matrix, lobatto, radau, coord, "L_with_BC_terms") mass_matrix_lu = lu(sparse(mass_matrix)) Qmat = allocate_float(coord.ngrid,coord.ngrid) return gausslegendre_info(lobatto,radau,mass_matrix,sparse(S_matrix),K_matrix,L_matrix,mass_matrix_lu,Qmat) @@ -272,7 +272,7 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] - get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord,explicit_BC_terms=true) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) zero_gradient_bc_lower_boundary = false#true @@ -288,7 +288,7 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) - get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + get_KK_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord,explicit_BC_terms=true) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) end @@ -313,7 +313,7 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) imin = coord.imin[j]-k # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] - get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord,explicit_BC_terms=true) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) zero_gradient_bc_lower_boundary = false#true @@ -329,7 +329,7 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) # imax is the maximum index on the full grid for this (jth) element imax = coord.imax[j] #@views mul!(df[:,j],gausslegendre.lobatto.Kmat[:,:],ff[imin:imax]) - get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord) + get_LL_local!(gausslegendre.Qmat,j,gausslegendre.lobatto,gausslegendre.radau,coord,explicit_BC_terms=true) #println(gausslegendre.Qmat) @views mul!(df[:,j],gausslegendre.Qmat[:,:],ff[imin:imax]) end @@ -339,16 +339,7 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) return nothing end -function gausslegendre_mass_matrix_solve!(f,b,coord_name,spectral) - if coord_name == "vperp" - # enforce zero (value or gradient) boundary conditions - #b[1] = 0.0 # uncomment if bc is imposed at vperp = 0 in mass matrix - #b[end] = 0.0 - else - # enforce zero (value or gradient) boundary conditions - #b[1] = 0.0 - #b[end] = 0.0 - end +function gausslegendre_mass_matrix_solve!(f,b,spectral) # invert mass matrix system y = spectral.mass_matrix_lu \ b @. f = y From 24aac53327d6157d9df429638abe77ff288213d5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 09:17:13 +0000 Subject: [PATCH 233/331] Move some basic test functions to src/fokker_planck_test.jl to permit reuse in the test/ directory. --- 2D_FEM_assembly_test.jl | 79 +---------------------------------- src/fokker_planck_test.jl | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 78 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 1360bdd1a..1063efad1 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -28,6 +28,7 @@ using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwelli using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian using moment_kinetics.fokker_planck_test: Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data @@ -57,84 +58,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary println("\n") end - function plot_test_data(func_exact,func_num,func_err,func_name,vpa,vperp) - @views heatmap(vperp.grid, vpa.grid, func_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string(func_name*"_num.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, func_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string(func_name*"_exact.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, func_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string(func_name*"_err.pdf") - savefig(outfile) - return nothing - end - - function print_test_data(func_exact,func_num,func_err,func_name) - @. func_err = abs(func_num - func_exact) - max_err = maximum(func_err) - println("maximum("*func_name*"_err): ",max_err) - return max_err - end - - function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy) - @. func_err = abs(func_num - func_exact) - max_err = maximum(func_err) - @. dummy = func_err^2 - # compute the numerator - num = get_density(dummy,vpa,vperp) - # compute the denominator - @. dummy = 1.0 - denom = get_density(dummy,vpa,vperp) - L2norm = sqrt(num/denom) - println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) - return max_err, L2norm - end - - mutable struct error_data - max::mk_float - L2::mk_float - end - - mutable struct moments_error_data - delta_density::mk_float - delta_upar::mk_float - delta_pressure::mk_float - end - - struct fkpl_error_data - C_M::error_data - H_M::error_data - dHdvpa_M::error_data - dHdvperp_M::error_data - G_M::error_data - dGdvperp_M::error_data - d2Gdvpa2_M::error_data - d2Gdvperpdvpa_M::error_data - d2Gdvperp2_M::error_data - moments::moments_error_data - end - - function allocate_error_data() - C_M = error_data(0.0,0.0) - H_M = error_data(0.0,0.0) - dHdvpa_M = error_data(0.0,0.0) - dHdvperp_M = error_data(0.0,0.0) - G_M = error_data(0.0,0.0) - dGdvperp_M = error_data(0.0,0.0) - d2Gdvpa2_M = error_data(0.0,0.0) - d2Gdvperpdvpa_M = error_data(0.0,0.0) - d2Gdvperp2_M = error_data(0.0,0.0) - moments = moments_error_data(0.0,0.0,0.0) - return fkpl_error_data(C_M,H_M,dHdvpa_M,dHdvperp_M, - G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvperp2_M, - moments) - end - - function test_weak_form_collisions(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0,plot_test_output=false, test_parallelism=false,test_self_operator=true, diff --git a/src/fokker_planck_test.jl b/src/fokker_planck_test.jl index 186a9dcbb..5e04efbb0 100644 --- a/src/fokker_planck_test.jl +++ b/src/fokker_planck_test.jl @@ -14,8 +14,14 @@ export H_Maxwellian, G_Maxwellian export Cssp_fully_expanded_form, calculate_collisional_fluxes +export print_test_data, plot_test_data, fkpl_error_data, allocate_error_data + +using Plots +using LaTeXStrings +using Measures using ..type_definitions: mk_float, mk_int using SpecialFunctions: erf +using ..velocity_moments: get_density # below are a series of functions that can be used to test the calculation # of the Rosenbluth potentials for a shifted Maxwellian # or provide an estimate for collisional coefficients @@ -266,4 +272,86 @@ function calculate_collisional_fluxes(F,dFdvpa,dFdvperp, return Cflux_vpa, Cflux_vperp end + +""" +Below are functions which are used for storing and printing data from the tests +""" + +function plot_test_data(func_exact,func_num,func_err,func_name,vpa,vperp) + @views heatmap(vperp.grid, vpa.grid, func_num[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_num.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, func_exact[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_exact.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, func_err[:,:], ylabel=L"v_{\|\|}", xlabel=L"v_{\perp}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string(func_name*"_err.pdf") + savefig(outfile) + return nothing +end + +function print_test_data(func_exact,func_num,func_err,func_name) + @. func_err = abs(func_num - func_exact) + max_err = maximum(func_err) + println("maximum("*func_name*"_err): ",max_err) + return max_err +end + +function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy) + @. func_err = abs(func_num - func_exact) + max_err = maximum(func_err) + @. dummy = func_err^2 + # compute the numerator + num = get_density(dummy,vpa,vperp) + # compute the denominator + @. dummy = 1.0 + denom = get_density(dummy,vpa,vperp) + L2norm = sqrt(num/denom) + println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) + return max_err, L2norm +end + +mutable struct error_data + max::mk_float + L2::mk_float +end + +mutable struct moments_error_data + delta_density::mk_float + delta_upar::mk_float + delta_pressure::mk_float +end + +struct fkpl_error_data + C_M::error_data + H_M::error_data + dHdvpa_M::error_data + dHdvperp_M::error_data + G_M::error_data + dGdvperp_M::error_data + d2Gdvpa2_M::error_data + d2Gdvperpdvpa_M::error_data + d2Gdvperp2_M::error_data + moments::moments_error_data +end + +function allocate_error_data() + C_M = error_data(0.0,0.0) + H_M = error_data(0.0,0.0) + dHdvpa_M = error_data(0.0,0.0) + dHdvperp_M = error_data(0.0,0.0) + G_M = error_data(0.0,0.0) + dGdvperp_M = error_data(0.0,0.0) + d2Gdvpa2_M = error_data(0.0,0.0) + d2Gdvperpdvpa_M = error_data(0.0,0.0) + d2Gdvperp2_M = error_data(0.0,0.0) + moments = moments_error_data(0.0,0.0,0.0) + return fkpl_error_data(C_M,H_M,dHdvpa_M,dHdvperp_M, + G_M,dGdvperp_M,d2Gdvpa2_M,d2Gdvperpdvpa_M,d2Gdvperp2_M, + moments) +end + end From 159e4d840cceb4bdbde86137e6d6fbac5264baf7 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 10:22:25 +0000 Subject: [PATCH 234/331] Creation of initial Fokker-Planck operator testing script for check-in tests. --- test/Project.toml | 1 + test/fokker_planck_tests.jl | 145 ++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 3 files changed, 147 insertions(+) create mode 100644 test/fokker_planck_tests.jl diff --git a/test/Project.toml b/test/Project.toml index 37165a783..fb1cb33d4 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -6,3 +6,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl new file mode 100644 index 000000000..32ca0f25f --- /dev/null +++ b/test/fokker_planck_tests.jl @@ -0,0 +1,145 @@ +module FokkerPlanckTests + +include("setup.jl") + + +using MPI +using moment_kinetics.fokker_planck_calculus: ravel_c_to_vpavperp!, ravel_vpavperp_to_c!, ravel_c_to_vpavperp_parallel! +using LinearAlgebra: mul! +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_float, allocate_shared_float +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.type_definitions: mk_float, mk_int + +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form +using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data + +function create_grids(ngrid,nelement_vpa,nelement_vperp; + Lvpa=12.0,Lvperp=6.0) + + nelement_local_vpa = nelement_vpa # number of elements per rank + nelement_global_vpa = nelement_local_vpa # total number of elements + nelement_local_vperp = nelement_vperp # number of elements per rank + nelement_global_vperp = nelement_local_vperp # total number of elements + bc = "" #not required to take a particular value, not used + # fd_option and adv_input not actually used so given values unimportant + #discretization = "chebyshev_pseudospectral" + discretization = "gausslegendre_pseudospectral" + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + element_spacing_option = "uniform" + vpa_input = grid_input("vpa", ngrid, nelement_global_vpa, nelement_local_vpa, + nrank, irank, Lvpa, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + vperp_input = grid_input("vperp", ngrid, nelement_global_vperp, nelement_local_vperp, + nrank, irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + # create the coordinate struct 'x' + #println("made inputs") + #println("vpa: ngrid: ",ngrid," nelement: ",nelement_local_vpa, " Lvpa: ",Lvpa) + #println("vperp: ngrid: ",ngrid," nelement: ",nelement_local_vperp, " Lvperp: ",Lvperp) + vpa, vpa_spectral = define_coordinate(vpa_input) + vperp, vperp_spectral = define_coordinate(vperp_input) + + # Set up MPI + initialize_comms!() + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + + return vpa, vpa_spectral, vperp, vperp_spectral +end + +function runtests() + @testset "Fokker Planck tests" verbose=use_verbose begin + println("Fokker Planck tests") + + @testset "weak-form 2D differentiation test" begin + # tests the correct definition of mass and stiffness matrices in 2D + ngrid = 9 + nelement_vpa = 8 + nelement_vperp = 4 + vpa, vpa_spectral, vperp, vperp_spectral = create_grids(ngrid,nelement_vpa,nelement_vperp, + Lvpa=2.0,Lvperp=1.0) + nc_global = vpa.n*vperp.n + begin_serial_region() + fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral, + precompute_weights=false) + KKpar2D_with_BC_terms_sparse = fkpl_arrays.KKpar2D_with_BC_terms_sparse + KKperp2D_with_BC_terms_sparse = fkpl_arrays.KKperp2D_with_BC_terms_sparse + lu_obj_MM = fkpl_arrays.lu_obj_MM + + dummy_array = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) + fvpavperp_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) + d2fvpavperp_dvperp2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) + fc = Array{mk_float,1}(undef,nc_global) + dfc = Array{mk_float,1}(undef,nc_global) + gc = Array{mk_float,1}(undef,nc_global) + dgc = Array{mk_float,1}(undef,nc_global) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvpa2_exact[ivpa,ivperp] = (4.0*vpa.grid[ivpa]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + d2fvpavperp_dvperp2_exact[ivpa,ivperp] = (4.0*vperp.grid[ivperp]^2 - 2.0)*exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) + end + end + + # fill fc with fvpavperp + ravel_vpavperp_to_c!(fc,fvpavperp,vpa.n,vperp.n) + ravel_c_to_vpavperp!(fvpavperp_test,fc,nc_global,vpa.n) + @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) + max_ravel_err = maximum(fvpavperp_err) + @serial_region begin + println("max(ravel_err)",max_ravel_err) + @test isapprox(max_ravel_err, 1.0e-15 ; atol = 1.0e-15) + end + #print_vector(fc,"fc",nc_global) + # multiply by KKpar2D and fill dfc + mul!(dfc,KKpar2D_with_BC_terms_sparse,fc) + mul!(dgc,KKperp2D_with_BC_terms_sparse,fc) + # invert mass matrix and fill fc + fc = lu_obj_MM \ dfc + gc = lu_obj_MM \ dgc + #print_vector(fc,"fc",nc_global) + # unravel + ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) + ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) + @serial_region begin + d2fvpavperp_dvpa2_max, d2fvpavperp_dvpa2_L2 = print_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fdvpa2",vpa,vperp,dummy_array) + @test isapprox(d2fvpavperp_dvpa2_max, 1.0e-7 ; atol=1.0e-7) + @test isapprox(d2fvpavperp_dvpa2_L2, 1.0e-8 ; atol=1.0e-8) + d2fvpavperp_dvperp2_max, d2fvpavperp_dvperp2_L2 = print_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fdvperp2",vpa,vperp,dummy_array) + @test isapprox(d2fvpavperp_dvperp2_max, 1.0e-7 ; atol=1.0e-7) + @test isapprox(d2fvpavperp_dvperp2_L2, 1.0e-8 ; atol=1.0e-8) + #if plot_test_output + # plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) + # plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) + #end + end + finalize_comms!() + end + + end +end + +end #FokkerPlanckTests + +using .FokkerPlanckTests + +FokkerPlanckTests.runtests() + diff --git a/test/runtests.jl b/test/runtests.jl index 4bab7c641..8ac45b6f2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,7 @@ function runtests() include(joinpath(@__DIR__, "Krook_collisions_tests.jl")) include(joinpath(@__DIR__, "harrisonthompson.jl")) include(joinpath(@__DIR__, "wall_bc_tests.jl")) + include(joinpath(@__DIR__, "fokker_planck_tests.jl")) end end From 3ad30231eaa39c0da49fc9929d92708141d9784a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 11:13:22 +0000 Subject: [PATCH 235/331] Remove function "explicit_fokker_planck_collisions_weak_form_opt!". This function attempted to improve the accuracy of the collision operator to ensure numerical stability, by evaluating the collision operator only on the delta F = F - F_Maxwellian. This splitting did not transpire to be useful (numerical stability appears to have been acheived without this feature), and has the downside of 3 times the cost due to the three collision operator evaluations. --- src/fokker_planck.jl | 273 ++++++++++--------------------------------- src/time_advance.jl | 4 - 2 files changed, 64 insertions(+), 213 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 5f0c3ddea..d96d2cecb 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -13,7 +13,6 @@ export get_local_Cssp_coefficients!, init_fokker_planck_collisions # testing export symmetric_matrix_inverse export fokker_planck_collision_operator_weak_form! -export explicit_fokker_planck_collisions_weak_form_opt! using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature @@ -227,147 +226,6 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos return nothing end -""" -Function for advancing with the explicit, weak-form, self-collision operator -using the optimized method where we only differentiate F - F_Maxwellian -""" - -function explicit_fokker_planck_collisions_weak_form_opt!(pdf_out,pdf_in,dSdt,composition,collisions,dt, - fkpl_arrays::fokkerplanck_weakform_arrays_struct, - r, z, vperp, vpa, vperp_spectral, vpa_spectral, scratch_dummy; - test_assembly_serial=false,impose_zero_gradient_BC=false, - diagnose_entropy_production=false) - # N.B. only self-collisions are currently supported - # This can be modified by adding a loop over s' below - n_ion_species = composition.n_ion_species - @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) - @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) - @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) - @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) - @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) - @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) - @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) - @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) - @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(dSdt,1) || throw(BoundsError(dSdt)) - @boundscheck r.n == size(dSdt,2) || throw(BoundsError(dSdt)) - @boundscheck n_ion_species == size(dSdt,3) || throw(BoundsError(dSdt)) - - # masses and collision frequencies - ms, msp = 1.0, 1.0 # generalise! - nussp = collisions.nuii # generalise! - Css = scratch_dummy.buffer_vpavperp_1 - delta_Fs = scratch_dummy.buffer_vpavperp_2 - Fs_M = scratch_dummy.buffer_vpavperp_3 - dummy_vpavperp = scratch_dummy.dummy_vpavperp - # N.B. parallelisation is only over vpa vperp - # ensure s, r, z are local before initiating the s, r, z loop - begin_vperp_vpa_region() - @loop_s_r_z is ir iz begin - dens = get_density(@view(pdf_in[:,:,iz,ir,is]),vpa,vperp) - upar = get_upar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, dens) - ppar = get_ppar(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp, upar) - pperp = get_pperp(@view(pdf_in[:,:,iz,ir,is]), vpa, vperp) - pressure = get_pressure(ppar,pperp) - vth = sqrt(2.0*pressure/dens) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - Fs_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - delta_Fs[ivpa,ivperp] = pdf_in[ivpa,ivperp,iz,ir,is] - Fs_M[ivpa,ivperp] - end - # the functions within this loop will call - # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - # first argument is Fs, and second argument is Fs' in C[Fs,Fs'] - fokker_planck_collision_operator_weak_form!(delta_Fs,delta_Fs,ms,msp,nussp, - fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, - impose_zero_gradient_BC=true) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - Css[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] - end - fokker_planck_collision_operator_weak_form!(@view(pdf_in[:,:,iz,ir,is]),delta_Fs,ms,msp,nussp, - fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, - impose_zero_gradient_BC=true, - use_Maxwellian_Rosenbluth_coefficients=false, - use_Maxwellian_field_particle_distribution=true, - skip_Rosenbluth_potential_calculation=true) - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - Css[ivpa,ivperp] += fkpl_arrays.CC[ivpa,ivperp] - end - fokker_planck_collision_operator_weak_form!(delta_Fs,@view(pdf_in[:,:,iz,ir,is]),ms,msp,nussp, - fkpl_arrays,vperp,vpa,vperp_spectral,vpa_spectral, - impose_zero_gradient_BC=true, - use_Maxwellian_Rosenbluth_coefficients=true, - use_Maxwellian_field_particle_distribution=false) - # advance this part of s,r,z with the resulting C[Fs,Fs] - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - Css[ivpa,ivperp] += fkpl_arrays.CC[ivpa,ivperp] - pdf_out[ivpa,ivperp,iz,ir,is] += dt*Css[ivpa,ivperp] - end - if diagnose_entropy_production - # assign dummy array - lnfC = fkpl_arrays.rhsvpavperp - @loop_vperp_vpa ivperp ivpa begin - lnfC[ivpa,ivperp] = log(abs(pdf_in[ivpa,ivperp,iz,ir,is]) + 1.0e-15)*Css[ivpa,ivperp] - end - begin_serial_region() - @serial_region begin - dSdt[iz,ir,is] = -get_density(lnfC,vpa,vperp) - end - end - if collisions.numerical_conserving_terms == "density+u||+T" - # use an ad-hoc numerical model to conserve density, upar, vth - # a different model is required for inter-species collisions - # simply conserving particle density may be more appropriate in the multi-species case - begin_serial_region() - n_in = dens - upar_in = upar - pressure_in = pressure - - n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - upar_out = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, n_out) - ppar_out = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out) - pperp_out = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - pressure_out = get_pressure(ppar_out,pperp_out) - qpar_out = get_qpar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, dummy_vpavperp) - rmom_out = get_rmom(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, dummy_vpavperp) - - # form the appropriate matrix coefficients - b0, b1, b2 = n_in, n_in*(upar_in - upar_out), (3.0)*(pressure_in) + n_in*(upar_in - upar_out)^2 - A00, A02, A11, A12, A22 = n_out, (3.0)*(pressure_out), ppar_out, qpar_out, rmom_out - - # obtain the coefficients for the corrections - (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) - - # fill with the corrected pdf - @loop_vperp_vpa ivperp ivpa begin - wpar = vpa.grid[ivpa] - upar_out - pdf_out[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp,iz,ir,is] - end - elseif collisions.numerical_conserving_terms == "density" - # get density moment of incoming and outgoing distribution functions - n_in = dens - - n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - - # obtain the coefficient for the corrections - x0 = n_in/n_out - - # update pdf_out with the corrections - @loop_vperp_vpa ivperp ivpa begin - pdf_out[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp,iz,ir,is] - end - elseif !(collisions.numerical_conserving_terms == "none") - println("ERROR: collisions.numerical_conserving_terms = ",collisions.numerical_conserving_terms," NOT SUPPORTED") - end - - end - return nothing -end - """ Function for evaluating Css' = Css'[Fs,Fs'] """ @@ -377,7 +235,6 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp test_assembly_serial=false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, - skip_Rosenbluth_potential_calculation=false, algebraic_solve_for_d2Gdvperp2 = false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @@ -423,81 +280,79 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp dFdvpa = fkpl_arrays.dFdvpa dFdvperp = fkpl_arrays.dFdvperp - if !skip_Rosenbluth_potential_calculation - if use_Maxwellian_Rosenbluth_coefficients - begin_serial_region() - dens = get_density(ffsp_in,vpa,vperp) - upar = get_upar(ffsp_in, vpa, vperp, dens) - ppar = get_ppar(ffsp_in, vpa, vperp, upar) - pperp = get_pperp(ffsp_in, vpa, vperp) - pressure = get_pressure(ppar,pperp) - vth = sqrt(2.0*pressure/dens) + if use_Maxwellian_Rosenbluth_coefficients + begin_serial_region() + dens = get_density(ffsp_in,vpa,vperp) + upar = get_upar(ffsp_in, vpa, vperp, dens) + ppar = get_ppar(ffsp_in, vpa, vperp, upar) + pperp = get_pperp(ffsp_in, vpa, vperp) + pressure = get_pressure(ppar,pperp) + vth = sqrt(2.0*pressure/dens) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + HH[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + else + # the functions within this loop will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays + # calculate the boundary data + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) + # carry out the elliptic solves required + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] + end + elliptic_solve!(HH,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + + end + #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + if algebraic_solve_for_d2Gdvperp2 begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin - HH[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvpa2[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperp2[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dGdvperp[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] end + # use the algebraic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) else - # the functions within this loop will call - # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) - # carry out the elliptic solves required - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] - end - elliptic_solve!(HH,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - + # solve a weak-form PDE for d2Gdvperp2 begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - + Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] end - #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - - if algebraic_solve_for_d2Gdvperp2 - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] - end - # use the algebraic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - else - # solve a weak-form PDE for d2Gdvperp2 - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] - end - elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - end - begin_serial_region() + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) end + begin_serial_region() end # assemble the RHS of the collision operator matrix eq if use_Maxwellian_field_particle_distribution diff --git a/src/time_advance.jl b/src/time_advance.jl index 30ebb0ae7..c3cf95a40 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -55,7 +55,6 @@ using ..energy_equation: energy_equation!, neutral_energy_equation! using ..em_fields: setup_em_fields, update_phi! using ..fokker_planck: init_fokker_planck_collisions_weak_form, init_fokker_planck_collisions, explicit_fokker_planck_collisions! using ..fokker_planck: explicit_fokker_planck_collisions_weak_form!, explicit_fokker_planck_collisions_Maxwellian_coefficients! -using ..fokker_planck: explicit_fokker_planck_collisions_weak_form_opt! using ..manufactured_solns: manufactured_sources using ..advection: advection_info @debug_detect_redundant_block_synchronize using ..communication: debug_detect_redundant_is_active @@ -1819,9 +1818,6 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition,collisions,dt, fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, diagnose_entropy_production = update_entropy_diagnostic) - #explicit_fokker_planck_collisions_weak_form_opt!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition,collisions,dt, - # fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, - # diagnose_entropy_production = update_entropy_diagnostic) end if advance.explicit_fp_F_FM_collisions explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, From 21ae7f2c8801e074fcf67335d5bc98947770f731 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 11:49:33 +0000 Subject: [PATCH 236/331] Refactor calculation of Rosenbluth potentials to use a lower-level function to improve readability and testability of code. --- src/fokker_planck.jl | 87 ++++------------------------------- src/fokker_planck_calculus.jl | 87 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 77 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index d96d2cecb..ec5f7ac55 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -39,6 +39,7 @@ using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_paralle using ..fokker_planck_calculus: calculate_YY_arrays, enforce_vpavperp_BCs! using ..fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data! using ..fokker_planck_calculus: enforce_zero_bc!, elliptic_solve!, algebraic_solve!, ravel_c_to_vpavperp_parallel! +using ..fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_solve! using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes, H_Maxwellian, dGdvperp_Maxwellian using ..fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian, dHdvpa_Maxwellian, dHdvperp_Maxwellian using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian @@ -242,31 +243,11 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp @boundscheck vperp.n == size(ffs_in,2) || throw(BoundsError(ffs_in)) # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays - MM2D_sparse = fkpl_arrays.MM2D_sparse - KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse - KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse - LP2D_sparse = fkpl_arrays.LP2D_sparse - LV2D_sparse = fkpl_arrays.LV2D_sparse - PUperp2D_sparse = fkpl_arrays.PUperp2D_sparse - PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse - PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse - MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse - KPperp2D_sparse = fkpl_arrays.KPperp2D_sparse - lu_obj_MM = fkpl_arrays.lu_obj_MM - lu_obj_LP = fkpl_arrays.lu_obj_LP - lu_obj_LV = fkpl_arrays.lu_obj_LV - lu_obj_LB = fkpl_arrays.lu_obj_LB - - S_dummy = fkpl_arrays.S_dummy - Q_dummy = fkpl_arrays.Q_dummy rhsc = fkpl_arrays.rhsc - rhqc = fkpl_arrays.rhqc sc = fkpl_arrays.sc - qc = fkpl_arrays.qc rhsvpavperp = fkpl_arrays.rhsvpavperp + lu_obj_MM = fkpl_arrays.lu_obj_MM YY_arrays = fkpl_arrays.YY_arrays - bwgt = fkpl_arrays.bwgt - rpbd = fkpl_arrays.rpbd CC = fkpl_arrays.CC HH = fkpl_arrays.HH @@ -282,10 +263,10 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp if use_Maxwellian_Rosenbluth_coefficients begin_serial_region() - dens = get_density(ffsp_in,vpa,vperp) - upar = get_upar(ffsp_in, vpa, vperp, dens) - ppar = get_ppar(ffsp_in, vpa, vperp, upar) - pperp = get_pperp(ffsp_in, vpa, vperp) + dens = get_density(@view(ffsp_in[:,:]),vpa,vperp) + upar = get_upar(@view(ffsp_in[:,:]), vpa, vperp, dens) + ppar = get_ppar(@view(ffsp_in[:,:]), vpa, vperp, upar) + pperp = get_pperp(@view(ffsp_in[:,:]), vpa, vperp) pressure = get_pressure(ppar,pperp) vth = sqrt(2.0*pressure/dens) begin_vperp_vpa_region() @@ -301,58 +282,10 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp else # the functions within this loop will call # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) - # carry out the elliptic solves required - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] - end - elliptic_solve!(HH,S_dummy,rpbd.H_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, - lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - - end - #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, - lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, - lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) - - if algebraic_solve_for_d2Gdvperp2 - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] - end - # use the algebraic solve function to find - # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp - # using a weak form - algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - else - # solve a weak-form PDE for d2Gdvperp2 - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] - end - elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, - lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, - rhsc,rhqc,sc,qc,vpa,vperp) - end - begin_serial_region() + calculate_rosenbluth_potentials_via_elliptic_solve!(HH,dHdvpa,dHdvperp, + d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,@view(ffsp_in[:,:]), + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays, + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) end # assemble the RHS of the collision operator matrix eq if use_Maxwellian_field_particle_distribution diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index af3445376..5677a2bd8 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -18,6 +18,7 @@ export elliptic_solve!, algebraic_solve! export fokkerplanck_arrays_struct export fokkerplanck_weakform_arrays_struct export enforce_vpavperp_BCs! +export calculate_rosenbluth_potentials_via_elliptic_solve! # testing export calculate_rosenbluth_potential_boundary_data_exact! @@ -2068,6 +2069,92 @@ function algebraic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_bound return nothing end +function calculate_rosenbluth_potentials_via_elliptic_solve!(HH,dHdvpa,dHdvperp, + d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; + algebraic_solve_for_d2Gdvperp2=false) + + # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays + MM2D_sparse = fkpl_arrays.MM2D_sparse + KKpar2D_sparse = fkpl_arrays.KKpar2D_sparse + KKperp2D_sparse = fkpl_arrays.KKperp2D_sparse + LP2D_sparse = fkpl_arrays.LP2D_sparse + LV2D_sparse = fkpl_arrays.LV2D_sparse + PUperp2D_sparse = fkpl_arrays.PUperp2D_sparse + PPparPUperp2D_sparse = fkpl_arrays.PPparPUperp2D_sparse + PPpar2D_sparse = fkpl_arrays.PPpar2D_sparse + MMparMNperp2D_sparse = fkpl_arrays.MMparMNperp2D_sparse + KPperp2D_sparse = fkpl_arrays.KPperp2D_sparse + lu_obj_MM = fkpl_arrays.lu_obj_MM + lu_obj_LP = fkpl_arrays.lu_obj_LP + lu_obj_LV = fkpl_arrays.lu_obj_LV + lu_obj_LB = fkpl_arrays.lu_obj_LB + + bwgt = fkpl_arrays.bwgt + rpbd = fkpl_arrays.rpbd + + S_dummy = fkpl_arrays.S_dummy + Q_dummy = fkpl_arrays.Q_dummy + rhsc = fkpl_arrays.rhsc + rhqc = fkpl_arrays.rhqc + sc = fkpl_arrays.sc + qc = fkpl_arrays.qc + + # calculate the boundary data + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) + # carry out the elliptic solves required + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = -(4.0/sqrt(pi))*ffsp_in[ivpa,ivperp] + end + elliptic_solve!(HH,S_dummy,rpbd.H_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvpa,S_dummy,rpbd.dHdvpa_data, + lu_obj_LP,PPpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dHdvperp,S_dummy,rpbd.dHdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + + end + #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, + # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, + lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, + lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) + + if algebraic_solve_for_d2Gdvperp2 + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] - d2Gdvpa2[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = -dGdvperp[ivpa,ivperp] + end + # use the algebraic solve function to find + # d2Gdvperp2 = 2H - d2Gdvpa2 - (1/vperp)dGdvperp + # using a weak form + algebraic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_MM,MM2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + else + # solve a weak-form PDE for d2Gdvperp2 + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] + Q_dummy[ivpa,ivperp] = 2.0*d2Gdvpa2[ivpa,ivperp] + end + elliptic_solve!(d2Gdvperp2,S_dummy,Q_dummy,rpbd.d2Gdvperp2_data, + lu_obj_LB,KPperp2D_sparse,MMparMNperp2D_sparse, + rhsc,rhqc,sc,qc,vpa,vperp) + end + begin_serial_region() + return nothing +end + """ function to enforce boundary conditions on the collision operator result to be consistent with the boundary conditions imposed on the the pdf From 814b11b42760516e5b641fb9b1b10ec2504312fe Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 11:58:55 +0000 Subject: [PATCH 237/331] Re-incorporate calculation of the potential G into the base-level functions for easy testing. --- 2D_FEM_assembly_test.jl | 11 +---------- src/fokker_planck.jl | 10 ++++++---- src/fokker_planck_calculus.jl | 7 ++++--- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 1063efad1..7aae9cf42 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -268,6 +268,7 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + G_M_num[ivpa,ivperp] = fkpl_arrays.GG[ivpa,ivperp] H_M_num[ivpa,ivperp] = fkpl_arrays.HH[ivpa,ivperp] dHdvpa_M_num[ivpa,ivperp] = fkpl_arrays.dHdvpa[ivpa,ivperp] dHdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dHdvperp[ivpa,ivperp] @@ -277,16 +278,6 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary d2Gdvperpdvpa_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperpdvpa[ivpa,ivperp] end - S_dummy = fkpl_arrays.S_dummy - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - S_dummy[ivpa,ivperp] = 2.0*H_M_num[ivpa,ivperp] - end - # solve for G as an added test bonus - elliptic_solve!(G_M_num,S_dummy,fkpl_arrays.rpbd.G_data, - fkpl_arrays.lu_obj_LP,fkpl_arrays.MM2D_sparse,fkpl_arrays.rhsc, - fkpl_arrays.sc,vpa,vperp) - init_time = Dates.value(finish_init_time - start_init_time) calculate_time = Dates.value(now() - finish_init_time) begin_serial_region() diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index ec5f7ac55..b247920b1 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -139,6 +139,7 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp qc = allocate_shared_float(nc) CC = allocate_shared_float(nvpa,nvperp) + GG = allocate_shared_float(nvpa,nvperp) HH = allocate_shared_float(nvpa,nvperp) dHdvpa = allocate_shared_float(nvpa,nvperp) dHdvperp = allocate_shared_float(nvpa,nvperp) @@ -157,7 +158,7 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp PPpar2D_sparse,MMparMNperp2D_sparse,KPperp2D_sparse, lu_obj_MM,lu_obj_LP,lu_obj_LV,lu_obj_LB, YY_arrays, S_dummy, Q_dummy, rhsvpavperp, rhsc, rhqc, sc, qc, - CC, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa, + CC, GG, HH, dHdvpa, dHdvperp, dGdvperp, d2Gdvperp2, d2Gdvpa2, d2Gdvperpdvpa, FF, dFdvpa, dFdvperp) return fka end @@ -241,6 +242,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @boundscheck vperp.n == size(ffs_in,2) || throw(BoundsError(ffs_in)) + # the functions within this function will call + # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays rhsc = fkpl_arrays.rhsc @@ -250,6 +253,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp YY_arrays = fkpl_arrays.YY_arrays CC = fkpl_arrays.CC + GG = fkpl_arrays.GG HH = fkpl_arrays.HH dHdvpa = fkpl_arrays.dHdvpa dHdvperp = fkpl_arrays.dHdvperp @@ -280,9 +284,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) end else - # the functions within this loop will call - # begin_vpa_region(), begin_vperp_region(), begin_vperp_vpa_region(), begin_serial_region() to synchronise the shared-memory arrays - calculate_rosenbluth_potentials_via_elliptic_solve!(HH,dHdvpa,dHdvperp, + calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,@view(ffsp_in[:,:]), vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays, algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 5677a2bd8..4bdef6fb9 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -201,6 +201,7 @@ struct fokkerplanck_weakform_arrays_struct{N} # dummy array for the result of the calculation CC::MPISharedArray{mk_float,2} # dummy arrays for storing Rosenbluth potentials + GG::MPISharedArray{mk_float,2} HH::MPISharedArray{mk_float,2} dHdvpa::MPISharedArray{mk_float,2} dHdvperp::MPISharedArray{mk_float,2} @@ -2069,7 +2070,7 @@ function algebraic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_bound return nothing end -function calculate_rosenbluth_potentials_via_elliptic_solve!(HH,dHdvpa,dHdvperp, +function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; algebraic_solve_for_d2Gdvperp2=false) @@ -2119,8 +2120,8 @@ function calculate_rosenbluth_potentials_via_elliptic_solve!(HH,dHdvpa,dHdvperp, S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] end - #elliptic_solve!(G_M_num,S_dummy,rpbd.G_data, - # lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + elliptic_solve!(GG,S_dummy,rpbd.G_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, From 25850d6c0f5d09282653641cf88b7156e2aacb17 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 13:13:40 +0000 Subject: [PATCH 238/331] Add the option to avoid computing the potentials G and dGdvperp when they are not required. Add the tests of the Rosenbluth potentials and the boundary data to the check-in tests. --- 2D_FEM_assembly_test.jl | 12 ++- src/fokker_planck.jl | 7 +- src/fokker_planck_calculus.jl | 65 ++++++------ test/fokker_planck_tests.jl | 180 +++++++++++++++++++++++++++++++--- 4 files changed, 216 insertions(+), 48 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 7aae9cf42..58abcd1d4 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -34,6 +34,7 @@ using moment_kinetics.fokker_planck_calculus: elliptic_solve!, ravel_c_to_vpavpe using moment_kinetics.fokker_planck_calculus: enforce_zero_bc!, allocate_rosenbluth_potential_boundary_data using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potential_boundary_data!, calculate_rosenbluth_potential_boundary_data_exact! using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data, enforce_vpavperp_BCs! +using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_solve! @@ -256,13 +257,19 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary test_assembly_serial=test_parallelism, use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, - algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2, + calculate_GG = false, calculate_dGdvperp=false) if test_numerical_conserving_terms && test_self_operator # enforce the boundary conditions on CC before it is used for timestepping enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) # make ad-hoc conserving corrections conserving_corrections!(fkpl_arrays.CC,Fs_M,vpa,vperp,dummy_vpavperp) end + # calculate Rosenbluth potentials again as a standalone to G and dGdvperp + calculate_rosenbluth_potentials_via_elliptic_solve!(fkpl_arrays.GG,fkpl_arrays.HH,fkpl_arrays.dHdvpa,fkpl_arrays.dHdvperp, + fkpl_arrays.d2Gdvpa2,fkpl_arrays.dGdvperp,fkpl_arrays.d2Gdvperpdvpa,fkpl_arrays.d2Gdvperp2,F_M, + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; + algebraic_solve_for_d2Gdvperp2=false,calculate_GG=true,calculate_dGdvperp=true) # extract C[Fs,Fs'] result # and Rosenbluth potentials for testing begin_vperp_vpa_region() @@ -287,7 +294,8 @@ using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary # test the boundary data calculation if !use_Maxwellian_Rosenbluth_coefficients - test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) + max_H_err, max_dHdvpa_err, max_dHdvperp_err, max_G_err, max_dGdvperp_err, + max_d2Gdvperp2_err, max_d2Gdvperpdvpa_err, max_d2Gdvpa2_err = test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) end dummy_array = Array{mk_float,2}(undef,vpa.n,vperp.n) fkerr.H_M.max, fkerr.H_M.L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index b247920b1..b57008803 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -237,7 +237,9 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp test_assembly_serial=false, use_Maxwellian_Rosenbluth_coefficients=false, use_Maxwellian_field_particle_distribution=false, - algebraic_solve_for_d2Gdvperp2 = false) + algebraic_solve_for_d2Gdvperp2 = false, + calculate_GG=false, + calculate_dGdvperp=false) @boundscheck vpa.n == size(ffsp_in,1) || throw(BoundsError(ffsp_in)) @boundscheck vperp.n == size(ffsp_in,2) || throw(BoundsError(ffsp_in)) @boundscheck vpa.n == size(ffs_in,1) || throw(BoundsError(ffs_in)) @@ -287,7 +289,8 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,@view(ffsp_in[:,:]), vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays, - algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2) + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2, + calculate_GG=calculate_GG,calculate_dGdvperp=calculate_dGdvperp) end # assemble the RHS of the collision operator matrix eq if use_Maxwellian_field_particle_distribution diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 4bdef6fb9..bdc3f6f4f 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1096,7 +1096,8 @@ function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, end function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, - fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral) + fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral; + calculate_GG=false,calculate_dGdvperp=false) # get derivatives of pdf dfdvperp = fkpl.dfdvperp dfdvpa = fkpl.dfdvpa @@ -1121,8 +1122,12 @@ function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potentia calculate_boundary_data!(rpbd.H_data,fkpl.H0_weights,pdf,vpa,vperp) calculate_boundary_data!(rpbd.dHdvpa_data,fkpl.H0_weights,dfdvpa,vpa,vperp) calculate_boundary_data!(rpbd.dHdvperp_data,fkpl.H1_weights,dfdvperp,vpa,vperp) - calculate_boundary_data!(rpbd.G_data,fkpl.G0_weights,pdf,vpa,vperp) - calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) + if calculate_GG + calculate_boundary_data!(rpbd.G_data,fkpl.G0_weights,pdf,vpa,vperp) + end + if calculate_dGdvperp + calculate_boundary_data!(rpbd.dGdvperp_data,fkpl.G1_weights,dfdvperp,vpa,vperp) + end calculate_boundary_data!(rpbd.d2Gdvperp2_data,fkpl.H2_weights,dfdvperp,vpa,vperp) calculate_boundary_data!(rpbd.d2Gdvperpdvpa_data,fkpl.G1_weights,d2fdvperpdvpa,vpa,vperp) calculate_boundary_data!(rpbd.d2Gdvpa2_data,fkpl.H3_weights,dfdvpa,vpa,vperp) @@ -1136,16 +1141,16 @@ function test_rosenbluth_potential_boundary_data(rpbd::rosenbluth_potential_boun error_buffer_vpa = Array{mk_float,1}(undef,vpa.n) error_buffer_vperp_1 = Array{mk_float,1}(undef,vperp.n) error_buffer_vperp_2 = Array{mk_float,1}(undef,vperp.n) - test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_H_err = test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_dHdvpa_err = test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_dHdvperp_err = test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_G_err = test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_dGdvperp_err = test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_d2Gdvperp2_err = test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_d2Gdvperpdvpa_err = test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_d2Gdvpa2_err = test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - return nothing + return max_H_err, max_dHdvpa_err, max_dHdvperp_err, max_G_err, max_dGdvperp_err, max_d2Gdvperp2_err, max_d2Gdvperpdvpa_err, max_d2Gdvpa2_err end function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2) @@ -1158,16 +1163,15 @@ function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffe for ivpa in 1:nvpa buffer_vpa = abs(func.upper_boundary_vperp[ivpa] - func_exact.upper_boundary_vperp[ivpa]) end - @serial_region begin - max_lower_vpa_err = maximum(buffer_vperp_1) - max_upper_vpa_err = maximum(buffer_vperp_2) - max_upper_vperp_err = maximum(buffer_vpa) - println(string(func_name*" boundary data:")) - println("max(lower_vpa_err) = ",max_lower_vpa_err) - println("max(upper_vpa_err) = ",max_upper_vpa_err) - println("max(upper_vperp_err) = ",max_upper_vperp_err) - end - return nothing + max_lower_vpa_err = maximum(buffer_vperp_1) + max_upper_vpa_err = maximum(buffer_vperp_2) + max_upper_vperp_err = maximum(buffer_vpa) + println(string(func_name*" boundary data:")) + println("max(lower_vpa_err) = ",max_lower_vpa_err) + println("max(upper_vpa_err) = ",max_upper_vpa_err) + println("max(upper_vperp_err) = ",max_upper_vperp_err) + max_err = max(max_lower_vpa_err,max_upper_vpa_err,max_upper_vperp_err) + return max_err end function get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) @@ -2073,7 +2077,7 @@ end function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; - algebraic_solve_for_d2Gdvperp2=false) + algebraic_solve_for_d2Gdvperp2=false,calculate_GG=false,calculate_dGdvperp=false) # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays MM2D_sparse = fkpl_arrays.MM2D_sparse @@ -2102,7 +2106,8 @@ function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvpe qc = fkpl_arrays.qc # calculate the boundary data - calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral) + calculate_rosenbluth_potential_boundary_data!(rpbd,bwgt,@view(ffsp_in[:,:]),vpa,vperp,vpa_spectral,vperp_spectral, + calculate_GG=calculate_GG,calculate_dGdvperp=(calculate_dGdvperp||algebraic_solve_for_d2Gdvperp2)) # carry out the elliptic solves required begin_vperp_vpa_region() @loop_vperp_vpa ivperp ivpa begin @@ -2120,12 +2125,16 @@ function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvpe S_dummy[ivpa,ivperp] = 2.0*HH[ivpa,ivperp] end - elliptic_solve!(GG,S_dummy,rpbd.G_data, - lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + if calculate_GG + elliptic_solve!(GG,S_dummy,rpbd.G_data, + lu_obj_LP,MM2D_sparse,rhsc,sc,vpa,vperp) + end + if calculate_dGdvperp || algebraic_solve_for_d2Gdvperp2 + elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, + lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) + end elliptic_solve!(d2Gdvpa2,S_dummy,rpbd.d2Gdvpa2_data, lu_obj_LP,KKpar2D_sparse,rhsc,sc,vpa,vperp) - elliptic_solve!(dGdvperp,S_dummy,rpbd.dGdvperp_data, - lu_obj_LV,PUperp2D_sparse,rhsc,sc,vpa,vperp) elliptic_solve!(d2Gdvperpdvpa,S_dummy,rpbd.d2Gdvperpdvpa_data, lu_obj_LV,PPparPUperp2D_sparse,rhsc,sc,vpa,vperp) diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index 32ca0f25f..3129ff8ca 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -15,7 +15,11 @@ using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data - +using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian +using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian +using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian +using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_solve!, calculate_rosenbluth_potential_boundary_data_exact! +using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data, allocate_rosenbluth_potential_boundary_data function create_grids(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0) @@ -62,7 +66,7 @@ function runtests() @testset "Fokker Planck tests" verbose=use_verbose begin println("Fokker Planck tests") - @testset "weak-form 2D differentiation test" begin + @testset " - test weak-form 2D differentiation" begin # tests the correct definition of mass and stiffness matrices in 2D ngrid = 9 nelement_vpa = 8 @@ -77,20 +81,20 @@ function runtests() KKperp2D_with_BC_terms_sparse = fkpl_arrays.KKperp2D_with_BC_terms_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM - dummy_array = Array{mk_float,2}(undef,vpa.n,vperp.n) - fvpavperp = Array{mk_float,2}(undef,vpa.n,vperp.n) - fvpavperp_test = Array{mk_float,2}(undef,vpa.n,vperp.n) - fvpavperp_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvpa2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_exact = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_err = Array{mk_float,2}(undef,vpa.n,vperp.n) - d2fvpavperp_dvperp2_num = Array{mk_float,2}(undef,vpa.n,vperp.n) - fc = Array{mk_float,1}(undef,nc_global) - dfc = Array{mk_float,1}(undef,nc_global) - gc = Array{mk_float,1}(undef,nc_global) - dgc = Array{mk_float,1}(undef,nc_global) + dummy_array = allocate_float(vpa.n,vperp.n) + fvpavperp = allocate_float(vpa.n,vperp.n) + fvpavperp_test = allocate_float(vpa.n,vperp.n) + fvpavperp_err = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvpa2_exact = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvpa2_err = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvpa2_num = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvperp2_exact = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvperp2_err = allocate_float(vpa.n,vperp.n) + d2fvpavperp_dvperp2_num = allocate_float(vpa.n,vperp.n) + fc = allocate_float(nc_global) + dfc = allocate_float(nc_global) + gc = allocate_float(nc_global) + dgc = allocate_float(nc_global) for ivperp in 1:vperp.n for ivpa in 1:vpa.n fvpavperp[ivpa,ivperp] = exp(-vpa.grid[ivpa]^2 - vperp.grid[ivperp]^2) @@ -134,6 +138,150 @@ function runtests() finalize_comms!() end + @testset " - test weak-form Rosenbluth potential calculation" begin + ngrid = 9 + nelement_vpa = 8 + nelement_vperp = 4 + vpa, vpa_spectral, vperp, vperp_spectral = create_grids(ngrid,nelement_vpa,nelement_vperp, + Lvpa=12.0,Lvperp=6.0) + nc_global = vpa.n*vperp.n + begin_serial_region() + fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral, + precompute_weights=true) + dummy_array = allocate_float(vpa.n,vperp.n) + F_M = allocate_float(vpa.n,vperp.n) + H_M_exact = allocate_float(vpa.n,vperp.n) + H_M_num = allocate_shared_float(vpa.n,vperp.n) + H_M_err = allocate_float(vpa.n,vperp.n) + G_M_exact = allocate_float(vpa.n,vperp.n) + G_M_num = allocate_shared_float(vpa.n,vperp.n) + G_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvpa2_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvpa2_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvpa2_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvperp2_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvperp2_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvperp2_M_err = allocate_float(vpa.n,vperp.n) + dGdvperp_M_exact = allocate_float(vpa.n,vperp.n) + dGdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) + dGdvperp_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_err = allocate_float(vpa.n,vperp.n) + dHdvpa_M_exact = allocate_float(vpa.n,vperp.n) + dHdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) + dHdvpa_M_err = allocate_float(vpa.n,vperp.n) + dHdvperp_M_exact = allocate_float(vpa.n,vperp.n) + dHdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) + dHdvperp_M_err = allocate_float(vpa.n,vperp.n) + + dens, upar, vth = 1.0, 1.0, 1.0 + begin_serial_region() + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + end + rpbd_exact = allocate_rosenbluth_potential_boundary_data(vpa,vperp) + # use known test function to provide exact data + calculate_rosenbluth_potential_boundary_data_exact!(rpbd_exact, + H_M_exact,dHdvpa_M_exact,dHdvperp_M_exact,G_M_exact, + dGdvperp_M_exact,d2Gdvperp2_M_exact, + d2Gdvperpdvpa_M_exact,d2Gdvpa2_M_exact,vpa,vperp) + # calculate the potentials numerically + calculate_rosenbluth_potentials_via_elliptic_solve!(fkpl_arrays.GG,fkpl_arrays.HH,fkpl_arrays.dHdvpa,fkpl_arrays.dHdvperp, + fkpl_arrays.d2Gdvpa2,fkpl_arrays.dGdvperp,fkpl_arrays.d2Gdvperpdvpa,fkpl_arrays.d2Gdvperp2,F_M, + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; + algebraic_solve_for_d2Gdvperp2=false,calculate_GG=true,calculate_dGdvperp=true) + # extract C[Fs,Fs'] result + # and Rosenbluth potentials for testing + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + G_M_num[ivpa,ivperp] = fkpl_arrays.GG[ivpa,ivperp] + H_M_num[ivpa,ivperp] = fkpl_arrays.HH[ivpa,ivperp] + dHdvpa_M_num[ivpa,ivperp] = fkpl_arrays.dHdvpa[ivpa,ivperp] + dHdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dHdvperp[ivpa,ivperp] + dGdvperp_M_num[ivpa,ivperp] = fkpl_arrays.dGdvperp[ivpa,ivperp] + d2Gdvperp2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperp2[ivpa,ivperp] + d2Gdvpa2_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvpa2[ivpa,ivperp] + d2Gdvperpdvpa_M_num[ivpa,ivperp] = fkpl_arrays.d2Gdvperpdvpa[ivpa,ivperp] + end + begin_serial_region() + @serial_region begin + # test the boundary data + max_H_boundary_data_err, max_dHdvpa_boundary_data_err, + max_dHdvperp_boundary_data_err, max_G_boundary_data_err, + max_dGdvperp_boundary_data_err, max_d2Gdvperp2_boundary_data_err, + max_d2Gdvperpdvpa_boundary_data_err, max_d2Gdvpa2_boundary_data_err = test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) + rtol_max, atol_max = 2.0e-13, 2.0e-13 + @test isapprox(max_H_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 2.0e-12, 2.0e-12 + @test isapprox(max_dHdvpa_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 3.0e-9, 3.0e-9 + @test isapprox(max_dHdvperp_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 7.0e-12, 7.0e-12 + @test isapprox(max_G_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 2.0e-7, 2.0e-7 + @test isapprox(max_dGdvperp_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 2.0e-8, 2.0e-8 + @test isapprox(max_d2Gdvperp2_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 2.0e-8, 2.0e-8 + @test isapprox(max_d2Gdvperpdvpa_boundary_data_err, rtol_max ; atol=atol_max) + rtol_max, atol_max = 7.0e-12, 7.0e-12 + @test isapprox(max_d2Gdvpa2_boundary_data_err, rtol_max ; atol=atol_max) + # test the elliptic solvers + H_M_max, H_M_L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array) + dHdvpa_M_max, dHdvpa_M_L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array) + dHdvperp_M_max, dHdvperp_M_L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp,dummy_array) + G_M_max, G_M_L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp,dummy_array) + d2Gdvpa2_M_max, d2Gdvpa2_M_L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp,dummy_array) + dGdvperp_M_max, dGdvperp_M_L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array) + d2Gdvperpdvpa_M_max, d2Gdvperpdvpa_M_L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array) + d2Gdvperp2_M_max, d2Gdvperp2_M_L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array) + rtol_max, atol_max = 2.0e-7, 2.0e-7 + rtol_L2, atol_L2 = 5.0e-9, 5.0e-9 + @test isapprox(H_M_max, rtol_max ; atol=atol_max) + @test isapprox(H_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-6, 2.0e-6 + rtol_L2, atol_L2 = 5.0e-8, 5.0e-8 + @test isapprox(dHdvpa_M_max, rtol_max ; atol=atol_max) + @test isapprox(dHdvpa_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-5, 2.0e-5 + rtol_L2, atol_L2 = 1.0e-7, 1.0e-7 + @test isapprox(dHdvperp_M_max, rtol_max ; atol=atol_max) + @test isapprox(dHdvperp_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-8, 2.0e-8 + rtol_L2, atol_L2 = 7.0e-10, 7.0e-10 + @test isapprox(G_M_max, rtol_max ; atol=atol_max) + @test isapprox(G_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-7, 2.0e-7 + rtol_L2, atol_L2 = 4.0e-9, 4.0e-9 + @test isapprox(d2Gdvpa2_M_max, rtol_max ; atol=atol_max) + @test isapprox(d2Gdvpa2_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-6, 2.0e-6 + rtol_L2, atol_L2 = 2.0e-7, 2.0e-7 + @test isapprox(dGdvperp_M_max, rtol_max ; atol=atol_max) + @test isapprox(dGdvperp_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 2.0e-6, 2.0e-6 + rtol_L2, atol_L2 = 2.0e-8, 2.0e-8 + @test isapprox(d2Gdvperpdvpa_M_max, rtol_max ; atol=atol_max) + @test isapprox(d2Gdvperpdvpa_M_L2, rtol_L2 ; atol=atol_L2) + rtol_max, atol_max = 3.0e-7, 3.0e-7 + rtol_L2, atol_L2 = 2.0e-8, 2.0e-8 + @test isapprox(d2Gdvperp2_M_max, rtol_max ; atol=atol_max) + @test isapprox(d2Gdvperp2_M_L2, rtol_L2 ; atol=atol_L2) + end + finalize_comms!() + end + end end From 4158f0ba6262295e35070096b026cd6ffc18f383 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 14:20:38 +0000 Subject: [PATCH 239/331] Commit containing the tests of the Fokker-Planck collision operator using all of the options currently supported by the weak-form code. We test both the self- and the cross-species collision operator on Maxwellian distributions. We test various options for the calculating the coefficients in the operator, including a full numerical calculation, and semi-analytical calculations where the Rosenbluth potentials or field-particle distributions are known exactly. The option to compute d2Gdvperp2 using an algebraic relation rather than an elliptic solve is also supported. The numerical conserving terms used for the self-collision operator in the time-evolving code are also tested, checking that the moments are constant to machine precision at each collision operator evaluation. Print-to-screen statements are suppressed in the check-in test script, but retained in the manual test script 2D_FEM_assembly_test.jl --- 2D_FEM_assembly_test.jl | 3 +- src/fokker_planck.jl | 14 +-- src/fokker_planck_calculus.jl | 66 +++++++------- src/fokker_planck_test.jl | 6 +- test/fokker_planck_tests.jl | 165 ++++++++++++++++++++++++++++++---- 5 files changed, 195 insertions(+), 59 deletions(-) diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 58abcd1d4..856744d53 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -381,13 +381,14 @@ using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_vi test_parallelism=false, test_numerical_conserving_terms=false, algebraic_solve_for_d2Gdvperp2=false, + test_self_operator = true, Lvpa = 12.0, Lvperp = 6.0) initialize_comms!() #ngrid = 5 #plot_scan = true #plot_test_output = true#false #test_parallelism = false - test_self_operator = true + #test_self_operator = true #test_dense_construction = false #nelement_list = Int[8, 16, 32, 64, 128] #nelement_list = Int[4, 8, 16, 32, 64] diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index b57008803..85329f54f 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -92,11 +92,11 @@ at the boundary and using an elliptic solve to obtain the potentials in the rest of the velocity space domain. """ -function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false) +function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false, print_to_screen=true) bwgt = allocate_boundary_integration_weights(vpa,vperp) if vperp.n > 1 && precompute_weights @views init_Rosenbluth_potential_boundary_integration_weights!(bwgt.G0_weights, bwgt.G1_weights, bwgt.H0_weights, bwgt.H1_weights, - bwgt.H2_weights, bwgt.H3_weights, vpa, vperp) + bwgt.H2_weights, bwgt.H3_weights, vpa, vperp, print_to_screen=print_to_screen) end rpbd = allocate_rosenbluth_potential_boundary_data(vpa,vperp) if test_dense_matrix_construction @@ -104,27 +104,27 @@ function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_sp KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral,print_to_screen=print_to_screen) else MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, LP2D_sparse, LV2D_sparse, LB2D_sparse, KPperp2D_sparse, PUperp2D_sparse, PPparPUperp2D_sparse, PPpar2D_sparse, - MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) + MMparMNperp2D_sparse = assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral,print_to_screen=print_to_screen) end lu_obj_MM = lu(MM2D_sparse) lu_obj_LP = lu(LP2D_sparse) lu_obj_LV = lu(LV2D_sparse) lu_obj_LB = lu(LB2D_sparse) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("finished LU decomposition initialisation ", Dates.format(now(), dateformat"H:MM:SS")) end end YY_arrays = calculate_YY_arrays(vpa,vperp,vpa_spectral,vperp_spectral) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("finished YY array calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -314,7 +314,7 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp vpa,vperp,YY_arrays) elseif test_assembly_serial assemble_explicit_collision_operator_rhs_serial!(rhsc,@view(ffs_in[:,:]), - d2Gdvpa,d2Gdvperpdvpa,d2Gdvperp2, + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2, dHdvpa,dHdvperp,ms,msp,nussp, vpa,vperp,YY_arrays) else diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index bdc3f6f4f..d8ad16103 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -245,12 +245,12 @@ end """ function that precomputes the required integration weights """ -function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa) +function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa;print_to_screen=true) x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -289,7 +289,7 @@ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0 @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("finished weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -301,9 +301,9 @@ function for getting the basic quadratures used for the numerical integration of the Lagrange polynomials and the Green's function. """ -function setup_basic_quadratures(vpa,vperp) +function setup_basic_quadratures(vpa,vperp;print_to_screen=true) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -347,12 +347,12 @@ function that precomputes the required integration weights only along the velocity space boundaries """ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, - G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp) + G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vpa,vperp;print_to_screen=true) - x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp,print_to_screen=print_to_screen) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("beginning (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -464,8 +464,8 @@ function init_Rosenbluth_potential_boundary_integration_weights!(G0_weights, end # return the parallelisation status to serial begin_serial_region() - @serial_region begin - if global_rank[] == 0 + @serial_region begin + if global_rank[] == 0 && print_to_screen println("finished (boundary) weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -1136,24 +1136,24 @@ function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potentia end function test_rosenbluth_potential_boundary_data(rpbd::rosenbluth_potential_boundary_data, - rpbd_exact::rosenbluth_potential_boundary_data,vpa,vperp) + rpbd_exact::rosenbluth_potential_boundary_data,vpa,vperp;print_to_screen=true) error_buffer_vpa = Array{mk_float,1}(undef,vpa.n) error_buffer_vperp_1 = Array{mk_float,1}(undef,vperp.n) error_buffer_vperp_2 = Array{mk_float,1}(undef,vperp.n) - max_H_err = test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_dHdvpa_err = test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_dHdvperp_err = test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_G_err = test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_dGdvperp_err = test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_d2Gdvperp2_err = test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_d2Gdvperpdvpa_err = test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) - max_d2Gdvpa2_err = test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2) + max_H_err = test_boundary_data(rpbd.H_data,rpbd_exact.H_data,"H",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_dHdvpa_err = test_boundary_data(rpbd.dHdvpa_data,rpbd_exact.dHdvpa_data,"dHdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_dHdvperp_err = test_boundary_data(rpbd.dHdvperp_data,rpbd_exact.dHdvperp_data,"dHdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_G_err = test_boundary_data(rpbd.G_data,rpbd_exact.G_data,"G",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_dGdvperp_err = test_boundary_data(rpbd.dGdvperp_data,rpbd_exact.dGdvperp_data,"dGdvperp",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_d2Gdvperp2_err = test_boundary_data(rpbd.d2Gdvperp2_data,rpbd_exact.d2Gdvperp2_data,"d2Gdvperp2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_d2Gdvperpdvpa_err = test_boundary_data(rpbd.d2Gdvperpdvpa_data,rpbd_exact.d2Gdvperpdvpa_data,"d2Gdvperpdvpa",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) + max_d2Gdvpa2_err = test_boundary_data(rpbd.d2Gdvpa2_data,rpbd_exact.d2Gdvpa2_data,"d2Gdvpa2",vpa,vperp,error_buffer_vpa,error_buffer_vperp_1,error_buffer_vperp_2,print_to_screen) return max_H_err, max_dHdvpa_err, max_dHdvperp_err, max_G_err, max_dGdvperp_err, max_d2Gdvperp2_err, max_d2Gdvperpdvpa_err, max_d2Gdvpa2_err end -function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2) +function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffer_vperp_1,buffer_vperp_2,print_to_screen) nvpa = vpa.n nvperp = vperp.n for ivperp in 1:nvperp @@ -1166,10 +1166,12 @@ function test_boundary_data(func,func_exact,func_name,vpa,vperp,buffer_vpa,buffe max_lower_vpa_err = maximum(buffer_vperp_1) max_upper_vpa_err = maximum(buffer_vperp_2) max_upper_vperp_err = maximum(buffer_vpa) - println(string(func_name*" boundary data:")) - println("max(lower_vpa_err) = ",max_lower_vpa_err) - println("max(upper_vpa_err) = ",max_upper_vpa_err) - println("max(upper_vperp_err) = ",max_upper_vperp_err) + if print_to_screen + println(string(func_name*" boundary data:")) + println("max(lower_vpa_err) = ",max_lower_vpa_err) + println("max(upper_vpa_err) = ",max_upper_vpa_err) + println("max(upper_vperp_err) = ",max_upper_vperp_err) + end max_err = max(max_lower_vpa_err,max_upper_vpa_err,max_upper_vperp_err) return max_err end @@ -1303,7 +1305,7 @@ function enforce_dirichlet_bc!(fc,vpa,vperp,f_bc::vpa_vperp_boundary_data) return nothing end -function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral) +function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spectral;print_to_screen=true) nc_global = vpa.n*vperp.n # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n @@ -1355,7 +1357,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe impose_BC_at_zero_vperp = false @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -1498,7 +1500,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe end end @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) end if nc_global < 60 @@ -1509,7 +1511,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe #print_matrix(LV2D,"LV",nc_global,nc_global) end # convert these matrices to sparse matrices - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -1533,7 +1535,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe PPpar2D_sparse, MMparMNperp2D_sparse end -function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral) +function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vperp_spectral;print_to_screen=true) # Assemble a 2D mass matrix in the global compound coordinate nc_global = vpa.n*vperp.n ntot_vpa = (vpa.nelement_local - 1)*(vpa.ngrid^2 - 1) + vpa.ngrid^2 @@ -1578,7 +1580,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp impose_BC_at_zero_vperp = false @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("begin elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) end end @@ -1753,7 +1755,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp PPpar2D_sparse = create_sparse_matrix(PPpar2D) MMparMNperp2D_sparse = create_sparse_matrix(MMparMNperp2D) @serial_region begin - if global_rank[] == 0 + if global_rank[] == 0 && print_to_screen println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) end if nc_global < 60 @@ -1764,8 +1766,6 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp # print_matrix(LP2D,"LP",nc_global,nc_global) # print_matrix(LV2D,"LV",nc_global,nc_global) end - # convert these matrices to sparse matrices - #println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) end return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, diff --git a/src/fokker_planck_test.jl b/src/fokker_planck_test.jl index 5e04efbb0..244045ca7 100644 --- a/src/fokker_planck_test.jl +++ b/src/fokker_planck_test.jl @@ -300,7 +300,7 @@ function print_test_data(func_exact,func_num,func_err,func_name) return max_err end -function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy) +function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy;print_to_screen=true) @. func_err = abs(func_num - func_exact) max_err = maximum(func_err) @. dummy = func_err^2 @@ -310,7 +310,9 @@ function print_test_data(func_exact,func_num,func_err,func_name,vpa,vperp,dummy) @. dummy = 1.0 denom = get_density(dummy,vpa,vperp) L2norm = sqrt(num/denom) - println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) + if print_to_screen + println("maximum("*func_name*"_err): ",max_err," L2("*func_name*"_err): ",L2norm) + end return max_err, L2norm end diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index 3129ff8ca..493644ab6 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -12,14 +12,18 @@ using moment_kinetics.array_allocation: allocate_float, allocate_shared_float using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure -using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form, fokker_planck_collision_operator_weak_form! +using moment_kinetics.fokker_planck: conserving_corrections! using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian -using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian +using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian, Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_solve!, calculate_rosenbluth_potential_boundary_data_exact! using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data, allocate_rosenbluth_potential_boundary_data +using moment_kinetics.fokker_planck_calculus: enforce_vpavperp_BCs! + function create_grids(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0) @@ -63,6 +67,7 @@ function create_grids(ngrid,nelement_vpa,nelement_vperp; end function runtests() + print_to_screen = false @testset "Fokker Planck tests" verbose=use_verbose begin println("Fokker Planck tests") @@ -76,7 +81,7 @@ function runtests() nc_global = vpa.n*vperp.n begin_serial_region() fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral, - precompute_weights=false) + precompute_weights=false, print_to_screen=print_to_screen) KKpar2D_with_BC_terms_sparse = fkpl_arrays.KKpar2D_with_BC_terms_sparse KKperp2D_with_BC_terms_sparse = fkpl_arrays.KKperp2D_with_BC_terms_sparse lu_obj_MM = fkpl_arrays.lu_obj_MM @@ -109,7 +114,9 @@ function runtests() @. fvpavperp_err = abs(fvpavperp - fvpavperp_test) max_ravel_err = maximum(fvpavperp_err) @serial_region begin - println("max(ravel_err)",max_ravel_err) + if print_to_screen + println("max(ravel_err)",max_ravel_err) + end @test isapprox(max_ravel_err, 1.0e-15 ; atol = 1.0e-15) end #print_vector(fc,"fc",nc_global) @@ -124,10 +131,10 @@ function runtests() ravel_c_to_vpavperp!(d2fvpavperp_dvpa2_num,fc,nc_global,vpa.n) ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) @serial_region begin - d2fvpavperp_dvpa2_max, d2fvpavperp_dvpa2_L2 = print_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fdvpa2",vpa,vperp,dummy_array) + d2fvpavperp_dvpa2_max, d2fvpavperp_dvpa2_L2 = print_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fdvpa2",vpa,vperp,dummy_array,print_to_screen=print_to_screen) @test isapprox(d2fvpavperp_dvpa2_max, 1.0e-7 ; atol=1.0e-7) @test isapprox(d2fvpavperp_dvpa2_L2, 1.0e-8 ; atol=1.0e-8) - d2fvpavperp_dvperp2_max, d2fvpavperp_dvperp2_L2 = print_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fdvperp2",vpa,vperp,dummy_array) + d2fvpavperp_dvperp2_max, d2fvpavperp_dvperp2_L2 = print_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fdvperp2",vpa,vperp,dummy_array,print_to_screen=print_to_screen) @test isapprox(d2fvpavperp_dvperp2_max, 1.0e-7 ; atol=1.0e-7) @test isapprox(d2fvpavperp_dvperp2_L2, 1.0e-8 ; atol=1.0e-8) #if plot_test_output @@ -147,7 +154,7 @@ function runtests() nc_global = vpa.n*vperp.n begin_serial_region() fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral, - precompute_weights=true) + precompute_weights=true, print_to_screen=print_to_screen) dummy_array = allocate_float(vpa.n,vperp.n) F_M = allocate_float(vpa.n,vperp.n) H_M_exact = allocate_float(vpa.n,vperp.n) @@ -220,7 +227,7 @@ function runtests() max_H_boundary_data_err, max_dHdvpa_boundary_data_err, max_dHdvperp_boundary_data_err, max_G_boundary_data_err, max_dGdvperp_boundary_data_err, max_d2Gdvperp2_boundary_data_err, - max_d2Gdvperpdvpa_boundary_data_err, max_d2Gdvpa2_boundary_data_err = test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp) + max_d2Gdvperpdvpa_boundary_data_err, max_d2Gdvpa2_boundary_data_err = test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp,print_to_screen=print_to_screen) rtol_max, atol_max = 2.0e-13, 2.0e-13 @test isapprox(max_H_boundary_data_err, rtol_max ; atol=atol_max) rtol_max, atol_max = 2.0e-12, 2.0e-12 @@ -238,14 +245,14 @@ function runtests() rtol_max, atol_max = 7.0e-12, 7.0e-12 @test isapprox(max_d2Gdvpa2_boundary_data_err, rtol_max ; atol=atol_max) # test the elliptic solvers - H_M_max, H_M_L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array) - dHdvpa_M_max, dHdvpa_M_L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array) - dHdvperp_M_max, dHdvperp_M_L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp,dummy_array) - G_M_max, G_M_L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp,dummy_array) - d2Gdvpa2_M_max, d2Gdvpa2_M_L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp,dummy_array) - dGdvperp_M_max, dGdvperp_M_L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array) - d2Gdvperpdvpa_M_max, d2Gdvperpdvpa_M_L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array) - d2Gdvperp2_M_max, d2Gdvperp2_M_L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array) + H_M_max, H_M_L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dHdvpa_M_max, dHdvpa_M_L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dHdvperp_M_max, dHdvperp_M_L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + G_M_max, G_M_L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvpa2_M_max, d2Gdvpa2_M_L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dGdvperp_M_max, dGdvperp_M_L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvperpdvpa_M_max, d2Gdvperpdvpa_M_L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvperp2_M_max, d2Gdvperp2_M_L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) rtol_max, atol_max = 2.0e-7, 2.0e-7 rtol_L2, atol_L2 = 5.0e-9, 5.0e-9 @test isapprox(H_M_max, rtol_max ; atol=atol_max) @@ -282,6 +289,132 @@ function runtests() finalize_comms!() end + @testset " - test weak-form collision operator calculation" begin + ngrid = 9 + nelement_vpa = 8 + nelement_vperp = 4 + vpa, vpa_spectral, vperp, vperp_spectral = create_grids(ngrid,nelement_vpa,nelement_vperp, + Lvpa=12.0,Lvperp=6.0) + nc_global = vpa.n*vperp.n + begin_serial_region() + fkpl_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral, + precompute_weights=true, print_to_screen=print_to_screen) + + @testset "test_self_operator=$test_self_operator test_numerical_conserving_terms=$test_numerical_conserving_terms test_parallelism = $test_parallelism test_dense_construction=$test_dense_construction use_Maxwellian_Rosenbluth_coefficients=$use_Maxwellian_Rosenbluth_coefficients use_Maxwellian_field_particle_distribution=$use_Maxwellian_field_particle_distribution algebraic_solve_for_d2Gdvperp2=$algebraic_solve_for_d2Gdvperp2" for + (test_self_operator, test_numerical_conserving_terms, test_parallelism, test_dense_construction, + use_Maxwellian_Rosenbluth_coefficients, use_Maxwellian_field_particle_distribution, + algebraic_solve_for_d2Gdvperp2) in ((true,false,false,false,false,false,false),(false,false,false,false,false,false,false), + (true,true,false,false,false,false,false),(true,false,true,false,false,false,false), + (true,false,false,true,false,false,false),(true,false,false,false,true,false,false), + (true,false,false,false,false,true,false),(true,false,false,false,false,false,true)) + + dummy_array = allocate_float(vpa.n,vperp.n) + Fs_M = allocate_float(vpa.n,vperp.n) + F_M = allocate_float(vpa.n,vperp.n) + C_M_num = allocate_shared_float(vpa.n,vperp.n) + C_M_exact = allocate_float(vpa.n,vperp.n) + C_M_err = allocate_float(vpa.n,vperp.n) + if test_self_operator + dens, upar, vth = 1.0, 1.0, 1.0 + denss, upars, vths = dens, upar, vth + else + denss, upars, vths = 1.0, -1.0, 2.0/3.0 + dens, upar, vth = 1.0, 1.0, 1.0 + end + ms = 1.0 + msp = 1.0 + nussp = 1.0 + begin_serial_region() + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + Fs_M[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + C_M_exact[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + dens,upar,vth,msp, + nussp,vpa,vperp,ivpa,ivperp) + end + end + fokker_planck_collision_operator_weak_form!(Fs_M,F_M,ms,msp,nussp, + fkpl_arrays, + vperp, vpa, vperp_spectral, vpa_spectral, + test_assembly_serial=test_parallelism, + use_Maxwellian_Rosenbluth_coefficients=use_Maxwellian_Rosenbluth_coefficients, + use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, + algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2, + calculate_GG = false, calculate_dGdvperp=false) + # extract C[Fs,Fs'] result + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + end + if test_numerical_conserving_terms && test_self_operator + # enforce the boundary conditions on CC before it is used for timestepping + enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) + # make ad-hoc conserving corrections + conserving_corrections!(fkpl_arrays.CC,Fs_M,vpa,vperp,dummy_array) + end + begin_serial_region() + @serial_region begin + C_M_max, C_M_L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + if test_self_operator + rtol_max, atol_max = 6.0e-4, 6.0e-4 + rtol_L2, atol_L2 = 7.0e-6, 7.0e-6 + else + rtol_max, atol_max = 7.0e-2, 7.0e-2 + rtol_L2, atol_L2 = 6.0e-4, 6.0e-4 + end + @test isapprox(C_M_max, rtol_max ; atol=atol_max) + @test isapprox(C_M_L2, rtol_L2 ; atol=atol_L2) + # calculate the entropy production + lnfC = fkpl_arrays.rhsvpavperp + @loop_vperp_vpa ivperp ivpa begin + lnfC[ivpa,ivperp] = Fs_M[ivpa,ivperp]*C_M_num[ivpa,ivperp] + end + dSdt = - get_density(lnfC,vpa,vperp) + if test_self_operator + if algebraic_solve_for_d2Gdvperp2 + rtol, atol = 0.0, 1.0e-7 + else + rtol, atol = 0.0, 1.0e-8 + end + @test isapprox(dSdt, rtol ; atol=atol) + delta_n = get_density(C_M_num, vpa, vperp) + delta_upar = get_upar(C_M_num, vpa, vperp, dens) + delta_ppar = msp*get_ppar(C_M_num, vpa, vperp, upar) + delta_pperp = msp*get_pperp(C_M_num, vpa, vperp) + delta_pressure = get_pressure(delta_ppar,delta_pperp) + rtol, atol = 0.0, 1.0e-12 + @test isapprox(delta_n, rtol ; atol=atol) + rtol, atol = 0.0, 1.0e-9 + @test isapprox(delta_upar, rtol ; atol=atol) + if algebraic_solve_for_d2Gdvperp2 + rtol, atol = 0.0, 1.0e-7 + else + rtol, atol = 0.0, 1.0e-8 + end + @test isapprox(delta_pressure, rtol ; atol=atol) + if print_to_screen + println("dSdt: $dSdt should be >0.0") + println("delta_n: ", delta_n) + println("delta_upar: ", delta_upar) + println("delta_pressure: ", delta_pressure) + end + else + atol = 1.0e-4 + @test isapprox(dSdt, 2.543251178128757 ; atol=atol) + delta_n = get_density(C_M_num, vpa, vperp) + rtol, atol = 0.0, 1.0e-12 + @test isapprox(delta_n, rtol ; atol=atol) + if print_to_screen + println("dSdt: $dSdt") + println("delta_n: ", delta_n) + end + end + end + end + finalize_comms!() + end + end end From 56c1d37131875eca3e220a3b2e6185c1abe228fb Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 16:26:46 +0000 Subject: [PATCH 240/331] Commit changes that should have been recorded in the previous commit --- test/fokker_planck_tests.jl | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index 493644ab6..5a55b7dcd 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -342,17 +342,17 @@ function runtests() use_Maxwellian_field_particle_distribution=use_Maxwellian_field_particle_distribution, algebraic_solve_for_d2Gdvperp2=algebraic_solve_for_d2Gdvperp2, calculate_GG = false, calculate_dGdvperp=false) - # extract C[Fs,Fs'] result - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] - end if test_numerical_conserving_terms && test_self_operator # enforce the boundary conditions on CC before it is used for timestepping enforce_vpavperp_BCs!(fkpl_arrays.CC,vpa,vperp,vpa_spectral,vperp_spectral) # make ad-hoc conserving corrections conserving_corrections!(fkpl_arrays.CC,Fs_M,vpa,vperp,dummy_array) end + # extract C[Fs,Fs'] result + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + C_M_num[ivpa,ivperp] = fkpl_arrays.CC[ivpa,ivperp] + end begin_serial_region() @serial_region begin C_M_max, C_M_L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) @@ -371,7 +371,7 @@ function runtests() lnfC[ivpa,ivperp] = Fs_M[ivpa,ivperp]*C_M_num[ivpa,ivperp] end dSdt = - get_density(lnfC,vpa,vperp) - if test_self_operator + if test_self_operator && !test_numerical_conserving_terms if algebraic_solve_for_d2Gdvperp2 rtol, atol = 0.0, 1.0e-7 else @@ -399,6 +399,26 @@ function runtests() println("delta_upar: ", delta_upar) println("delta_pressure: ", delta_pressure) end + elseif test_self_operator && test_numerical_conserving_terms + rtol, atol = 0.0, 6.0e-7 + @test isapprox(dSdt, rtol ; atol=atol) + delta_n = get_density(C_M_num, vpa, vperp) + delta_upar = get_upar(C_M_num, vpa, vperp, dens) + delta_ppar = msp*get_ppar(C_M_num, vpa, vperp, upar) + delta_pperp = msp*get_pperp(C_M_num, vpa, vperp) + delta_pressure = get_pressure(delta_ppar,delta_pperp) + rtol, atol = 0.0, 1.0e-15 + @test isapprox(delta_n, rtol ; atol=atol) + rtol, atol = 0.0, 1.0e-15 + @test isapprox(delta_upar, rtol ; atol=atol) + rtol, atol = 0.0, 1.0e-15 + @test isapprox(delta_pressure, rtol ; atol=atol) + if print_to_screen + println("dSdt: $dSdt should be >0.0") + println("delta_n: ", delta_n) + println("delta_upar: ", delta_upar) + println("delta_pressure: ", delta_pressure) + end else atol = 1.0e-4 @test isapprox(dSdt, 2.543251178128757 ; atol=atol) From 5a8c894600121936329e32e456bf8f143342e9dc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 16 Nov 2023 16:27:09 +0000 Subject: [PATCH 241/331] Make sure that vpa_diffusion = true when using fokker-planck collisions. --- src/time_advance.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time_advance.jl b/src/time_advance.jl index c3cf95a40..eeeafd3eb 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -576,7 +576,7 @@ function setup_advance_flags(moments, composition, t_input, collisions, num_diss # 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) # 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) + vpa_diffusion = ((advance_numerical_dissipation && num_diss_params.vpa_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions || explicit_fp_collisions || explicit_fp_F_FM_collisions) vz_diffusion = (advance_numerical_dissipation && num_diss_params.vz_dissipation_coefficient > 0.0) end From ea3da914d48d86ecedda05a31ce28e1e21db4ef6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 17 Nov 2023 23:19:12 +0000 Subject: [PATCH 242/331] Addition of a time-evolution test of dF/dt = C[F,F], using data from the cheapest possible relaxation simulation (the input file is also recorded in this commit) for the given initial conditions. The simulation is run long enough to reach the steady state, in that L2(f-f_M) does not change significantly. The absolute error tolerance of atol=1.0e-2 was used in this test. This high value suggests that one of the input parameters or another aspect of the test execution is different between the case run from the input file and the case run from the test/fokker_planck_time_evolution_test.jl script. --- ...3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml | 97 ++++++ src/load_data.jl | 15 +- test/fokker_planck_time_evolution_tests.jl | 294 ++++++++++++++++++ test/runtests.jl | 1 + 4 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml create mode 100644 test/fokker_planck_time_evolution_tests.jl 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 new file mode 100644 index 000000000..e0a867fbc --- /dev/null +++ b/runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml @@ -0,0 +1,97 @@ +use_manufactured_solns_for_init = true +use_manufactured_solns_for_advance = false +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +#electron_physics = "boltzmann_electron_response_with_simple_sheath" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +force_Er_zero_at_wall = false #true +Er_constant = 0.0 +epsilon_offset = 0.1 +use_vpabar_in_mms_dfni = true +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +constant_ionization_rate = false +nuii = 1.0 +nuii_krook = 0.0 +nuii_pitch = 0.0 +nstep = 50000 +dt = 1.0e-3 +nwrite = 50000 +nwrite_dfns = 50000 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 1 +z_nelement = 1 +z_nelement_local = 1 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 3 +vpa_nelement = 6 +vpa_L = 6.0 +vpa_bc = "zero" +#vpa_discretization = "chebyshev_pseudospectral" +vpa_discretization = "gausslegendre_pseudospectral" +vperp_ngrid = 3 +vperp_nelement = 3 +vperp_L = 3.0 +vperp_bc = "periodic" +#vperp_discretization = "finite_difference" +#vperp_discretization = "chebyshev_pseudospectral" +vperp_discretization = "gausslegendre_pseudospectral" + +vz_ngrid = 17 +vz_nelement = 4 +vz_L = 12.0 +vz_bc = "periodic" +vz_discretization = "chebyshev_pseudospectral" + +vr_ngrid = 17 +vr_nelement = 4 +vr_L = 12.0 +vr_bc = "periodic" +vr_discretization = "chebyshev_pseudospectral" + +vzeta_ngrid = 17 +vzeta_nelement = 4 +vzeta_L = 12.0 +vzeta_bc = "periodic" +vzeta_discretization = "chebyshev_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.0 +vperp_dissipation_coefficient = 0.0 +#z_dissipation_coefficient = 0.1 +r_dissipation_coefficient = 0.0 diff --git a/src/load_data.jl b/src/load_data.jl index f9935b6a0..e86a20836 100644 --- a/src/load_data.jl +++ b/src/load_data.jl @@ -381,7 +381,7 @@ end """ """ -function load_charged_particle_moments_data(fid; printout=false) +function load_charged_particle_moments_data(fid; printout=false, extended_moments = false) if printout print("Loading charged particle velocity moments data...") end @@ -403,11 +403,20 @@ function load_charged_particle_moments_data(fid; printout=false) # Read charged species thermal speed thermal_speed = load_variable(group, "thermal_speed") + # Read charged species perpendicular pressure + perpendicular_pressure = load_variable(group, "perpendicular_pressure") + + # Read charged species entropy_production + entropy_production = load_variable(group, "entropy_production") + if printout println("done.") end - - return density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed + if extended_moments + 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 end function load_neutral_particle_moments_data(fid; printout=false) diff --git a/test/fokker_planck_time_evolution_tests.jl b/test/fokker_planck_time_evolution_tests.jl new file mode 100644 index 000000000..a6bb49b42 --- /dev/null +++ b/test/fokker_planck_time_evolution_tests.jl @@ -0,0 +1,294 @@ +module FokkerPlanckTimeEvolutionTests +include("setup.jl") + +using Base.Filesystem: tempname +using MPI +using TimerOutputs + +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_time_data, load_species_data +using moment_kinetics.type_definitions: mk_float + +const analytical_rtol = 3.e-2 +const regression_rtol = 2.e-8 + +# Create a temporary directory for test output +test_output_directory = tempname() +mkpath(test_output_directory) + +# The expected output +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 + dSdt::Array{mk_float, 1} # time + f_charged::Array{mk_float, 3} # vpa, vperp, time +end + +const expected = + expected_data( + vec([-3 -2.5 -2 -1.5 -1 -0.5 0 0.5 1 1.5 2 2.5 3]), + vec([0.155051 0.644949 1 1.5 2 2.5 3]), + # Expected phi: + vec([-1.267505494648937 -1.275240686285454]), + # Expected n_charged: + vec([0.281533032234007 0.279363721112678]), + # Expected upar_charged: + vec([0.0 -0.000000000000001]), + # Expected ppar_charged: + vec([0.179822802480489 0.147624463313134]), + # Expected pperp_charged + vec([0.143401466675068 0.158821758231315]), + # Expected qpar_charged + vec([0.0 0.0]), + # Expected v_t_charged + vec([1.051172608301042 1.053709630931266]), + # Expected dSdt + vec([0.0 0.000008785979565]), + # Expected f_charged: + [0.000000000000000; 0.000619960016181; 0.005882016862627; 0.033848669972256; 0.118138103172003; 0.229579465209362; 0.000000000000000; 0.229579465209362; 0.118138103172003; 0.033848669972256; 0.005882016862627; 0.000619960016181; 0.000000000000000;; + 0.000000000000000; 0.000478053009971; 0.004535640674379; 0.026100809957764; 0.091096642266611; 0.177029407552679; 0.000000000000000; 0.177029407552679; 0.091096642266611; 0.026100809957764; 0.004535640674379; 0.000478053009971; 0.000000000000000;; + 0.000000000000000; 0.000266581711212; 0.002529256854782; 0.014554868262370; 0.050799175561231; 0.098718764270694; 0.000000000000000; 0.098718764270694; 0.050799175561231; 0.014554868262370; 0.002529256854782; 0.000266581711212; 0.000000000000000;; + 0.000000000000000; 0.000076376939017; 0.000724644221386; 0.004170039574837; 0.014554207474836; 0.028283399503664; 0.000000000000000; 0.028283399503664; 0.014554207474836; 0.004170039574837; 0.000724644221386; 0.000076376939017; 0.000000000000000;; + 0.000000000000000; 0.000013272321882; 0.000125924283949; 0.000724644221264; 0.002529142026698; 0.004914917865936; 0.000000000000000; 0.004914917865936; 0.002529142026698; 0.000724644221264; 0.000125924283949; 0.000013272321882; 0.000000000000000;; + 0.000000000000000; 0.000001398892434; 0.000013272321882; 0.000076376939004; 0.000266569608421; 0.000518028531855; 0.000000000000000; 0.000518028531855; 0.000266569608421; 0.000076376939004; 0.000013272321882; 0.000001398892434; 0.000000000000000;; + 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000;;; + 0.000000000000000; 0.000138780288619; 0.005791061761083; 0.027815242206659; 0.081445074789095; 0.152354938162618; 0.186355844713317; 0.152354938162617; 0.081445074789094; 0.027815242206658; 0.005791061761083; 0.000138780288619; 0.000000000000000;; + 0.000000000000000; 0.000041802335902; 0.004577414397901; 0.022242056574201; 0.065733717960889; 0.123790379312686; 0.151688746523960; 0.123790379312685; 0.065733717960889; 0.022242056574200; 0.004577414397901; 0.000041802335902; 0.000000000000000;; + 0.000000000000000; -0.000102715219360; 0.002768824455923; 0.013936837294347; 0.042320490365826; 0.081223176185748; 0.100027475709292; 0.081223176185748; 0.042320490365826; 0.013936837294347; 0.002768824455923; -0.000102715219360; 0.000000000000000;; + 0.000000000000000; -0.000164682482097; 0.000767345547592; 0.004535511737563; 0.014877992136837; 0.029791500388954; 0.037097641507925; 0.029791500388954; 0.014877992136837; 0.004535511737563; 0.000767345547592; -0.000164682482097; 0.000000000000000;; + 0.000000000000000; -0.000091767217551; 0.000022353567834; 0.000693446610930; 0.002889786006257; 0.006284514039983; 0.007979739613551; 0.006284514039983; 0.002889786006257; 0.000693446610930; 0.000022353567834; -0.000091767217551; 0.000000000000000;; + 0.000000000000000; -0.000032024960899; -0.000089243114436; -0.000201682249055; -0.000175449913982; 0.000111463338879; 0.000290773816217; 0.000111463338879; -0.000175449913982; -0.000201682249055; -0.000089243114436; -0.000032024960899; 0.000000000000000;; + 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000]) + +# default inputs for tests +test_input_gauss_legendre = Dict("n_ion_species" => 1, + "n_neutral_species" => 0, + "boltzmann_electron_response" => true, + "run_name" => "gausslegendre_pseudospectral", + "base_directory" => test_output_directory, + "evolve_moments_density" => false, + "evolve_moments_parallel_flow" => false, + "evolve_moments_parallel_pressure" => false, + "evolve_moments_conservation" => false, + "initial_density1" => 0.5, + "initial_temperature1" => 1.0, + "initial_density2" => 0.5, + "initial_temperature2" => 1.0, + "z_IC_option1" => "sinusoid", + "z_IC_density_amplitude1" => 0.001, + "z_IC_density_phase1" => 0.0, + "z_IC_upar_amplitude1" => 0.0, + "z_IC_upar_phase1" => 0.0, + "z_IC_temperature_amplitude1" => 0.0, + "z_IC_temperature_phase1" => 0.0, + "z_IC_option2" => "sinusoid", + "z_IC_density_amplitude2" => 0.001, + "z_IC_density_phase2" => 0.0, + "z_IC_upar_amplitude2" => 0.0, + "z_IC_upar_phase2" => 0.0, + "z_IC_temperature_amplitude2" => 0.0, + "z_IC_temperature_phase2" => 0.0, + "T_e" => 1.0, + "charge_exchange_frequency" => 0.0, + "ionization_frequency" => 0.0, + "nuii" => 1.0, + "nstep" => 50000, + "dt" => 1.0e-3, + "nwrite" => 50000, + "nwrite_dfns" => 50000, + "use_semi_lagrange" => false, + "n_rk_stages" => 4, + "split_operators" => false, + "r_ngrid" => 1, + "r_nelement" => 1, + "r_nelement_local" => 1, + "r_bc" => "periodic", + "r_discretization" => "chebyshev_pseudospectral", + "z_ngrid" => 1, + "z_nelement" => 1, + "z_nelement_local" => 1, + "z_bc" => "wall", + "z_discretization" => "chebyshev_pseudospectral", + "vpa_ngrid" => 3, + "vpa_nelement" => 6, + "vpa_L" => 6.0, + "vpa_bc" => "zero", + "vpa_discretization" => "gausslegendre_pseudospectral", + "vperp_ngrid" => 3, + "vperp_nelement" => 3, + "vperp_L" => 3.0, + "vperp_bc" => "zero", + "vperp_discretization" => "gausslegendre_pseudospectral") + + +# Not actually used in the tests, but needed for first argument of run_moment_kinetics +to = TimerOutput() + +""" +Run a sound-wave test for a single set of parameters +""" +# Note 'name' should not be shared by any two tests in this file +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 + + if upar_rtol === nothing + upar_rtol = rtol + end + + # Convert keyword arguments to a unique name + name = test_input["run_name"] + if length(args) > 0 + name = string(name, "_", (string(k, "-", v, "_") for (k, v) in args)...) + + # Remove trailing "_" + name = chop(name) + end + + # Provide some progress info + println(" - testing ", name) + + # Convert dict from symbol keys to String keys + modified_inputs = Dict(String(k) => v for (k, v) in args) + + # Update default inputs with values to be changed + #input = merge(test_input, modified_inputs) + input = test_input + + input["run_name"] = name + + # Suppress console output while running + quietoutput() do + # run simulation + run_moment_kinetics(to, input) + end + + phi = nothing + n_charged = nothing + upar_charged = nothing + ppar_charged = nothing + pperp_charged = nothing + qpar_charged = nothing + v_t_charged = nothing + dSdt = nothing + f_charged = nothing + vpa, vpa_spectral = nothing, nothing + vperp, vperp_spectral = nothing, nothing + + if global_rank[] == 0 + quietoutput() do + + # Load and analyse output + ######################### + + path = joinpath(realpath(input["base_directory"]), name, name) + + # open the netcdf file containing moments data and give it the handle 'fid' + fid = open_readonly_output_file(path, "moments") + + # load species, time coordinate data + n_ion_species, n_neutral_species = load_species_data(fid) + ntime, time = load_time_data(fid) + n_ion_species, n_neutral_species = load_species_data(fid) + + # load fields data + 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) + + close(fid) + + # open the netcdf file containing pdf data + fid = open_readonly_output_file(path, "dfns") + # load coordinates + vpa, vpa_spectral = load_coordinate_data(fid, "vpa") + vperp, vperp_spectral = load_coordinate_data(fid, "vperp") + + # load particle distribution function (pdf) data + f_charged_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,:] + dSdt = dSdt_zrst[1,1,1,:] + f_charged = f_charged_vpavperpzrst[:,:,1,1,1,:] + + # Unnormalize f + # NEED TO UPGRADE TO 2V MOMENT KINETICS HERE + + end + + function test_values(tind) + @testset "tind=$tind" begin + # Check grids + ############# + + @test isapprox(expected.vpa[:], vpa.grid[:], atol=atol) + @test isapprox(expected.vperp[:], vperp.grid[:], atol=atol) + + # Check electrostatic potential + ############################### + + @test isapprox(expected.phi[tind], phi[tind], rtol=rtol) + + # Check charged 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.dSdt[tind], dSdt[tind], atol=atol) + @test isapprox(expected.f_charged[:,:,tind], f_charged[:,:,tind], atol=atol) + end + end + + # Test initial values + test_values(1) + + # Test final values + test_values(2) + end +end + + +function runtests() + @testset "Fokker Planck dFdt = C[F,F] relaxation test" verbose=use_verbose begin + println("Fokker Planck dFdt = C[F,F] relaxation test") + + # GaussLegendre pseudospectral + # Benchmark data is taken from this run (GaussLegendre) + @testset "Gauss Legendre base" begin + run_test(test_input_gauss_legendre, 1.e-2, 1.0e-2) + end + end +end + +end # FokkerPlanckTimeEvolutionTests + + +using .FokkerPlanckTimeEvolutionTests + +FokkerPlanckTimeEvolutionTests.runtests() diff --git a/test/runtests.jl b/test/runtests.jl index 8ac45b6f2..990c62fdd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,7 @@ function runtests() include(joinpath(@__DIR__, "harrisonthompson.jl")) include(joinpath(@__DIR__, "wall_bc_tests.jl")) include(joinpath(@__DIR__, "fokker_planck_tests.jl")) + include(joinpath(@__DIR__, "fokker_planck_time_evolution_tests.jl")) end end From 5539ee65fba69f7d744053f9ec1e182270edace6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 19 Nov 2023 17:33:48 +0000 Subject: [PATCH 243/331] Two changes: first, using @johnomotani's suggestion of manually replacing the input Dict with the input Dict constructed from the test input.toml. Second, increase the time step size of the test to 0.01 so that only 5000 steps are required for the test execution. This reduces the time to execute the run-time part of the test by factor of ten. The test now passes with atol = 1.0e-14. --- ...3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml | 62 ++--- test/fokker_planck_time_evolution_tests.jl | 212 +++++++++++------- 2 files changed, 156 insertions(+), 118 deletions(-) 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 e0a867fbc..2d5cc827e 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 @@ -1,5 +1,5 @@ -use_manufactured_solns_for_init = true -use_manufactured_solns_for_advance = false +#use_manufactured_solns_for_init = true +#use_manufactured_solns_for_advance = false n_ion_species = 1 n_neutral_species = 0 electron_physics = "boltzmann_electron_response" @@ -8,10 +8,10 @@ evolve_moments_density = false evolve_moments_parallel_flow = false evolve_moments_parallel_pressure = false evolve_moments_conservation = false -force_Er_zero_at_wall = false #true -Er_constant = 0.0 -epsilon_offset = 0.1 -use_vpabar_in_mms_dfni = true +#force_Er_zero_at_wall = false #true +#Er_constant = 0.0 +#epsilon_offset = 0.1 +#use_vpabar_in_mms_dfni = true T_e = 1.0 T_wall = 1.0 rhostar = 1.0 @@ -39,12 +39,12 @@ charge_exchange_frequency = 0.0 ionization_frequency = 0.0 constant_ionization_rate = false nuii = 1.0 -nuii_krook = 0.0 -nuii_pitch = 0.0 -nstep = 50000 -dt = 1.0e-3 -nwrite = 50000 -nwrite_dfns = 50000 +#nuii_krook = 0.0 +#nuii_pitch = 0.0 +nstep = 5000 +dt = 1.0e-2 +nwrite = 5000 +nwrite_dfns = 5000 use_semi_lagrange = false n_rk_stages = 4 split_operators = false @@ -72,26 +72,26 @@ vperp_bc = "periodic" #vperp_discretization = "chebyshev_pseudospectral" vperp_discretization = "gausslegendre_pseudospectral" -vz_ngrid = 17 -vz_nelement = 4 -vz_L = 12.0 -vz_bc = "periodic" -vz_discretization = "chebyshev_pseudospectral" +#vz_ngrid = 17 +#vz_nelement = 4 +#vz_L = 12.0 +#vz_bc = "periodic" +#vz_discretization = "chebyshev_pseudospectral" -vr_ngrid = 17 -vr_nelement = 4 -vr_L = 12.0 -vr_bc = "periodic" -vr_discretization = "chebyshev_pseudospectral" +#vr_ngrid = 17 +#vr_nelement = 4 +#vr_L = 12.0 +#vr_bc = "periodic" +#vr_discretization = "chebyshev_pseudospectral" -vzeta_ngrid = 17 -vzeta_nelement = 4 -vzeta_L = 12.0 -vzeta_bc = "periodic" -vzeta_discretization = "chebyshev_pseudospectral" +#vzeta_ngrid = 17 +#vzeta_nelement = 4 +#vzeta_L = 12.0 +#vzeta_bc = "periodic" +#vzeta_discretization = "chebyshev_pseudospectral" -[numerical_dissipation] -vpa_dissipation_coefficient = 0.0 -vperp_dissipation_coefficient = 0.0 +#[numerical_dissipation] +#vpa_dissipation_coefficient = 0.0 +#vperp_dissipation_coefficient = 0.0 #z_dissipation_coefficient = 0.1 -r_dissipation_coefficient = 0.0 +#r_dissipation_coefficient = 0.0 diff --git a/test/fokker_planck_time_evolution_tests.jl b/test/fokker_planck_time_evolution_tests.jl index a6bb49b42..856e70b24 100644 --- a/test/fokker_planck_time_evolution_tests.jl +++ b/test/fokker_planck_time_evolution_tests.jl @@ -37,99 +37,133 @@ end const expected = expected_data( - vec([-3 -2.5 -2 -1.5 -1 -0.5 0 0.5 1 1.5 2 2.5 3]), - vec([0.155051 0.644949 1 1.5 2 2.5 3]), + vec([-3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 ]), + vec([0.155051025721682 0.644948974278318 1.000000000000000 1.500000000000000 2.000000000000000 2.500000000000000 3.000000000000000 ]), # Expected phi: - vec([-1.267505494648937 -1.275240686285454]), + vec([-1.267505494648937 -1.275240686416660 ]), # Expected n_charged: - vec([0.281533032234007 0.279363721112678]), + vec([0.281533032234007 0.279363721076024 ]), # Expected upar_charged: - vec([0.0 -0.000000000000001]), + vec([0.0 0.0 ]), # Expected ppar_charged: - vec([0.179822802480489 0.147624463313134]), + vec([0.179822802480489 0.147624463307870 ]), # Expected pperp_charged - vec([0.143401466675068 0.158821758231315]), + vec([0.143401466675068 0.158821758223731 ]), # Expected qpar_charged - vec([0.0 0.0]), + vec([0.0 0.0 ]), # Expected v_t_charged - vec([1.051172608301042 1.053709630931266]), + vec([1.051172608301042 1.053709630977256 ]), # Expected dSdt - vec([0.0 0.000008785979565]), + vec([0.0 0.000008786074305 ]), # Expected f_charged: - [0.000000000000000; 0.000619960016181; 0.005882016862627; 0.033848669972256; 0.118138103172003; 0.229579465209362; 0.000000000000000; 0.229579465209362; 0.118138103172003; 0.033848669972256; 0.005882016862627; 0.000619960016181; 0.000000000000000;; - 0.000000000000000; 0.000478053009971; 0.004535640674379; 0.026100809957764; 0.091096642266611; 0.177029407552679; 0.000000000000000; 0.177029407552679; 0.091096642266611; 0.026100809957764; 0.004535640674379; 0.000478053009971; 0.000000000000000;; - 0.000000000000000; 0.000266581711212; 0.002529256854782; 0.014554868262370; 0.050799175561231; 0.098718764270694; 0.000000000000000; 0.098718764270694; 0.050799175561231; 0.014554868262370; 0.002529256854782; 0.000266581711212; 0.000000000000000;; - 0.000000000000000; 0.000076376939017; 0.000724644221386; 0.004170039574837; 0.014554207474836; 0.028283399503664; 0.000000000000000; 0.028283399503664; 0.014554207474836; 0.004170039574837; 0.000724644221386; 0.000076376939017; 0.000000000000000;; - 0.000000000000000; 0.000013272321882; 0.000125924283949; 0.000724644221264; 0.002529142026698; 0.004914917865936; 0.000000000000000; 0.004914917865936; 0.002529142026698; 0.000724644221264; 0.000125924283949; 0.000013272321882; 0.000000000000000;; - 0.000000000000000; 0.000001398892434; 0.000013272321882; 0.000076376939004; 0.000266569608421; 0.000518028531855; 0.000000000000000; 0.000518028531855; 0.000266569608421; 0.000076376939004; 0.000013272321882; 0.000001398892434; 0.000000000000000;; - 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000;;; - 0.000000000000000; 0.000138780288619; 0.005791061761083; 0.027815242206659; 0.081445074789095; 0.152354938162618; 0.186355844713317; 0.152354938162617; 0.081445074789094; 0.027815242206658; 0.005791061761083; 0.000138780288619; 0.000000000000000;; - 0.000000000000000; 0.000041802335902; 0.004577414397901; 0.022242056574201; 0.065733717960889; 0.123790379312686; 0.151688746523960; 0.123790379312685; 0.065733717960889; 0.022242056574200; 0.004577414397901; 0.000041802335902; 0.000000000000000;; - 0.000000000000000; -0.000102715219360; 0.002768824455923; 0.013936837294347; 0.042320490365826; 0.081223176185748; 0.100027475709292; 0.081223176185748; 0.042320490365826; 0.013936837294347; 0.002768824455923; -0.000102715219360; 0.000000000000000;; - 0.000000000000000; -0.000164682482097; 0.000767345547592; 0.004535511737563; 0.014877992136837; 0.029791500388954; 0.037097641507925; 0.029791500388954; 0.014877992136837; 0.004535511737563; 0.000767345547592; -0.000164682482097; 0.000000000000000;; - 0.000000000000000; -0.000091767217551; 0.000022353567834; 0.000693446610930; 0.002889786006257; 0.006284514039983; 0.007979739613551; 0.006284514039983; 0.002889786006257; 0.000693446610930; 0.000022353567834; -0.000091767217551; 0.000000000000000;; - 0.000000000000000; -0.000032024960899; -0.000089243114436; -0.000201682249055; -0.000175449913982; 0.000111463338879; 0.000290773816217; 0.000111463338879; -0.000175449913982; -0.000201682249055; -0.000089243114436; -0.000032024960899; 0.000000000000000;; - 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000; 0.000000000000000]) - + [ 0.000000000000000 ; 0.000619960016181 ; 0.005882016862627 ; 0.033848669972256 ; 0.118138103172003 ; 0.229579465209362 ; 0.000000000000000 ; 0.229579465209362 ; 0.118138103172003 ; 0.033848669972256 ; 0.005882016862627 ; 0.000619960016181 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000478053009971 ; 0.004535640674379 ; 0.026100809957764 ; 0.091096642266611 ; 0.177029407552679 ; 0.000000000000000 ; 0.177029407552679 ; 0.091096642266611 ; 0.026100809957764 ; 0.004535640674379 ; 0.000478053009971 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000266581711212 ; 0.002529256854782 ; 0.014554868262370 ; 0.050799175561231 ; 0.098718764270694 ; 0.000000000000000 ; 0.098718764270694 ; 0.050799175561231 ; 0.014554868262370 ; 0.002529256854782 ; 0.000266581711212 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000076376939017 ; 0.000724644221386 ; 0.004170039574837 ; 0.014554207474836 ; 0.028283399503664 ; 0.000000000000000 ; 0.028283399503664 ; 0.014554207474836 ; 0.004170039574837 ; 0.000724644221386 ; 0.000076376939017 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000013272321882 ; 0.000125924283949 ; 0.000724644221264 ; 0.002529142026698 ; 0.004914917865936 ; 0.000000000000000 ; 0.004914917865936 ; 0.002529142026698 ; 0.000724644221264 ; 0.000125924283949 ; 0.000013272321882 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000001398892434 ; 0.000013272321882 ; 0.000076376939004 ; 0.000266569608421 ; 0.000518028531855 ; 0.000000000000000 ; 0.000518028531855 ; 0.000266569608421 ; 0.000076376939004 ; 0.000013272321882 ; 0.000001398892434 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ;;; + 0.000000000000000 ; 0.000138780288865 ; 0.005791061761509 ; 0.027815242205125 ; 0.081445074775077 ; 0.152354938126957 ; 0.186355844666422 ; 0.152354938126957 ; 0.081445074775077 ; 0.027815242205125 ; 0.005791061761509 ; 0.000138780288865 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000041802336103 ; 0.004577414398302 ; 0.022242056573360 ; 0.065733717950559 ; 0.123790379285355 ; 0.151688746487743 ; 0.123790379285355 ; 0.065733717950559 ; 0.022242056573360 ; 0.004577414398302 ; 0.000041802336103 ; 0.000000000000000 ;; + 0.000000000000000 ; -0.000102715219225 ; 0.002768824456286 ; 0.013936837294540 ; 0.042320490360991 ; 0.081223176170832 ; 0.100027475688989 ; 0.081223176170832 ; 0.042320490360991 ; 0.013936837294540 ; 0.002768824456286 ; -0.000102715219225 ; 0.000000000000000 ;; + 0.000000000000000 ; -0.000164682482058 ; 0.000767345547797 ; 0.004535511738255 ; 0.014877992136829 ; 0.029791500386470 ; 0.037097641503996 ; 0.029791500386470 ; 0.014877992136829 ; 0.004535511738255 ; 0.000767345547797 ; -0.000164682482058 ; 0.000000000000000 ;; + 0.000000000000000 ; -0.000091767217558 ; 0.000022353567868 ; 0.000693446611185 ; 0.002889786006700 ; 0.006284514040367 ; 0.007979739613849 ; 0.006284514040367 ; 0.002889786006700 ; 0.000693446611185 ; 0.000022353567868 ; -0.000091767217558 ; 0.000000000000000 ;; + 0.000000000000000 ; -0.000032024960906 ; -0.000089243114443 ; -0.000201682249005 ; -0.000175449913808 ; 0.000111463339173 ; 0.000290773816557 ; 0.000111463339173 ; -0.000175449913808 ; -0.000201682249005 ; -0.000089243114443 ; -0.000032024960906 ; 0.000000000000000 ;; + 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ]) +########################################################################################### +# to modify the test, with a new expected f, print the new f using the following commands +# in an interative Julia REPL. The path is the path to the .dfns file. +########################################################################################## +""" +fid = open_readonly_output_file(path, "dfns") +f_charged_vpavperpzrst = load_pdf_data(fid) +f_charged = f_charged_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]) + print("; ") + end + @printf("%.15f ", f_charged[nvpa,j,k]) + print(";;\n") + end + for i in 1:nvpa-1 + @printf("%.15f ", f_charged[i,nvperp,k]) + print("; ") + end + @printf("%.15f ", f_charged[nvpa,nvperp,k]) + if k < ntind + print(";;;\n") + end +end +""" # default inputs for tests -test_input_gauss_legendre = Dict("n_ion_species" => 1, - "n_neutral_species" => 0, - "boltzmann_electron_response" => true, - "run_name" => "gausslegendre_pseudospectral", - "base_directory" => test_output_directory, - "evolve_moments_density" => false, - "evolve_moments_parallel_flow" => false, - "evolve_moments_parallel_pressure" => false, - "evolve_moments_conservation" => false, - "initial_density1" => 0.5, - "initial_temperature1" => 1.0, - "initial_density2" => 0.5, - "initial_temperature2" => 1.0, - "z_IC_option1" => "sinusoid", - "z_IC_density_amplitude1" => 0.001, - "z_IC_density_phase1" => 0.0, - "z_IC_upar_amplitude1" => 0.0, - "z_IC_upar_phase1" => 0.0, - "z_IC_temperature_amplitude1" => 0.0, - "z_IC_temperature_phase1" => 0.0, - "z_IC_option2" => "sinusoid", - "z_IC_density_amplitude2" => 0.001, - "z_IC_density_phase2" => 0.0, - "z_IC_upar_amplitude2" => 0.0, - "z_IC_upar_phase2" => 0.0, - "z_IC_temperature_amplitude2" => 0.0, - "z_IC_temperature_phase2" => 0.0, - "T_e" => 1.0, - "charge_exchange_frequency" => 0.0, - "ionization_frequency" => 0.0, - "nuii" => 1.0, - "nstep" => 50000, - "dt" => 1.0e-3, - "nwrite" => 50000, - "nwrite_dfns" => 50000, - "use_semi_lagrange" => false, - "n_rk_stages" => 4, - "split_operators" => false, - "r_ngrid" => 1, - "r_nelement" => 1, - "r_nelement_local" => 1, - "r_bc" => "periodic", - "r_discretization" => "chebyshev_pseudospectral", - "z_ngrid" => 1, - "z_nelement" => 1, - "z_nelement_local" => 1, - "z_bc" => "wall", - "z_discretization" => "chebyshev_pseudospectral", - "vpa_ngrid" => 3, - "vpa_nelement" => 6, - "vpa_L" => 6.0, - "vpa_bc" => "zero", - "vpa_discretization" => "gausslegendre_pseudospectral", - "vperp_ngrid" => 3, - "vperp_nelement" => 3, - "vperp_L" => 3.0, - "vperp_bc" => "zero", - "vperp_discretization" => "gausslegendre_pseudospectral") +test_input_gauss_legendre = Dict("run_name" => "gausslegendre_pseudospectral", + "base_directory" => test_output_directory, + "n_ion_species" => 1, + "n_neutral_species" => 0, + "T_wall" => 1.0, + "T_e" => 1.0, + "initial_temperature2" => 1.0, + "vpa_ngrid" => 3, + "vpa_L" => 6.0, + "vpa_nelement" => 6, + "vpa_bc" => "zero", + "vpa_discretization" => "gausslegendre_pseudospectral", + "vperp_ngrid" => 3, + "vperp_nelement" => 3, + "vperp_L" => 3.0, + "vperp_bc" => "periodic", + "vperp_discretization" => "gausslegendre_pseudospectral", + "n_rk_stages" => 4, + "split_operators" => false, + "ionization_frequency" => 0.0, + "charge_exchange_frequency" => 0.0, + "constant_ionization_rate" => false, + "electron_physics" => "boltzmann_electron_response", + "nuii" => 1.0, + "use_semi_lagrange" => false, + "Bzed" => 1.0, + "Bmag" => 1.0, + "rhostar" => 1.0, + "z_IC_upar_amplitude1" => 0.0, + "z_IC_density_amplitude1" => 0.001, + "z_IC_upar_amplitude2" => 0.0, + "z_IC_temperature_phase1" => 0.0, + "z_IC_temperature_amplitude1" => 0.0, + "evolve_moments_parallel_pressure" => false, + "evolve_moments_conservation" => false, + "z_IC_option1" => "sinusoid", + "evolve_moments_parallel_flow" => false, + "z_IC_density_phase2" => 0.0, + "z_discretization" => "chebyshev_pseudospectral", + "z_IC_upar_phase2" => 0.0, + "evolve_moments_density" => false, + "z_IC_temperature_amplitude2" => 0.0, + "initial_density1" => 0.5, + "z_IC_upar_phase1" => 0.0, + "initial_density2" => 0.5, + "z_IC_density_phase1" => 0.0, + "z_IC_option2" => "sinusoid", + "z_IC_density_amplitude2" => 0.001, + "initial_temperature1" => 1.0, + "z_IC_temperature_phase2" => 0.0, + "z_ngrid" => 1, + "z_nelement_local" => 1, + "z_nelement" => 1, + "z_bc" => "wall", + "r_discretization" => "chebyshev_pseudospectral", + "r_ngrid" => 1, + "r_nelement" => 1, + "r_nelement_local" => 1, + "r_bc" => "periodic", + "dt" => 0.01, + "nstep" => 5000, + "nwrite" => 5000, + "nwrite_dfns" => 5000 ) # Not actually used in the tests, but needed for first argument of run_moment_kinetics @@ -163,8 +197,8 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) modified_inputs = Dict(String(k) => v for (k, v) in args) # Update default inputs with values to be changed - #input = merge(test_input, modified_inputs) - input = test_input + input = merge(test_input, modified_inputs) + #input = test_input input["run_name"] = name @@ -183,6 +217,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) v_t_charged = nothing dSdt = nothing f_charged = nothing + f_err = nothing vpa, vpa_spectral = nothing, nothing vperp, vperp_spectral = nothing, nothing @@ -232,7 +267,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) v_t_charged = v_t_charged_zrst[1,1,1,:] dSdt = dSdt_zrst[1,1,1,:] f_charged = f_charged_vpavperpzrst[:,:,1,1,1,:] - + f_err = copy(f_charged) # Unnormalize f # NEED TO UPGRADE TO 2V MOMENT KINETICS HERE @@ -261,6 +296,9 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) @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.dSdt[tind], dSdt[tind], atol=atol) + @. f_err = abs(expected.f_charged - f_charged) + 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) end end @@ -281,7 +319,7 @@ function runtests() # GaussLegendre pseudospectral # Benchmark data is taken from this run (GaussLegendre) @testset "Gauss Legendre base" begin - run_test(test_input_gauss_legendre, 1.e-2, 1.0e-2) + run_test(test_input_gauss_legendre, 1.e-14, 1.0e-14 ) end end end From c255f08d5307cbbd7d986cfb03cb0baefecdc948 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 21 Nov 2023 12:02:41 +0000 Subject: [PATCH 244/331] Pin to a particular HDF5_jll (HDF5_jll@1.12). --- Project.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 2d18d767a..608d77923 100644 --- a/Project.toml +++ b/Project.toml @@ -16,13 +16,14 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +HDF5_jll = "0234f1f7-429e-5d53-9886-15a909be8d59" IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" LegendrePolynomials = "3db4a2ba-fc88-11e8-3e01-49c72059a882" +LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LsqFit = "2fda8390-95c7-5789-9bda-21331edee243" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" @@ -37,8 +38,8 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Preferences = "21216c6a-2e73-6563-6e65-726566657250" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" -PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" From c4ef1e6d242e1062d517befeea732a7ea7e23ee5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 21 Nov 2023 18:53:42 +0000 Subject: [PATCH 245/331] Fix a shared-memory bug, and remove unnecessary commas from array slices. --- src/fokker_planck.jl | 11 ++++++----- src/fokker_planck_calculus.jl | 2 +- src/velocity_moments.jl | 16 ++++++++++------ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 85329f54f..d69c354a3 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -682,14 +682,15 @@ function symmetric_matrix_inverse(A00,A01,A02,A11,A12,A22,b0,b1,b2) end function conserving_corrections!(CC,pdf_in,vpa,vperp,dummy_vpavperp) + begin_serial_region() # compute moments of the input pdf dens = get_density(@view(pdf_in[:,:]), vpa, vperp) - upar = get_upar(@view(pdf_in[:,:,]), vpa, vperp, dens) - ppar = get_ppar(@view(pdf_in[:,:,]), vpa, vperp, upar) - pperp = get_pperp(@view(pdf_in[:,:,]), vpa, vperp) + upar = get_upar(@view(pdf_in[:,:]), vpa, vperp, dens) + ppar = get_ppar(@view(pdf_in[:,:]), vpa, vperp, upar) + pperp = get_pperp(@view(pdf_in[:,:]), vpa, vperp) pressure = get_pressure(ppar,pperp) - qpar = get_qpar(@view(pdf_in[:,:,]), vpa, vperp, upar, dummy_vpavperp) - rmom = get_rmom(@view(pdf_in[:,:,]), vpa, vperp, upar, dummy_vpavperp) + qpar = get_qpar(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) + rmom = get_rmom(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) # compute moments of the numerical collision operator dn = get_density(CC, vpa, vperp) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index d8ad16103..021175913 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -2188,7 +2188,7 @@ function enforce_vpavperp_BCs!(pdf,vpa,vperp,vpa_spectral,vperp_spectral) begin_vpa_region() @loop_vpa ivpa begin pdf[ivpa,nvperp] = 0.0 - pdf[ivpa,1,] = -sum(D0[2:ngrid_vperp].*pdf[ivpa,2:ngrid_vperp])/D0[1] + pdf[ivpa,1] = -sum(D0[2:ngrid_vperp].*pdf[ivpa,2:ngrid_vperp])/D0[1] end end diff --git a/src/velocity_moments.jl b/src/velocity_moments.jl index 2def0ada6..f545081f4 100644 --- a/src/velocity_moments.jl +++ b/src/velocity_moments.jl @@ -886,18 +886,22 @@ function get_qpar_1V(ff, vpa, vperp, upar) end function get_qpar(ff, vpa, vperp, upar, dummy_vpavperp) - @loop_vperp_vpa ivperp ivpa begin - wpar = vpa.grid[ivpa]-upar - dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*wpar*( wpar^2 + vperp.grid[ivperp]^2) + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + wpar = vpa.grid[ivpa]-upar + dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*wpar*( wpar^2 + vperp.grid[ivperp]^2) + end end return integrate_over_vspace(@view(dummy_vpavperp[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) end # generalised moment useful for computing numerical conserving terms in the collision operator function get_rmom(ff, vpa, vperp, upar, dummy_vpavperp) - @loop_vperp_vpa ivperp ivpa begin - wpar = vpa.grid[ivpa]-upar - dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*( wpar^2 + vperp.grid[ivperp]^2)^2 + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + wpar = vpa.grid[ivpa]-upar + dummy_vpavperp[ivpa,ivperp] = ff[ivpa,ivperp]*( wpar^2 + vperp.grid[ivperp]^2)^2 + end end return integrate_over_vspace(@view(dummy_vpavperp[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) end From 8e3d6268fb9af2d89ad1402014ac7eec23c7f690 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 09:46:16 +0000 Subject: [PATCH 246/331] Add optional argument "init_YY" to setup_gausslegendre_pseudospectral(). The default value is true (YY elemental arrays are initialised). If the value is set to false then the YY elemental arrays are not initialised, permitting an inclusion of the GaussLegendre grid option into the check-in tests with a much shorter initialisation time. --- src/coordinates.jl | 4 ++-- src/gauss_legendre.jl | 48 +++++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/coordinates.jl b/src/coordinates.jl index db4b006f6..beaea3ae8 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -103,7 +103,7 @@ create arrays associated with a given coordinate, setup the coordinate grid, and populate the coordinate structure containing all of this information """ -function define_coordinate(input, parallel_io::Bool=false) +function define_coordinate(input, parallel_io::Bool=false; init_YY::Bool=true) # total number of grid points is ngrid for the first element # plus ngrid-1 unique points for each additional element due # to the repetition of a point at the element boundary @@ -176,7 +176,7 @@ function define_coordinate(input, parallel_io::Bool=false) elseif input.discretization == "gausslegendre_pseudospectral" && coord.n > 1 # create arrays needed for explicit GaussLegendre pseudospectral treatment in this # coordinate and create the matrices for differentiation - spectral = setup_gausslegendre_pseudospectral(coord) + spectral = setup_gausslegendre_pseudospectral(coord,init_YY=init_YY) # obtain the local derivatives of the uniform grid with respect to the used grid derivative!(coord.duniform_dgrid, coord.uniform_grid, coord, spectral) else diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index af3476cce..12125b20b 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -97,9 +97,9 @@ struct gausslegendre_info{} Qmat::Array{mk_float,2} end -function setup_gausslegendre_pseudospectral(coord) - lobatto = setup_gausslegendre_pseudospectral_lobatto(coord) - radau = setup_gausslegendre_pseudospectral_radau(coord) +function setup_gausslegendre_pseudospectral(coord;init_YY=true) + lobatto = setup_gausslegendre_pseudospectral_lobatto(coord,init_YY=init_YY) + radau = setup_gausslegendre_pseudospectral_radau(coord,init_YY=init_YY) mass_matrix = allocate_float(coord.n,coord.n) S_matrix = allocate_float(coord.n,coord.n) K_matrix = allocate_float(coord.n,coord.n) @@ -114,7 +114,7 @@ function setup_gausslegendre_pseudospectral(coord) return gausslegendre_info(lobatto,radau,mass_matrix,sparse(S_matrix),K_matrix,L_matrix,mass_matrix_lu,Qmat) end -function setup_gausslegendre_pseudospectral_lobatto(coord) +function setup_gausslegendre_pseudospectral_lobatto(coord;init_YY=true) x, w = gausslobatto(coord.ngrid) Dmat = allocate_float(coord.ngrid, coord.ngrid) gausslobattolegendre_differentiation_matrix!(Dmat,x,coord.ngrid) @@ -144,28 +144,30 @@ function setup_gausslegendre_pseudospectral_lobatto(coord) D0 = allocate_float(coord.ngrid) #@. D0 = Dmat[1,:] # values at lower extreme of element GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,x,w) + Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,x,w,"Y00") Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,x,w,"Y01") Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,x,w,"Y10") Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,x,w,"Y11") Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,x,w,"Y20") Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,x,w,"Y21") Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,x,w,"Y30") Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,x,w,"Y31") - + if init_YY + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,x,w,"Y00") + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,x,w,"Y01") + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,x,w,"Y10") + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,x,w,"Y11") + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,x,w,"Y20") + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,x,w,"Y21") + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,x,w,"Y30") + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,x,w,"Y31") + end return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end -function setup_gausslegendre_pseudospectral_radau(coord) +function setup_gausslegendre_pseudospectral_radau(coord;init_YY=true) # Gauss-Radau points on [-1,1) x, w = gaussradau(coord.ngrid) # Gauss-Radau points on (-1,1] @@ -199,21 +201,23 @@ function setup_gausslegendre_pseudospectral_radau(coord) D0 = allocate_float(coord.ngrid) GaussLegendre_derivative_vector!(D0,-1.0,coord.ngrid,xreverse,wreverse,radau=true) Y00 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,xreverse,wreverse,"Y00",radau=true) Y01 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,xreverse,wreverse,"Y01",radau=true) Y10 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,xreverse,wreverse,"Y10",radau=true) Y11 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,xreverse,wreverse,"Y11",radau=true) Y20 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,xreverse,wreverse,"Y20",radau=true) Y21 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,xreverse,wreverse,"Y21",radau=true) Y30 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,xreverse,wreverse,"Y30",radau=true) Y31 = allocate_float(coord.ngrid, coord.ngrid, coord.ngrid) - GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,xreverse,wreverse,"Y31",radau=true) + if init_YY + GaussLegendre_weak_product_matrix!(Y00,coord.ngrid,xreverse,wreverse,"Y00",radau=true) + GaussLegendre_weak_product_matrix!(Y01,coord.ngrid,xreverse,wreverse,"Y01",radau=true) + GaussLegendre_weak_product_matrix!(Y10,coord.ngrid,xreverse,wreverse,"Y10",radau=true) + GaussLegendre_weak_product_matrix!(Y11,coord.ngrid,xreverse,wreverse,"Y11",radau=true) + GaussLegendre_weak_product_matrix!(Y20,coord.ngrid,xreverse,wreverse,"Y20",radau=true) + GaussLegendre_weak_product_matrix!(Y21,coord.ngrid,xreverse,wreverse,"Y21",radau=true) + GaussLegendre_weak_product_matrix!(Y30,coord.ngrid,xreverse,wreverse,"Y30",radau=true) + GaussLegendre_weak_product_matrix!(Y31,coord.ngrid,xreverse,wreverse,"Y31",radau=true) + end return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end From b5bc673a76b9c79241e15993fa6ca20358ee6edf Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 09:47:06 +0000 Subject: [PATCH 247/331] Inclusion of the "gausslegendre_pseudospectral" coordinate option into the check in tests for first derivatives. --- test/calculus_tests.jl | 350 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 349 insertions(+), 1 deletion(-) diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index 89b1e54dc..23988ee74 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -15,7 +15,7 @@ function runtests() println("calculus tests") @testset "fundamental theorem of calculus" begin @testset "$discretization $ngrid $nelement" for - (discretization, element_spacing_option, etol, cheb_option) ∈ (("finite_difference", "uniform", 1.0e-15, ""), ("chebyshev_pseudospectral", "uniform", 1.0e-15, "FFT"), ("chebyshev_pseudospectral", "uniform", 1.0e-15, "matrix"), ("chebyshev_pseudospectral", "sqrt", 1.0e-2, "FFT")), + (discretization, element_spacing_option, etol, cheb_option) ∈ (("finite_difference", "uniform", 1.0e-15, ""), ("chebyshev_pseudospectral", "uniform", 1.0e-15, "FFT"), ("chebyshev_pseudospectral", "uniform", 1.0e-15, "matrix"), ("chebyshev_pseudospectral", "sqrt", 1.0e-2, "FFT"), ("gausslegendre_pseudospectral", "uniform", 1.0e-14, "")), ngrid ∈ (5,6,7,8,9,10), nelement ∈ (1, 2, 3, 4, 5) if discretization == "finite_difference" && (ngrid - 1) * nelement % 2 == 1 @@ -788,6 +788,354 @@ function runtests() end end end + + @testset "GaussLegendre pseudospectral derivatives (4 argument), testing periodic functions" verbose=false begin + @testset "$nelement $ngrid" for (nelement, ngrid, rtol) ∈ + ( + (1, 5, 8.e-1), + (1, 6, 2.e-1), + (1, 7, 1.e-1), + (1, 8, 1.e-2), + (1, 9, 5.e-3), + (1, 10, 3.e-3), + (1, 11, 5.e-4), + (1, 12, 5.e-6), + (1, 13, 3.e-6), + (1, 14, 8.e-8), + (1, 15, 4.e-8), + (1, 16, 8.e-10), + (1, 17, 8.e-10), + + + (2, 4, 2.e-1), + (2, 5, 4.e-2), + (2, 6, 2.e-2), + (2, 7, 4.e-4), + (2, 8, 2.e-4), + (2, 9, 4.e-6), + (2, 10, 2.e-6), + (2, 11, 2.e-8), + (2, 12, 1.e-8), + (2, 13, 1.e-10), + (2, 14, 5.e-11), + (2, 15, 4.e-13), + (2, 16, 2.e-13), + (2, 17, 2.e-13), + + (3, 3, 4.e-1), + (3, 4, 1.e-1), + (3, 5, 1.e-2), + (3, 6, 2.e-3), + (3, 7, 1.e-4), + (3, 8, 1.e-5), + (3, 9, 6.e-7), + (3, 10, 5.e-8), + (3, 11, 2.e-9), + (3, 12, 1.e-10), + (3, 13, 5.e-12), + (3, 14, 3.e-13), + (3, 15, 2.e-13), + (3, 16, 2.e-13), + (3, 17, 2.e-13), + + (4, 3, 3.e-1), + (4, 4, 4.e-2), + (4, 5, 4.e-3), + (4, 6, 4.e-4), + (4, 7, 4.e-5), + (4, 8, 1.e-6), + (4, 9, 8.e-8), + (4, 10, 4.e-9), + (4, 11, 4.e-10), + (4, 12, 4.e-12), + (4, 13, 2.e-13), + (4, 14, 2.e-13), + (4, 15, 2.e-13), + (4, 16, 2.e-13), + (4, 17, 2.e-13), + + (5, 3, 2.e-1), + (5, 4, 2.e-2), + (5, 5, 2.e-3), + (5, 6, 1.e-4), + (5, 7, 1.e-5), + (5, 8, 2.e-7), + (5, 9, 2.e-8), + (5, 10, 3.e-10), + (5, 11, 2.e-11), + (5, 12, 3.e-13), + (5, 13, 2.e-13), + (5, 14, 2.e-13), + (5, 15, 2.e-13), + (5, 16, 2.e-13), + (5, 17, 4.e-13), + ) + + # define inputs needed for the test + L = 6.0 + bc = "periodic" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" + # create the 'input' struct containing input info needed to create a + # coordinate + nelement_local = nelement + nrank_per_block = 0 # dummy value + irank = 0 # dummy value + comm = MPI.COMM_NULL # dummy value + element_spacing_option = "uniform" + input = grid_input("coord", ngrid, nelement, + nelement_local, nrank_per_block, irank, L, + "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, + element_spacing_option) + # create the coordinate struct 'x' and info for derivatives, etc. + x, spectral = define_coordinate(input,init_YY=false) + + offset = randn(rng) + f = @. sinpi(2.0 * x.grid / L) + offset + expected_df = @. 2.0 * π / L * cospi(2.0 * x.grid / L) + + # create array for the derivative df/dx + df = similar(f) + + # differentiate f + derivative!(df, f, x, spectral) + + @test isapprox(df, expected_df, rtol=rtol, atol=1.e-14, + norm=maxabs_norm) + end + end + + @testset "GaussLegendre pseudospectral derivatives upwinding (5 argument), testing periodic functions" verbose=false begin + @testset "$nelement $ngrid" for (nelement, ngrid, rtol) ∈ + ( + (1, 5, 8.e-1), + (1, 6, 2.e-1), + (1, 7, 1.e-1), + (1, 8, 1.e-2), + (1, 9, 5.e-3), + (1, 10, 3.e-3), + (1, 11, 8.e-4), + (1, 12, 5.e-6), + (1, 13, 3.e-6), + (1, 14, 8.e-8), + (1, 15, 4.e-8), + (1, 16, 8.e-10), + (1, 17, 8.e-10), + + (2, 4, 2.e-1), + (2, 5, 4.e-2), + (2, 6, 2.e-2), + (2, 7, 4.e-4), + (2, 8, 2.e-4), + (2, 9, 4.e-6), + (2, 10, 2.e-6), + (2, 11, 2.e-8), + (2, 12, 1.e-8), + (2, 13, 1.e-10), + (2, 14, 5.e-11), + (2, 15, 4.e-13), + (2, 16, 2.e-13), + (2, 17, 2.e-13), + + (3, 3, 4.e-1), + (3, 4, 1.e-1), + (3, 5, 3.e-2), + (3, 6, 2.e-3), + (3, 7, 5.e-4), + (3, 8, 1.e-5), + (3, 9, 1.e-6), + (3, 10, 5.e-8), + (3, 11, 2.e-8), + (3, 12, 1.e-9), + (3, 13, 5.e-11), + (3, 14, 3.e-13), + (3, 15, 2.e-13), + (3, 16, 2.e-13), + (3, 17, 2.e-13), + + (4, 3, 3.e-1), + (4, 4, 4.e-2), + (4, 5, 4.e-3), + (4, 6, 4.e-4), + (4, 7, 4.e-5), + (4, 8, 4.e-6), + (4, 9, 8.e-8), + (4, 10, 4.e-9), + (4, 11, 5.e-10), + (4, 12, 4.e-12), + (4, 13, 2.e-13), + (4, 14, 2.e-13), + (4, 15, 2.e-13), + (4, 16, 2.e-13), + (4, 17, 2.e-13), + + (5, 3, 2.e-1), + (5, 4, 2.e-2), + (5, 5, 2.e-3), + (5, 6, 1.e-4), + (5, 7, 1.e-5), + (5, 8, 4.e-7), + (5, 9, 2.e-8), + (5, 10, 8.e-10), + (5, 11, 2.e-11), + (5, 12, 3.e-13), + (5, 13, 2.e-13), + (5, 14, 2.e-13), + (5, 15, 2.e-13), + (5, 16, 2.e-13), + (5, 17, 4.e-13), + ) + + # define inputs needed for the test + L = 6.0 + bc = "periodic" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" + # create the 'input' struct containing input info needed to create a + # coordinate + nelement_local = nelement + nrank_per_block = 0 # dummy value + irank = 0 # dummy value + comm = MPI.COMM_NULL # dummy value + element_spacing_option = "uniform" + input = grid_input("coord", ngrid, nelement, + nelement_local, nrank_per_block, irank, L, + "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, + element_spacing_option) + # create the coordinate struct 'x' and info for derivatives, etc. + x, spectral = define_coordinate(input,init_YY=false) + + offset = randn(rng) + f = @. sinpi(2.0 * x.grid / L) + offset + expected_df = @. 2.0 * π / L * cospi(2.0 * x.grid / L) + + # create array for the derivative df/dx + df = similar(f) + + for advection ∈ (-1.0, 0.0, 1.0) + adv_fac = similar(f) + adv_fac .= advection + + # differentiate f + derivative!(df, f, x, adv_fac, spectral) + + @test isapprox(df, expected_df, rtol=rtol, atol=1.e-12, + norm=maxabs_norm) + end + end + end + + @testset "GaussLegendre pseudospectral derivatives (4 argument), testing exact polynomials" verbose=false begin + @testset "$nelement $ngrid" for bc ∈ ("constant", "zero"), element_spacing_option ∈ ("uniform", "sqrt"), + nelement ∈ (1:5), ngrid ∈ (3:17) + + # define inputs needed for the test + L = 1.0 + bc = "constant" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" #not used + # create the 'input' struct containing input info needed to create a + # coordinate + nelement_local = nelement + nrank_per_block = 0 # dummy value + irank = 0 # dummy value + comm = MPI.COMM_NULL # dummy value + input = grid_input("coord", ngrid, nelement, + nelement_local, nrank_per_block, irank, L, + "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, + element_spacing_option) + # create the coordinate struct 'x' and info for derivatives, etc. + x, spectral = define_coordinate(input,init_YY=false) + # test polynomials up to order ngrid-1 + for n ∈ 0:ngrid-1 + # create array for the function f(x) to be differentiated/integrated + f = Array{Float64,1}(undef, x.n) + # create array for the derivative df/dx and the expected result + df = similar(f) + expected_df = similar(f) + # initialize f and expected df + f[:] .= randn(rng) + expected_df .= 0.0 + for p ∈ 1:n + coefficient = randn(rng) + @. f += coefficient * x.grid ^ p + @. expected_df += coefficient * p * x.grid ^ (p - 1) + end + # differentiate f + derivative!(df, f, x, spectral) + + # Note the error we might expect for a p=32 polynomial is probably + # something like p*(round-off) for x^p (?) so error on expected_df would + # be p*p*(round-off), or plausibly 1024*(round-off), so tolerance of + # 2e-11 isn't unreasonable. + @test isapprox(df, expected_df, rtol=2.0e-11, atol=6.0e-12, + norm=maxabs_norm) + end + end + end + + @testset "GaussLegendre pseudospectral derivatives upwinding (5 argument), testing exact polynomials" verbose=false begin + @testset "$nelement $ngrid" for bc ∈ ("constant", "zero"), element_spacing_option ∈ ("uniform", "sqrt"), + nelement ∈ (1:5), ngrid ∈ (3:17) + + # define inputs needed for the test + L = 1.0 + bc = "constant" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" # not used + # create the 'input' struct containing input info needed to create a + # coordinate + nelement_local = nelement + nrank_per_block = 0 # dummy value + irank = 0 # dummy value + comm = MPI.COMM_NULL # dummy value + input = grid_input("coord", ngrid, nelement, + nelement_local, nrank_per_block, irank, L, + "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, + element_spacing_option) + # create the coordinate struct 'x' and info for derivatives, etc. + x, spectral = define_coordinate(input,init_YY=false) + # test polynomials up to order ngrid-1 + for n ∈ 0:ngrid-1 + # create array for the function f(x) to be differentiated/integrated + f = Array{Float64,1}(undef, x.n) + # create array for the derivative df/dx and the expected result + df = similar(f) + expected_df = similar(f) + # initialize f and expected df + f[:] .= randn(rng) + expected_df .= 0.0 + for p ∈ 1:n + coefficient = randn(rng) + @. f += coefficient * x.grid ^ p + @. expected_df += coefficient * p * x.grid ^ (p - 1) + end + + for advection ∈ (-1.0, 0.0, 1.0) + adv_fac = similar(f) + adv_fac .= advection + + # differentiate f + derivative!(df, f, x, adv_fac, spectral) + + # Note the error we might expect for a p=32 polynomial is probably + # something like p*(round-off) for x^p (?) so error on expected_df + # would be p*p*(round-off), or plausibly 1024*(round-off), so + # tolerance of 3e-11 isn't unreasonable. + @test isapprox(df, expected_df, rtol=3.0e-11, atol=3.0e-11, + norm=maxabs_norm) + end + end + end + end @testset "Chebyshev pseudospectral second derivatives (4 argument), periodic" verbose=false begin @testset "$nelement $ngrid" for (nelement, ngrid, rtol) ∈ From 028f285364ecbfdbdcd9f2b41f20dcc168a8bb0c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 10:24:34 +0000 Subject: [PATCH 248/331] Add test of second derivative function to test/calculus_tests.jl for gausslegendre_pseudospectral option. --- test/calculus_tests.jl | 108 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index 23988ee74..c6fa17e8b 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -1334,6 +1334,114 @@ function runtests() norm=maxabs_norm) end end + + @testset "GaussLegendre pseudospectral second derivatives (4 argument), periodic" verbose=false begin + @testset "$nelement $ngrid" for (nelement, ngrid, rtol) ∈ + ( + (1, 8, 2.e-2), + (1, 9, 5.e-3), + (1, 10, 3.e-3), + (1, 11, 2.e-4), + (1, 12, 2.e-5), + (1, 13, 4.e-6), + (1, 14, 4.e-7), + (1, 15, 1.e-7), + (1, 16, 5.e-9), + (1, 17, 1.e-9), + + (2, 4, 2.e-1), + (2, 5, 5.e-2), + (2, 6, 2.e-2), + (2, 7, 2.e-3), + (2, 8, 2.e-4), + (2, 9, 2.e-5), + (2, 10, 4.e-6), + (2, 11, 2.e-7), + (2, 12, 4.e-8), + (2, 13, 8.e-10), + (2, 14, 2.e-10), + (2, 15, 4.e-13), + (2, 16, 2.e-13), + (2, 17, 2.e-13), + + (3, 5, 1.e-1), + (3, 6, 2.e-2), + (3, 7, 2.e-3), + (3, 8, 2.e-4), + (3, 9, 1.e-4), + (3, 10, 4.e-6), + (3, 11, 1.e-7), + (3, 12, 8.e-9), + (3, 13, 8.e-10), + (3, 14, 3.e-10), + (3, 15, 2.e-10), + (3, 16, 2.e-10), + (3, 17, 2.e-10), + + (4, 5, 5.e-2), + (4, 6, 2.e-2), + (4, 7, 2.e-3), + (4, 8, 2.e-4), + (4, 9, 1.e-4), + (4, 10, 1.e-6), + (4, 11, 8.e-9), + (4, 12, 8.e-10), + (4, 13, 8.e-10), + (4, 14, 8.e-10), + (4, 15, 8.e-10), + (4, 16, 8.e-10), + (4, 17, 8.e-10), + + (5, 5, 4.e-2), + (5, 6, 8.e-3), + (5, 7, 5.e-4), + (5, 8, 5.e-5), + (5, 9, 8.e-7), + (5, 10, 5.e-8), + (5, 11, 8.e-10), + (5, 12, 4.e-10), + (5, 13, 2.e-10), + (5, 14, 2.e-10), + (5, 15, 8.e-10), + (5, 16, 8.e-10), + (5, 17, 8.e-10), + ) + + # define inputs needed for the test + L = 6.0 + bc = "periodic" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + cheb_option = "" + # create the 'input' struct containing input info needed to create a + # coordinate + nelement_local = nelement + nrank_per_block = 0 # dummy value + irank = 0 # dummy value + comm = MPI.COMM_NULL # dummy value + element_spacing_option = "uniform" + input = grid_input("coord", ngrid, nelement, + nelement_local, nrank_per_block, irank, L, + "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, + element_spacing_option) + # create the coordinate struct 'x' and info for derivatives, etc. + x, spectral = define_coordinate(input,init_YY=false) + + offset = randn(rng) + f = @. sinpi(2.0 * x.grid / L) + offset + expected_d2f = @. -4.0 * π^2 / L^2 * sinpi(2.0 * x.grid / L) + + # create array for the derivative d2f/dx2 + d2f = similar(f) + + # differentiate f + second_derivative!(d2f, f, x, spectral) + + @test isapprox(d2f, expected_d2f, rtol=rtol, atol=1.e-10, + norm=maxabs_norm) + end + end end end From 10c1cd6f403b91f4e71bcc0abf914ed653f7f6fc Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 13:26:12 +0000 Subject: [PATCH 249/331] Fix initialisation bug where vperp.ngrid and vpa.ngrid are mixed up. --- src/fokker_planck_calculus.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 021175913..2d846a431 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1353,7 +1353,7 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe LLperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) PPperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) PUperp = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) - PPpar = Array{mk_float,2}(undef,vperp.ngrid,vperp.ngrid) + PPpar = Array{mk_float,2}(undef,vpa.ngrid,vpa.ngrid) impose_BC_at_zero_vperp = false @serial_region begin @@ -1576,7 +1576,7 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp LLperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) PPperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) PUperp = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) - PPpar = Array{mk_float,2}(undef,ngrid_vperp,ngrid_vperp) + PPpar = Array{mk_float,2}(undef,ngrid_vpa,ngrid_vpa) impose_BC_at_zero_vperp = false @serial_region begin From c3b6261115ba671cf338e88bbb762de121f59c82 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 13:38:46 +0000 Subject: [PATCH 250/331] Commit removing the factor of Q(coord) from the second derivative functions in the chebyshev implementation. At the same time, refactor the numerical dissipation routines to have the same behaviour. Correct a minor bug in time_advance.jl to permit r.n > 1. Include the numerical dissipation terms in the manufactured solutions module. MMS tests do not currently pass. --- src/calculus.jl | 10 +--- src/manufactured_solns.jl | 22 ++++++- src/numerical_dissipation.jl | 111 +++++++++++++++-------------------- src/time_advance.jl | 4 +- test/calculus_tests.jl | 3 +- 5 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/calculus.jl b/src/calculus.jl index 31ce17442..acbca65d7 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -464,24 +464,20 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, _block_synchronize() end -function second_derivative!(d2f, f, Q, coord, spectral) - # computes d / d coord ( Q . d f / d coord) +function second_derivative!(d2f, f, coord, spectral::chebyshev_info) # For spectral element methods, calculate second derivative by applying first # derivative twice, with special treatment for element boundaries # First derivative chebyshev_derivative!(coord.scratch_2d, f, spectral, coord) - derivative_elements_to_full_grid!(coord.scratch3, coord.scratch_2d, coord) + derivative_elements_to_full_grid!(coord.scratch2, coord.scratch_2d, coord) # MPI reconcile code here if used with z or r coords # Save elementwise first derivative result coord.scratch2_2d .= coord.scratch_2d - #form Q . d f / d coord - coord.scratch3 .= Q .* coord.scratch3 - # Second derivative for element interiors - chebyshev_derivative!(coord.scratch_2d, coord.scratch3, spectral, coord) + chebyshev_derivative!(coord.scratch_2d, coord.scratch2, spectral, coord) derivative_elements_to_full_grid!(d2f, coord.scratch_2d, coord) # MPI reconcile code here if used with z or r coords diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index ef18ba915..71eec2af5 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -483,13 +483,13 @@ using IfElse Dz = Differential(z) Dvpa = Differential(vpa) Dvperp = Differential(vperp) + Dvz = Differential(vz) Dt = Differential(t) # get geometric/composition data Bzed = geometry.Bzed Bmag = geometry.Bmag rhostar = geometry.rhostar - nu_krook = collisions.nuii_krook #exceptions for cases with missing terms if composition.n_neutral_species > 0 cx_frequency = collisions.charge_exchange @@ -512,7 +512,14 @@ using IfElse # the ion source to maintain the manufactured solution Si = ( Dt(dfni) + ( vpa * (Bzed/Bmag) - 0.5*rhostar*Er ) * Dz(dfni) + ( 0.5*rhostar*Ez*rfac ) * Dr(dfni) + ( 0.5*Ez*Bzed/Bmag ) * Dvpa(dfni) + cx_frequency*( densn*dfni - densi*gav_dfnn ) - ionization_frequency*dense*gav_dfnn) + nu_krook = collisions.krook_collision_frequency_prefactor if nu_krook > 0.0 + Ti_over_Tref = vthi^2 + if collisions.krook_collisions_option == "manual" + nuii_krook = nu_krook + else # default option + nuii_krook = nu_krook * densi * Ti_over_Tref^(-1.5) + end if vperp_coord.n > 1 pvth = 3 else @@ -525,6 +532,9 @@ using IfElse if num_diss_params.vpa_dissipation_coefficient > 0.0 && include_num_diss_in_MMS Si += - num_diss_params.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)) + 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)) end @@ -553,6 +563,16 @@ using IfElse # 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)) + 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)) + 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)) + end + Source_n = expand_derivatives(Sn) Source_i_func = build_function(Source_i, vpa, vperp, z, r, t, expression=Val{false}) diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index c6a4a34bb..136fb701f 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -262,41 +262,33 @@ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, # # expected convergence of Chebyshev pseudospectral scheme # diffusion_coefficient *= (vpa.L/vpa.nelement)^(vpa.ngrid-1) # end - if vpa.discretization == "gausslegendre_pseudospectral" - @loop_s_r_z_vperp is ir iz ivperp begin - @views second_derivative!(vpa.scratch2, f_in[:,ivperp,iz,ir,is], vpa, spectral) - @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch2 - end - else - @loop_s_r_z_vperp is ir iz ivperp begin - # # Don't want to dissipate the fluid moments, so divide out the Maxwellian, then - # # diffuse the result, i.e. - # # df/dt += diffusion_coefficient * f_M d2(f/f_M)/dvpa2 - # # Store f_M in vpa.scratch - # if (moments.evolve_ppar || moments.evolve_vth) && moments.evolve_upar - # @views @. vpa.scratch = exp(-vpa.grid^2) - # elseif moments.evolve_ppar || moments.evolve_vth - # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) - # @views @. vpa.scratch = exp(-(vpa.grid - fvec_in.upar[iz,ir,is]/vth)^2) - # elseif moments.evolve_upar - # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) - # @views @. vpa.scratch = exp(-(vpa.grid/vth)^2) - # elseif moments.evolve_density - # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) - # @views @. vpa.scratch = exp(-((vpa.grid - fvec_in.upar[iz,ir,is])/vth)^2) - # else - # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) - # @views @. vpa.scratch = (fvec_in.density[iz,ir,is] * - # exp(-((vpa.grid - fvec_in.upar[iz,ir,is])/vth)^2)) - # end - # @views @. vpa.scratch2 = fvec_in.pdf[:,iz,ir,is] / vpa.scratch - # derivative!(vpa.scratch3, vpa.scratch2, vpa, spectral, Val(2)) - # @views @. f_out[:,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch * - # vpa.scratch3 - vpa.scratch2 .= 1.0 # placeholder for Q in d / d vpa ( Q d f / d vpa) - @views second_derivative!(vpa.scratch, f_in[:,ivperp,iz,ir,is], vpa.scratch2, vpa, spectral) - @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch - end + @loop_s_r_z_vperp is ir iz ivperp begin + # # Don't want to dissipate the fluid moments, so divide out the Maxwellian, then + # # diffuse the result, i.e. + # # df/dt += diffusion_coefficient * f_M d2(f/f_M)/dvpa2 + # # Store f_M in vpa.scratch + # if (moments.evolve_ppar || moments.evolve_vth) && moments.evolve_upar + # @views @. vpa.scratch = exp(-vpa.grid^2) + # elseif moments.evolve_ppar || moments.evolve_vth + # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) + # @views @. vpa.scratch = exp(-(vpa.grid - fvec_in.upar[iz,ir,is]/vth)^2) + # elseif moments.evolve_upar + # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) + # @views @. vpa.scratch = exp(-(vpa.grid/vth)^2) + # elseif moments.evolve_density + # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) + # @views @. vpa.scratch = exp(-((vpa.grid - fvec_in.upar[iz,ir,is])/vth)^2) + # else + # vth = sqrt(2.0*fvec_in.ppar[iz,ir,is]/fvec_in.density[iz,ir,is]) + # @views @. vpa.scratch = (fvec_in.density[iz,ir,is] * + # exp(-((vpa.grid - fvec_in.upar[iz,ir,is])/vth)^2)) + # end + # @views @. vpa.scratch2 = fvec_in.pdf[:,iz,ir,is] / vpa.scratch + # derivative!(vpa.scratch3, vpa.scratch2, vpa, spectral, Val(2)) + # @views @. f_out[:,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch * + # vpa.scratch3 + @views second_derivative!(vpa.scratch, f_in[:,ivperp,iz,ir,is], vpa, spectral) + @views @. f_out[:,ivperp,iz,ir,is] += dt * diffusion_coefficient * vpa.scratch end return nothing end @@ -323,9 +315,8 @@ function vperp_dissipation!(f_out, f_in, vperp, spectral::T_spectral, dt, end @loop_s_r_z_vpa is ir iz ivpa begin - @views derivative!(vperp.scratch, f_in[ivpa,:,iz,ir,is], vperp, spectral) - @views derivative!(vperp.scratch2, vperp.scratch, vperp, spectral) - @views @. f_out[ivpa,:,iz,ir,is] += dt * diffusion_coefficient * vperp.scratch2 + @views second_derivative!(vperp.scratch, f_in[ivpa,:,iz,ir,is], vperp, spectral) + @views @. f_out[ivpa,:,iz,ir,is] += dt * diffusion_coefficient * vperp.scratch end return nothing @@ -357,23 +348,21 @@ function z_dissipation!(f_out, f_in, z, z_spectral::T_spectral, dt, begin_s_r_vperp_vpa_region() - # calculate d / d z ( Q d f / d z ) using distributed memory compatible routines + # calculate d^2 f / d z^2 using distributed memory compatible routines # first compute d f / d z using centred reconciliation and place in dummy array #1 derivative_z!(scratch_dummy.buffer_vpavperpzrs_1, f_in[:,:,:,:,:], scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, scratch_dummy.buffer_vpavperprs_3,scratch_dummy.buffer_vpavperprs_4, z_spectral,z) - # form Q d f / d r and place in dummy array #2 @loop_s_r_vperp_vpa is ir ivperp ivpa begin - Q = 1.0 # placeholder for geometrical or velocity space dependent metric coefficient - @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,:,ir,is] = Q * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,:,ir,is] + @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,:,ir,is] = scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,:,ir,is] end - # compute d / d z ( Q d f / d z ) using centred reconciliation and place in dummy array #1 + # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #1 derivative_z!(scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperpzrs_2[:,:,:,:,:], scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, scratch_dummy.buffer_vpavperprs_3,scratch_dummy.buffer_vpavperprs_4, z_spectral,z) - # advance f due to diffusion_coefficient * d / d z ( Q d f / d z ) + # advance f due to diffusion_coefficient * d^2 f / d z^2 @loop_s_r_vperp_vpa is ir ivperp ivpa begin @views @. f_out[ivpa,ivperp,:,ir,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,:,ir,is] end @@ -408,23 +397,21 @@ function r_dissipation!(f_out, f_in, r, r_spectral::T_spectral, dt, begin_s_z_vperp_vpa_region() - # calculate d / d r ( Q d f / d r ) using distributed memory compatible routines + # calculate d^2 f / d r^2 using distributed memory compatible routines # first compute d f / d r using centred reconciliation and place in dummy array #1 derivative_r!(scratch_dummy.buffer_vpavperpzrs_1, f_in[:,:,:,:,:], scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2, scratch_dummy.buffer_vpavperpzs_3,scratch_dummy.buffer_vpavperpzs_4, r_spectral,r) - # form Q d f / d r and place in dummy array #2 @loop_s_z_vperp_vpa is iz ivperp ivpa begin - Q = 1.0 # placeholder for geometrical or velocity space dependent metric coefficient - @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,iz,:,is] = Q * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,iz,:,is] + @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,iz,:,is] = scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,iz,:,is] end - # compute d / d r ( Q d f / d r ) using centred reconciliation and place in dummy array #1 + # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #1 derivative_r!(scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperpzrs_2[:,:,:,:,:], scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2, scratch_dummy.buffer_vpavperpzs_3,scratch_dummy.buffer_vpavperpzs_4, r_spectral,r) - # advance f due to diffusion_coefficient * d / d r ( Q d f / d r ) + # advance f due to diffusion_coefficient * d^2 f / d r^2 @loop_s_z_vperp_vpa is iz ivperp ivpa begin @views @. f_out[ivpa,ivperp,iz,:,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,iz,:,is] end @@ -454,8 +441,7 @@ function vz_dissipation_neutral!(f_out, f_in, vz, spectral::T_spectral, dt, begin_sn_r_z_vzeta_vr_region() @loop_sn_r_z_vzeta_vr isn ir iz ivzeta ivr begin - vz.scratch2 .= 1.0 # placeholder for Q in d / d vpa ( Q d f / d vpa) - @views second_derivative!(vz.scratch, f_in[:,ivr,ivzeta,iz,ir,isn], vz.scratch2, vz, spectral) + @views second_derivative!(vz.scratch, f_in[:,ivr,ivzeta,iz,ir,isn], vz, spectral) @views @. f_out[:,ivr,ivzeta,iz,ir,isn] += dt * diffusion_coefficient * vz.scratch end @@ -488,25 +474,23 @@ function z_dissipation_neutral!(f_out, f_in, z, z_spectral::T_spectral, dt, begin_sn_r_vzeta_vr_vz_region() - # calculate d / d z ( Q d f / d z ) using distributed memory compatible routines + # calculate d^2 f / d z^2 using distributed memory compatible routines # first compute d f / d z using centred reconciliation and place in dummy array #1 derivative_z!(scratch_dummy.buffer_vzvrvzetazrsn_1, f_in, scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, scratch_dummy.buffer_vzvrvzetarsn_3,scratch_dummy.buffer_vzvrvzetarsn_4, z_spectral,z) - # form Q d f / d r and place in dummy array #2 @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - Q = 1.0 # placeholder for geometrical or velocity space dependent metric coefficient - @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,:,ir,isn] = Q * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,:,ir,isn] + @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,:,ir,isn] = scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,:,ir,isn] end - # compute d / d z ( Q d f / d z ) using centred reconciliation and place in dummy array #1 + # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #1 derivative_z!(scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetazrsn_2, scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, scratch_dummy.buffer_vzvrvzetarsn_3,scratch_dummy.buffer_vzvrvzetarsn_4, z_spectral,z) - # advance f due to diffusion_coefficient * d / d z ( Q d f / d z ) + # advance f due to diffusion_coefficient * d^2 f/ d z^2 @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - @views @. f_out[ivz,ivr,ivzeta,:,ir,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivz,ivr,ivzeta,:,ir,isn] + @views @. f_out[ivz,ivr,ivzeta,:,ir,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,:,ir,isn] end return nothing @@ -539,7 +523,7 @@ function r_dissipation_neutral!(f_out, f_in, r, r_spectral::T_spectral, dt, begin_sn_z_vzeta_vr_vz_region() - # calculate d / d r ( Q d f / d r ) using distributed memory compatible routines + # calculate d^2 f/ d r^2 using distributed memory compatible routines # first compute d f / d r using centred reconciliation and place in dummy array #1 derivative_r!(scratch_dummy.buffer_vzvrvzetazrsn_1, f_in, scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, @@ -547,17 +531,16 @@ function r_dissipation_neutral!(f_out, f_in, r, r_spectral::T_spectral, dt, r_spectral,r) # form Q d f / d r and place in dummy array #2 @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - Q = 1.0 # placeholder for geometrical or velocity space dependent metric coefficient - @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,iz,:,isn] = Q * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,iz,:,isn] + @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,iz,:,isn] = scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,iz,:,isn] end - # compute d / d r ( Q d f / d r ) using centred reconciliation and place in dummy array #1 + # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #1 derivative_r!(scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetazrsn_2, scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, scratch_dummy.buffer_vzvrvzetazsn_3,scratch_dummy.buffer_vzvrvzetazsn_4, r_spectral,r) # advance f due to diffusion_coefficient * d / d r ( Q d f / d r ) @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - @views @. f_out[ivz,ivr,ivzeta,iz,:,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivz,ivr,ivzeta,iz,:,isn] + @views @. f_out[ivz,ivr,ivzeta,iz,:,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,iz,:,isn] end return nothing diff --git a/src/time_advance.jl b/src/time_advance.jl index eeeafd3eb..48457eacf 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -245,7 +245,9 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, # initialise the r advection speed begin_s_z_vperp_vpa_region() @loop_s is begin - @views update_speed_r!(r_advect[is], fields, vpa, vperp, z, r, geometry) + @views update_speed_r!(r_advect[is], moments.charged.upar[:,:,is], + moments.charged.vth[:,:,is], fields, moments.evolve_upar, + moments.evolve_ppar, vpa, vperp, z, r, geometry) end # enforce prescribed boundary condition in r on the distribution function f end diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index c6fa17e8b..d0fab3707 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -1327,8 +1327,7 @@ function runtests() d2f = similar(f) # differentiate f - x.scratch2 .= 1.0 # placeholder for Q in d / d x ( Q d f / d x) - second_derivative!(d2f, f, x.scratch2, x, spectral) + second_derivative!(d2f, f, x, spectral) @test isapprox(d2f, expected_d2f, rtol=rtol, atol=1.e-10, norm=maxabs_norm) From 6f5193d4b7cfa59d82337f5cdf57da0d2854a8c5 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 22 Nov 2023 14:06:11 +0000 Subject: [PATCH 251/331] Example input file using the Fokker-Planck operator, based on the same example used in test/fokker_planck_time_evolution_tests.jl --- .../fokker-planck-relaxation.toml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 examples/fokker-planck/fokker-planck-relaxation.toml diff --git a/examples/fokker-planck/fokker-planck-relaxation.toml b/examples/fokker-planck/fokker-planck-relaxation.toml new file mode 100644 index 000000000..45c4bd352 --- /dev/null +++ b/examples/fokker-planck/fokker-planck-relaxation.toml @@ -0,0 +1,67 @@ +# cheap input file for a 0D2V relaxation to a collisional Maxwellian distribution with self-ion collisions. +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +constant_ionization_rate = false +# nuii sets the normalised input C[F,F] Fokker-Planck collision frequency +nuii = 1.0 +nstep = 5000 +dt = 1.0e-2 +nwrite = 5000 +nwrite_dfns = 5000 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 1 +z_nelement = 1 +z_nelement_local = 1 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 3 +vpa_nelement = 6 +vpa_L = 6.0 +vpa_bc = "zero" +vpa_discretization = "gausslegendre_pseudospectral" +vperp_ngrid = 3 +vperp_nelement = 3 +vperp_L = 3.0 +# vperp_bc variable not used +vperp_bc = "periodic" +vperp_discretization = "gausslegendre_pseudospectral" +# Fokker-Planck operator requires the "gausslegendre_pseudospectral +# options for the vpa and vperp grids + From f3b96e5a408ca945df9aa59680f50ffc7c841b9e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Wed, 22 Nov 2023 20:44:41 +0000 Subject: [PATCH 252/331] Refactor calculus.jl, discretizations to have same structure as master --- src/calculus.jl | 313 ++++++++++++++-------------- src/chebyshev.jl | 25 ++- src/coordinates.jl | 17 +- src/finite_differences.jl | 33 ++- src/gauss_legendre.jl | 34 +-- src/hermite_spline_interpolation.jl | 10 +- src/moment_kinetics.jl | 2 +- src/moment_kinetics_structs.jl | 24 ++- test/calculus_tests.jl | 15 +- 9 files changed, 271 insertions(+), 202 deletions(-) diff --git a/src/calculus.jl b/src/calculus.jl index acbca65d7..05536fd67 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -6,101 +6,180 @@ export derivative!, second_derivative!, laplacian_derivative! export reconcile_element_boundaries_MPI! export integral - -using ..chebyshev: chebyshev_info, chebyshev_derivative! -using ..gauss_legendre: gausslegendre_info, gausslegendre_derivative! -using ..gauss_legendre: gausslegendre_apply_Kmat!, gausslegendre_mass_matrix_solve! -using ..gauss_legendre: gausslegendre_apply_Lmat! -using ..finite_differences: derivative_finite_difference! +using ..moment_kinetics_structs: discretization_info, null_spatial_dimension_info, + null_velocity_dimension_info, weak_discretization_info using ..type_definitions: mk_float, mk_int -using MPI +using MPI using ..communication: block_rank using ..communication: _block_synchronize using ..looping """ -Use Gauss-Legendre differentiation matrices to take the first derivative + elementwise_derivative!(coord, f, adv_fac, spectral) + elementwise_derivative!(coord, f, spectral) + +Generic function for element-by-element derivatives + +First signature, with `adv_fac`, calculates an upwind derivative, the second signature +calculates a derivative without upwinding information. + +Result is stored in coord.scratch_2d. """ -function derivative!(df, f, coord, adv_fac, spectral::gausslegendre_info) - # get the derivative at each grid point within each element and store in df - gausslegendre_derivative!(coord.scratch_2d, f, spectral, coord) +function elementwise_derivative! end + +""" + elementwise_second_derivative!(coord, f, spectral) + +Generic function for element-by-element second derivatives. + +Note: no upwinding versions of second deriatives. + +Result is stored in coord.scratch_2d. +""" +function elementwise_second_derivative! end + +""" + derivative!(df, f, coord, adv_fac, spectral) + +Upwinding derivative. +""" +function derivative!(df, f, coord, adv_fac, spectral::discretization_info) + # get the derivative at each grid point within each element and store in + # coord.scratch_2d + elementwise_derivative!(coord, f, adv_fac, spectral) # map the derivative from the elemental grid to the full grid; # at element boundaries, use the derivative from the upwind element. derivative_elements_to_full_grid!(df, coord.scratch_2d, coord, adv_fac) end -function derivative!(df, f, coord, spectral::gausslegendre_info) - # get the derivative at each grid point within each element and store in df - gausslegendre_derivative!(coord.scratch_2d, f, spectral, coord) +""" + derivative!(df, f, coord, spectral) + +Non-upwinding derivative. +""" +function derivative!(df, f, coord, spectral) + # get the derivative at each grid point within each element and store in + # coord.scratch_2d + elementwise_derivative!(coord, f, spectral) # map the derivative from the elemental grid to the full grid; # at element boundaries, use the average of the derivatives from neighboring elements. derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) end -function second_derivative!(d2f, f, coord, spectral::gausslegendre_info) - # get the derivative at each grid point within each element and store in df - gausslegendre_apply_Kmat!(coord.scratch_2d, f, spectral, coord) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the average of the derivatives from neighboring elements. - derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) - # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) +# Special versions for 'null' coordinates with only one point +function derivative!(df, f, coord, spectral::Union{null_spatial_dimension_info, + null_velocity_dimension_info}) + df .= 0.0 + return nothing end -function laplacian_derivative!(d2f, f, coord, spectral::gausslegendre_info) - # get the derivative at each grid point within each element and store in df - gausslegendre_apply_Lmat!(coord.scratch_2d, f, spectral, coord) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the average of the derivatives from neighboring elements. - derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) - # solve weak form problem M * d2f = K * f - gausslegendre_mass_matrix_solve!(d2f,coord.scratch,spectral) +function second_derivative!(d2f, f, coord, spectral) + # computes d^2f / d(coord)^2 + # For spectral element methods, calculate second derivative by applying first + # derivative twice, with special treatment for element boundaries + + # First derivative + elementwise_derivative!(coord, f, spectral) + derivative_elements_to_full_grid!(coord.scratch3, coord.scratch_2d, coord) + # MPI reconcile code here if used with z or r coords + + # Save elementwise first derivative result + coord.scratch2_2d .= coord.scratch_2d + + # Second derivative for element interiors + elementwise_derivative!(coord, coord.scratch3, spectral) + derivative_elements_to_full_grid!(d2f, coord.scratch_2d, coord) + # MPI reconcile code here if used with z or r coords + + # Add contribution to penalise discontinuous first derivatives at element + # boundaries. For smooth functions this would do nothing so should not affect + # convergence of the second derivative. Aims to stabilise numerical instability when + # spike develops at an element boundary. The coefficient is an arbitrary choice, it + # should probably be large enough for stability but as small as possible. + # + # Arbitrary numerical coefficient + C = 1.0 + function penalise_discontinuous_first_derivative!(d2f, imin, imax, df) + # Left element boundary + d2f[imin] += C * df[1] + + # Right element boundary + d2f[imax] -= C * df[end] + + return nothing + end + @views penalise_discontinuous_first_derivative!(d2f, 1, coord.imax[1], + coord.scratch2_2d[:,1]) + for ielement ∈ 2:coord.nelement_local + @views penalise_discontinuous_first_derivative!(d2f, coord.imin[ielement]-1, + coord.imax[ielement], + coord.scratch2_2d[:,ielement]) + end + + if coord.bc ∈ ("wall", "zero", "both_zero") + # For stability don't contribute to evolution at boundaries, in case these + # points are not set by a boundary condition. + # Full grid may be across processes and bc only applied to extreme ends of the + # domain. + if coord.irank == 0 + d2f[1] = 0.0 + end + if coord.irank == coord.nrank - 1 + d2f[end] = 0.0 + end + elseif coord.bc == "periodic" + # Need to get first derivatives from opposite ends of grid + if coord.nelement_local != coord.nelement_global + error("Distributed memory MPI not yet supported here") + end + d2f[1] -= C * coord.scratch2_2d[end,end] + d2f[end] += C * coord.scratch2_2d[1,1] + else + error("Unsupported bc '$(coord.bc)'") + end + return nothing end + """ -Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' + mass_matrix_solve!(f, b, spectral::weak_discretization_info) + +Solve +```math +M.f = b +``` +for \$a\$, where \$M\$ is the mass matrix of a weak-form finite element method and \$b\$ +is an input. """ -function derivative!(df, f, coord, adv_fac, spectral::chebyshev_info) - # get the derivative at each grid point within each element and store in df - chebyshev_derivative!(coord.scratch_2d, f, spectral, coord) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the derivative from the upwind element. - derivative_elements_to_full_grid!(df, coord.scratch_2d, coord, adv_fac) -end +function mass_matrix_solve! end """ -Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f' +Apply 'K-matrix' as part of a weak-form second derivative """ -function derivative!(df, f, coord, spectral::chebyshev_info) +function elementwise_apply_Kmat! end + +function second_derivative!(d2f, f, coord, spectral::weak_discretization_info) # get the derivative at each grid point within each element and store in df - chebyshev_derivative!(coord.scratch_2d, f, spectral, coord) + elementwise_apply_Kmat!(coord, f, spectral) # map the derivative from the elemental grid to the full grid; # at element boundaries, use the average of the derivatives from neighboring elements. - derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) + derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) + # solve weak form problem M * d2f = K * f + mass_matrix_solve!(d2f, coord.scratch, spectral) end """ -calculate the derivative of f using finite differences, with particular scheme -specified by coord.fd_option; stored in df +Apply 'L-matrix' as part of a weak-form Laplacian derivative """ -function derivative!(df, f, coord, adv_fac, not_spectral::Bool) - # get the derivative at each grid point within each element and store in df - derivative_finite_difference!(coord.scratch_2d, f, coord.cell_width, adv_fac, - coord.bc, coord.fd_option, coord.igrid, coord.ielement) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the derivative from the upwind element. - derivative_elements_to_full_grid!(df, coord.scratch_2d, coord, adv_fac) -end +function elementwise_apply_Lmat! end -""" -calculate the derivative of f using centered differences; stored in df -""" -function derivative!(df, f, coord, not_spectral::Bool) +function laplacian_derivative!(d2f, f, coord, spectral::weak_discretization_info) # get the derivative at each grid point within each element and store in df - derivative_finite_difference!(coord.scratch_2d, f, coord.cell_width, - coord.bc, "fourth_order_centered", coord.igrid, coord.ielement) + elementwise_apply_Lmat!(coord, f, spectral) # map the derivative from the elemental grid to the full grid; # at element boundaries, use the average of the derivatives from neighboring elements. - derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) + derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) + # solve weak form problem M * d2f = K * f + mass_matrix_solve!(d2f, coord.scratch, spectral) end """ @@ -233,7 +312,7 @@ function reconcile_element_boundaries_centered!(df1d, df2d, coord) df1d[1] = 0.5*(df2d[1,1]+df2d[coord.ngrid,coord.nelement_local]) # consider right domain boundary df1d[coord.n] = df1d[1] - else + else # put endpoints into 1D array to be reconciled # across processes at a higher scope -> larger message sizes possible df1d[1] = df2d[1,1] @@ -253,8 +332,8 @@ end """ extension of the above function to distributed memory MPI -function allows for arbitray array sizes ONLY IF the -if statements doing the final endpoint assignments are +function allows for arbitray array sizes ONLY IF the +if statements doing the final endpoint assignments are updated to include each physical dimension required in the main code """ @@ -300,22 +379,22 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, # synchronize buffers # -- this all-to-all block communicate here requires that this function is NOT called from within a parallelised loop - # -- or from a @serial_region or from an if statment isolating a single rank on a block + # -- or from a @serial_region or from an if statment isolating a single rank on a block _block_synchronize() #if block_rank[] == 0 # lead process on this shared-memory block @serial_region begin # now deal with endpoints that are stored across ranks comm = coord.comm - nrank = coord.nrank - irank = coord.irank + nrank = coord.nrank + irank = coord.irank #send_buffer = coord.send_buffer #receive_buffer = coord.receive_buffer # sending pattern is cyclic. First we send data form irank -> irank + 1 # to fix the lower endpoints, then we send data from irank -> irank - 1 # to fix upper endpoints. Special exception for the periodic points. # receive_buffer[1] is for data received, send_buffer[1] is data to be sent - + # pass data from irank -> irank + 1, receive data from irank - 1 idst = mod(irank+1,nrank) # destination rank for sent data isrc = mod(irank-1,nrank) # source rank for received data @@ -323,7 +402,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, rreq1 = MPI.Irecv!(receive_buffer1, comm; source=isrc, tag=1) sreq1 = MPI.Isend(dfdx_upper_endpoints, comm; dest=idst, tag=1) #print("$irank: Sending $irank -> $idst = $dfdx_upper_endpoints\n") - + # pass data from irank -> irank - 1, receive data from irank + 1 idst = mod(irank-1,nrank) # destination rank for sent data isrc = mod(irank+1,nrank) # source rank for received data @@ -348,7 +427,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, end #now update the df1d array -- using a slice appropriate to the dimension reconciled assign_endpoint!(df1d,receive_buffer1,"lower",coord) - + if irank == nrank-1 if coord.bc == "periodic" #update the extreme upper endpoint with data from irank = 0 @@ -361,7 +440,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, end #now update the df1d array -- using a slice appropriate to the dimension reconciled assign_endpoint!(df1d,receive_buffer2,"upper",coord) - + end # synchronize buffers _block_synchronize() @@ -376,41 +455,40 @@ function apply_adv_fac!(buffer::AbstractArray{mk_float,Ndims},adv_fac::AbstractA #sgn = -1 for send irank + 1 -> irank #loop over all indices in array for i in eachindex(buffer,adv_fac,endpoints) - if sgn*adv_fac[i] > 0.0 - # replace buffer value with endpoint value + if sgn*adv_fac[i] > 0.0 + # replace buffer value with endpoint value buffer[i] = endpoints[i] elseif sgn*adv_fac[i] < 0.0 #do nothing - else #average values + else #average values buffer[i] = 0.5*(buffer[i] + endpoints[i]) end end end -function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, +function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, adv_fac_lower_endpoints::AbstractArray{mk_float,Mdims}, adv_fac_upper_endpoints::AbstractArray{mk_float,Mdims}, dfdx_lower_endpoints::AbstractArray{mk_float,Mdims}, dfdx_upper_endpoints::AbstractArray{mk_float,Mdims}, receive_buffer1::AbstractArray{mk_float,Mdims}, receive_buffer2::AbstractArray{mk_float,Mdims}, coord) where {Ndims,Mdims} # synchronize buffers # -- this all-to-all block communicate here requires that this function is NOT called from within a parallelised loop - # -- or from a @serial_region or from an if statment isolating a single rank on a block + # -- or from a @serial_region or from an if statment isolating a single rank on a block _block_synchronize() #if block_rank[] == 0 # lead process on this shared-memory block @serial_region begin - # now deal with endpoints that are stored across ranks comm = coord.comm - nrank = coord.nrank - irank = coord.irank + nrank = coord.nrank + irank = coord.irank #send_buffer = coord.send_buffer #receive_buffer = coord.receive_buffer # sending pattern is cyclic. First we send data form irank -> irank + 1 # to fix the lower endpoints, then we send data from irank -> irank - 1 # to fix upper endpoints. Special exception for the periodic points. # receive_buffer[1] is for data received, send_buffer[1] is data to be sent - + # send highest end point on THIS rank # pass data from irank -> irank + 1, receive data from irank - 1 idst = mod(irank+1,nrank) # destination rank for sent data @@ -419,7 +497,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, rreq1 = MPI.Irecv!(receive_buffer1, comm; source=isrc, tag=1) sreq1 = MPI.Isend(dfdx_upper_endpoints, comm; dest=idst, tag=1) #print("$irank: Sending $irank -> $idst = $dfdx_upper_endpoints\n") - + # send lowest end point on THIS rank # pass data from irank -> irank - 1, receive data from irank + 1 idst = mod(irank-1,nrank) # destination rank for sent data @@ -437,7 +515,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, if coord.bc == "periodic" # depending on adv_fac, update the extreme lower endpoint with data from irank = nrank -1 apply_adv_fac!(receive_buffer1,adv_fac_lower_endpoints,dfdx_lower_endpoints,1) - else # directly use value from Cheb at extreme lower point + else # directly use value from Cheb at extreme lower point receive_buffer1 .= dfdx_lower_endpoints end else # depending on adv_fac, update the lower endpoint with data from irank = nrank -1 @@ -445,7 +523,7 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, end #now update the df1d array -- using a slice appropriate to the dimension reconciled assign_endpoint!(df1d,receive_buffer1,"lower",coord) - + if irank == nrank-1 if coord.bc == "periodic" # depending on adv_fac, update the extreme upper endpoint with data from irank = 0 @@ -463,75 +541,6 @@ function reconcile_element_boundaries_MPI!(df1d::AbstractArray{mk_float,Ndims}, # synchronize buffers _block_synchronize() end - -function second_derivative!(d2f, f, coord, spectral::chebyshev_info) - # For spectral element methods, calculate second derivative by applying first - # derivative twice, with special treatment for element boundaries - - # First derivative - chebyshev_derivative!(coord.scratch_2d, f, spectral, coord) - derivative_elements_to_full_grid!(coord.scratch2, coord.scratch_2d, coord) - # MPI reconcile code here if used with z or r coords - - # Save elementwise first derivative result - coord.scratch2_2d .= coord.scratch_2d - - # Second derivative for element interiors - chebyshev_derivative!(coord.scratch_2d, coord.scratch2, spectral, coord) - derivative_elements_to_full_grid!(d2f, coord.scratch_2d, coord) - # MPI reconcile code here if used with z or r coords - - - # Add contribution to penalise discontinuous first derivatives at element - # boundaries. For smooth functions this would do nothing so should not affect - # convergence of the second derivative. Aims to stabilise numerical instability when - # spike develops at an element boundary. The coefficient is an arbitrary choice, it - # should probably be large enough for stability but as small as possible. - # - # Arbitrary numerical coefficient - C = 1.0 - function penalise_discontinuous_first_derivative!(d2f, imin, imax, df) - # Left element boundary - d2f[imin] += C * df[1] - - # Right element boundary - d2f[imax] -= C * df[end] - - return nothing - end - @views penalise_discontinuous_first_derivative!(d2f, 1, coord.imax[1], - coord.scratch2_2d[:,1]) - for ielement ∈ 2:coord.nelement_local - @views penalise_discontinuous_first_derivative!(d2f, coord.imin[ielement]-1, - coord.imax[ielement], - coord.scratch2_2d[:,ielement]) - end - - if coord.bc ∈ ("wall", "zero") - # For stability don't contribute to evolution at boundaries, in case these - # points are not set by a boundary condition. - # Full grid may be across processes and bc only applied to extreme ends of the - # domain. - if coord.irank == 0 - d2f[1] = 0.0 - end - if coord.irank == coord.nrank - 1 - d2f[end] = 0.0 - end - elseif coord.bc == "periodic" - # Need to get first derivatives from opposite ends of grid - if coord.nelement_local != coord.nelement_global - error("Distributed memory MPI not yet supported here") - end - d2f[1] -= C * coord.scratch2_2d[end,end] - d2f[end] += C * coord.scratch2_2d[1,1] - else - error("Unsupported bc '$coord.bc'") - end - return nothing -end - - """ Computes the integral of the integrand, using the input wgts @@ -603,7 +612,7 @@ function integral(integrand, vx, px, wgtsx, vy, py, wgtsy) @boundscheck ny == length(vy) || throw(BoundsError(vy)) # @boundscheck ny == length(wgtsy) || throw(BoundsError(wtgsy)) # @boundscheck nx == length(wgtsx) || throw(BoundsError(wtgsx)) - + @inbounds for j ∈ 1:ny @inbounds for i ∈ 1:nx integral += integrand[i,j] * (vx[i] ^ px) * (vy[j] ^ py) * wgtsx[i] * wgtsy[j] @@ -633,7 +642,7 @@ function integral(integrand, vx, px, wgtsx, vy, py, wgtsy, vz, pz, wgtsz) @boundscheck nx == length(vx) || throw(BoundsError(vx)) @boundscheck ny == length(vy) || throw(BoundsError(vy)) @boundscheck nz == length(vz) || throw(BoundsError(vz)) - + @inbounds for k ∈ 1:nz @inbounds for j ∈ 1:ny @inbounds for i ∈ 1:nx diff --git a/src/chebyshev.jl b/src/chebyshev.jl index ab3d190b0..fbca571a7 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -9,17 +9,18 @@ export scaled_chebyshev_grid export scaled_chebyshev_radau_grid export chebyshev_spectral_derivative! export chebyshev_info -export chebyshev_base_info -export chebyshev_derivative! using LinearAlgebra: mul! using FFTW using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_complex using ..clenshaw_curtis: clenshawcurtisweights +import ..calculus: elementwise_derivative! import ..interpolation: interpolate_to_grid_1d! +using ..moment_kinetics_structs: discretization_info """ +Chebyshev pseudospectral discretization """ struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} # fext is an array for storing f(z) on the extended domain needed @@ -40,7 +41,7 @@ struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs Dmat::Array{mk_float,2} end -struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} +struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} <: discretization_info lobatto::chebyshev_base_info{TForward, TBackward} radau::chebyshev_base_info{TForward, TBackward} end @@ -196,9 +197,11 @@ function scaled_chebyshev_radau_grid(ngrid, nelement_local, n, end """ + elementwise_derivative!(coord, ff, chebyshev::chebyshev_info) + Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f'. """ -function chebyshev_derivative!(df, ff, chebyshev, coord) +function elementwise_derivative!(coord, ff, chebyshev::chebyshev_info) df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local @@ -206,6 +209,8 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) @boundscheck nelement == size(chebyshev.lobatto.f,2) || throw(BoundsError(chebyshev.lobatto.f)) @boundscheck nelement == size(chebyshev.radau.f,2) || throw(BoundsError(chebyshev.radau.f)) @boundscheck nelement == size(df,2) && coord.ngrid == size(df,1) || throw(BoundsError(df)) + # note that one must multiply by a coordinate transform factor 1/element_scale[j] + # for each element j to get derivative on the extended grid if coord.cheb_option == "matrix" # variable k will be used to avoid double counting of overlapping point @@ -297,6 +302,16 @@ function chebyshev_derivative!(df, ff, chebyshev, coord) return nothing end +""" + elementwise_derivative!(coord, ff, adv_fac, spectral::chebyshev_info) + +Chebyshev transform f to get Chebyshev spectral coefficients and use them to calculate f'. + +Note: Chebyshev derivative does not make use of upwinding information within each element. +""" +function elementwise_derivative!(coord, ff, adv_fac, spectral::chebyshev_info) + return elementwise_derivative!(coord, ff, spectral) +end """ """ @@ -311,8 +326,6 @@ function chebyshev_derivative_single_element!(df, ff, cheby_f, cheby_df, cheby_f chebyshev_backward_transform!(df, cheby_fext, cheby_df, forward, coord.ngrid) end - - """ Chebyshev transform f to get Chebyshev spectral coefficients """ diff --git a/src/coordinates.jl b/src/coordinates.jl index beaea3ae8..936695a10 100644 --- a/src/coordinates.jl +++ b/src/coordinates.jl @@ -10,9 +10,11 @@ using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_int using ..calculus: derivative! using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid, setup_chebyshev_pseudospectral +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 using ..input_structs: advection_input +using ..moment_kinetics_structs: null_spatial_dimension_info, null_velocity_dimension_info using MPI @@ -166,22 +168,29 @@ function define_coordinate(input, parallel_io::Bool=false; init_YY::Bool=true) 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) - if input.discretization == "chebyshev_pseudospectral" && coord.n > 1 + if coord.n == 1 && occursin("v", coord.name) + spectral = null_velocity_dimension_info() + coord.duniform_dgrid .= 1.0 + elseif coord.n == 1 + spectral = null_spatial_dimension_info() + coord.duniform_dgrid .= 1.0 + elseif input.discretization == "chebyshev_pseudospectral" # create arrays needed for explicit Chebyshev pseudospectral treatment in this # coordinate and create the plans for the forward and backward fast Chebyshev # transforms spectral = setup_chebyshev_pseudospectral(coord) # obtain the local derivatives of the uniform grid with respect to the used grid derivative!(coord.duniform_dgrid, coord.uniform_grid, coord, spectral) - elseif input.discretization == "gausslegendre_pseudospectral" && coord.n > 1 + elseif input.discretization == "gausslegendre_pseudospectral" # create arrays needed for explicit GaussLegendre pseudospectral treatment in this # coordinate and create the matrices for differentiation spectral = setup_gausslegendre_pseudospectral(coord,init_YY=init_YY) # obtain the local derivatives of the uniform grid with respect to the used grid derivative!(coord.duniform_dgrid, coord.uniform_grid, coord, spectral) else - # create dummy Bool variable to return in place of the above struct - spectral = false + # finite_difference_info is just a type so that derivative methods, etc., dispatch + # to the finite difference versions, it does not contain any information. + spectral = finite_difference_info() coord.duniform_dgrid .= 1.0 end diff --git a/src/finite_differences.jl b/src/finite_differences.jl index 33e25ee9e..17f3c58c2 100644 --- a/src/finite_differences.jl +++ b/src/finite_differences.jl @@ -3,7 +3,14 @@ module finite_differences using ..type_definitions: mk_float -#import ..calculus: elementwise_derivative!, elementwise_second_derivative! +import ..calculus: elementwise_derivative!, elementwise_second_derivative!, + second_derivative! +using ..moment_kinetics_structs: discretization_info + +""" +Finite difference discretization +""" +struct finite_difference_info <: discretization_info end """ """ @@ -26,38 +33,50 @@ function fd_check_option(option, ngrid) end """ - elementwise_derivative!(coord, f, adv_fac, not_spectral::Bool) + elementwise_derivative!(coord, f, adv_fac, not_spectral::finite_difference_info) Calculate the derivative of f using finite differences, with particular scheme specified by coord.fd_option; result stored in coord.scratch_2d. """ -function elementwise_derivative!(coord, f, adv_fac, not_spectral::Bool) +function elementwise_derivative!(coord, f, adv_fac, not_spectral::finite_difference_info) return derivative_finite_difference!(coord.scratch_2d, f, coord.cell_width, adv_fac, coord.bc, coord.fd_option, coord.igrid, coord.ielement) end """ - elementwise_derivative!(coord, f, not_spectral::Bool) + elementwise_derivative!(coord, f, not_spectral::finite_difference_info) Calculate the derivative of f using 4th order centered finite differences; result stored in coord.scratch_2d. """ -function elementwise_derivative!(coord, f, not_spectral::Bool) +function elementwise_derivative!(coord, f, not_spectral::finite_difference_info) return derivative_finite_difference!(coord.scratch_2d, f, coord.cell_width, coord.bc, "fourth_order_centered", coord.igrid, coord.ielement) end """ - elementwise_second_derivative!(coord, f, not_spectral::Bool) + elementwise_second_derivative!(coord, f, not_spectral::finite_difference_info) Calculate the second derivative of f using 2nd order centered finite differences; result stored in coord.scratch_2d. """ -function elementwise_second_derivative!(coord, f, not_spectral::Bool) +function elementwise_second_derivative!(coord, f, not_spectral::finite_difference_info) return second_derivative_finite_difference!(coord.scratch_2d, f, coord.cell_width, coord.bc, coord.igrid, coord.ielement) end +function second_derivative!(df, f, coord, spectral::finite_difference_info) + # Finite difference version must use an appropriate second derivative stencil, not + # apply the 1st derivative twice as for the spectral element method + + # get the derivative at each grid point within each element and store in + # coord.scratch_2d + elementwise_second_derivative!(coord, f, spectral) + # map the derivative from the elem;ntal grid to the full grid; + # at element boundaries, use the average of the derivatives from neighboring elements. + derivative_elements_to_full_grid!(df, coord.scratch_2d, coord) +end + """ """ function derivative_finite_difference!(df, f, del, adv_fac, bc, fd_option, igrid, ielement) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 12125b20b..c7ec4872b 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -16,7 +16,6 @@ export scaled_gauss_legendre_radau_grid export gausslegendre_derivative! export gausslegendre_apply_Kmat! export gausslegendre_apply_Lmat! -export gausslegendre_mass_matrix_solve! export setup_gausslegendre_pseudospectral export GaussLegendre_weak_product_matrix! export ielement_global_func @@ -28,13 +27,16 @@ using LinearAlgebra: mul!, lu, LU using SparseArrays: sparse, AbstractSparseArray using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float +import ..calculus: elementwise_derivative!, elementwise_apply_Kmat!, + elementwise_apply_Lmat!, mass_matrix_solve! +using ..moment_kinetics_structs: weak_discretization_info """ structs for passing around matrices for taking the derivatives on Gauss-Legendre points in 1D """ -struct gausslegendre_base_info{} +struct gausslegendre_base_info # elementwise differentiation matrix (ngrid*ngrid) Dmat::Array{mk_float,2} # local mass matrix type 0 @@ -79,7 +81,7 @@ struct gausslegendre_base_info{} Y31::Array{mk_float,3} end -struct gausslegendre_info{} +struct gausslegendre_info <: weak_discretization_info lobatto::gausslegendre_base_info radau::gausslegendre_base_info # global (1D) mass matrix @@ -221,10 +223,9 @@ function setup_gausslegendre_pseudospectral_radau(coord;init_YY=true) return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end -""" -function for taking the first derivative on Gauss-Legendre points -""" -function gausslegendre_derivative!(df, ff, gausslegendre, coord) + +function elementwise_derivative!(coord, ff, gausslegendre::gausslegendre_info) + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -260,11 +261,14 @@ function gausslegendre_derivative!(df, ff, gausslegendre, coord) return nothing end + +# Spectral element method does not use upwinding within an element +function elementwise_derivative!(coord, ff, adv_fac, spectral::gausslegendre_info) + return elementwise_derivative!(coord, ff, spectral) +end -""" -function for taking the weak-form second derivative on Gauss-Legendre points -""" -function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) +function elementwise_apply_Kmat!(coord, ff, gausslegendre::gausslegendre_info) + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -302,10 +306,8 @@ function gausslegendre_apply_Kmat!(df, ff, gausslegendre, coord) return nothing end -""" -function for taking the weak-form Laplacian derivative on Gauss-Legendre points -""" -function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) +function elementwise_apply_Lmat!(coord, ff, gausslegendre::gausslegendre_info) + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -343,7 +345,7 @@ function gausslegendre_apply_Lmat!(df, ff, gausslegendre, coord) return nothing end -function gausslegendre_mass_matrix_solve!(f,b,spectral) +function mass_matrix_solve!(f, b, spectral::gausslegendre_info) # invert mass matrix system y = spectral.mass_matrix_lu \ b @. f = y diff --git a/src/hermite_spline_interpolation.jl b/src/hermite_spline_interpolation.jl index fe617a471..255c5da91 100644 --- a/src/hermite_spline_interpolation.jl +++ b/src/hermite_spline_interpolation.jl @@ -1,6 +1,7 @@ module hermite_spline_interpolation using ..calculus: derivative! +using ..finite_differences: finite_difference_info import ..interpolation: interpolate_to_grid_1d! """ @@ -17,11 +18,12 @@ f : Array{mk_float} Field to be interpolated coord : coordinate `coordinate` struct giving the coordinate along which f varies -not_spectral : Bool - A Bool argument here indicates that the coordinate is not spectral-element - discretized, i.e. it is on a uniform ('finite difference') grid. +not_spectral : finite_difference_info + A finite_difference_info argument here indicates that the coordinate is not + spectral-element discretized, i.e. it is on a uniform ('finite difference') grid. """ -function interpolate_to_grid_1d!(result, new_grid, f, coord, not_spectral::Bool) +function interpolate_to_grid_1d!(result, new_grid, f, coord, + not_spectral::finite_difference_info) x = coord.grid n_new = length(new_grid) diff --git a/src/moment_kinetics.jl b/src/moment_kinetics.jl index 4ca1eb5a9..4fae7a750 100644 --- a/src/moment_kinetics.jl +++ b/src/moment_kinetics.jl @@ -19,11 +19,11 @@ include("moment_kinetics_structs.jl") include("looping.jl") include("array_allocation.jl") include("interpolation.jl") +include("calculus.jl") include("clenshaw_curtis.jl") include("gauss_legendre.jl") include("chebyshev.jl") include("finite_differences.jl") -include("calculus.jl") include("quadrature.jl") include("hermite_spline_interpolation.jl") include("derivatives.jl") diff --git a/src/moment_kinetics_structs.jl b/src/moment_kinetics_structs.jl index 6c023c95e..f2c16d4b0 100644 --- a/src/moment_kinetics_structs.jl +++ b/src/moment_kinetics_structs.jl @@ -4,7 +4,6 @@ cycles when they are used by several other modules. """ module moment_kinetics_structs -using FFTW using ..communication using ..type_definitions: mk_float @@ -45,4 +44,27 @@ struct em_fields_struct force_Er_zero_at_wall::Bool end +""" +discretization_info for one dimension + +All the specific discretizations in moment_kinetics are subtypes of this type. +""" +abstract type discretization_info end + +""" +discretization_info for a discretization that supports 'weak form' methods, for one +dimension +""" +abstract type weak_discretization_info <: discretization_info end + +""" +Type representing a spatial dimension with only one grid point +""" +struct null_spatial_dimension_info <: discretization_info end + +""" +Type representing a velocity space dimension with only one grid point +""" +struct null_velocity_dimension_info <: discretization_info end + end diff --git a/test/calculus_tests.jl b/test/calculus_tests.jl index d0fab3707..e3b24ad50 100644 --- a/test/calculus_tests.jl +++ b/test/calculus_tests.jl @@ -9,7 +9,6 @@ using moment_kinetics.calculus: derivative!, second_derivative!, integral using MPI using Random -#cheb_option_global = "FFT" # "matrix" # function runtests() @testset "calculus" verbose=use_verbose begin println("calculus tests") @@ -33,7 +32,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -106,7 +104,7 @@ function runtests() expected_df = @. 2.0 * π / L * cospi(2.0 * x.grid / L) # differentiate f - derivative!(df, f, x, false) + derivative!(df, f, x, spectral) rtol = 1.e2 / (nelement*(ngrid-1))^4 @test isapprox(df, expected_df, rtol=rtol, atol=1.e-15, @@ -160,7 +158,7 @@ function runtests() adv_fac .= advection # differentiate f - derivative!(df, f, x, adv_fac, false) + derivative!(df, f, x, adv_fac, spectral) rtol = 1.e2 / (nelement*(ngrid-1))^order @test isapprox(df, expected_df, rtol=rtol, atol=1.e-15, @@ -207,7 +205,7 @@ function runtests() expected_df = @. -2.0 * π / L * sinpi(2.0 * x.grid / L) # differentiate f - derivative!(df, f, x, false) + derivative!(df, f, x, spectral) # Note: only get 1st order convergence at the boundary for an input # function that has zero gradient at the boundary @@ -265,7 +263,7 @@ function runtests() adv_fac .= advection # differentiate f - derivative!(df, f, x, adv_fac, false) + derivative!(df, f, x, adv_fac, spectral) # Note: only get 1st order convergence at the boundary for an input # function that has zero gradient at the boundary @@ -449,7 +447,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -646,7 +643,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -691,7 +687,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -742,7 +737,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement @@ -1304,7 +1298,6 @@ function runtests() # fd_option and adv_input not actually used so given values unimportant fd_option = "" adv_input = advection_input("default", 1.0, 0.0, 0.0) - #cheb_option = cheb_option_global #"FFT" # create the 'input' struct containing input info needed to create a # coordinate nelement_local = nelement From fbd6dd54fbe5954eccfd0bd51d05029f37b80de3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 23 Nov 2023 12:08:50 +0000 Subject: [PATCH 253/331] Added automatic checks to moment_kinetics_input.jl to catch incorrect discretization choices for vpa and vperp, and to warn about using the weakform_fokker_planck = false. --- src/moment_kinetics_input.jl | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 801fb3469..0d131d7b6 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -182,8 +182,13 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) * "krook_collisions_option=$(collisions.krook_collisions_option) passed") end - collisions.weakform_fokker_planck = get(scan_input, "weakform_fokker_planck", true) collisions.nuii = get(scan_input, "nuii", 0.0) + collisions.weakform_fokker_planck = get(scan_input, "weakform_fokker_planck", true) + if !collisions.weakform_fokker_planck + println("WARNING: you have used weakform_fokker_planck = false") + println("WARNING: you have selected a depreciated version of the ion-ion self collision operator") + end + # options below only used with collisions.weakform_fokker_planck = false collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) collisions.numerical_conserving_terms = get(scan_input, "numerical_conserving_terms", "density") @@ -526,9 +531,9 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) end # check input to catch errors/unsupported options - check_input(io, output_dir, nstep, dt, r_immutable, z_immutable, vpa_immutable, + check_input(io, output_dir, nstep, dt, r_immutable, z_immutable, vpa_immutable, vperp_immutable, composition, species_immutable, evolve_moments, num_diss_params, - save_inputs_to_txt) + save_inputs_to_txt, collisions) # return immutable structs for z, vpa, species and composition all_inputs = (io_immutable, evolve_moments, t_input, z, z_spectral, r, r_spectral, @@ -1005,8 +1010,8 @@ end """ check various input options to ensure they are all valid/consistent """ -function check_input(io, output_dir, nstep, dt, r, z, vpa, composition, species, - evolve_moments, num_diss_params, save_inputs_to_txt) +function check_input(io, output_dir, nstep, dt, r, z, vpa, vperp, composition, species, + evolve_moments, num_diss_params, save_inputs_to_txt, collisions) # copy the input file to the output directory to be saved if save_inputs_to_txt && global_rank[] == 0 cp(joinpath(@__DIR__, "moment_kinetics_input.jl"), joinpath(output_dir, "moment_kinetics_input.jl"), force=true) @@ -1016,12 +1021,22 @@ function check_input(io, output_dir, nstep, dt, r, z, vpa, composition, species, check_coordinate_input(r, "r", io) check_coordinate_input(z, "z", io) check_coordinate_input(vpa, "vpa", io) + check_coordinate_input(vperp, "vperp", io) # if the parallel flow is evolved separately, then the density must also be evolved separately if evolve_moments.parallel_flow && !evolve_moments.density print(io,">evolve_moments.parallel_flow = true, but evolve_moments.density = false.") println(io, "this is not a supported option. forcing evolve_moments.density = true.") evolve_moments.density = true end + if collisions.nuii > 0.0 + # check that the grids support the collision operator + print(io, "The self-collision operator is switched on \n nuii = $collisions.nuii \n") + if !(vpa.discretization == "gausslegendre_pseudospectral") || !(vperp.discretization == "gausslegendre_pseudospectral") + error("ERROR: you are using \n vpa.discretization='"*vpa.discretization* + "' \n vperp.discretization='"*vperp.discretization*"' \n with the ion self-collision operator \n"* + "ERROR: you should use \n vpa.discretization='gausslegendre_pseudospectral' \n vperp.discretization='gausslegendre_pseudospectral'") + end + end end """ From 2f75fba422c44d2b9e7180c16babe0c310675c4b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 23 Nov 2023 13:54:30 +0000 Subject: [PATCH 254/331] Documentation noting the two different collision operator implementations in fokker_planck.jl, move functions appropriately. --- src/fokker_planck.jl | 209 ++++++++++++++++++++++++++----------------- 1 file changed, 126 insertions(+), 83 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index d69c354a3..237f15fac 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -1,5 +1,27 @@ """ module for including the Full-F Fokker-Planck Collision Operator + +The functions in this module are split into two groups. + +The first set of functions implement the weak-form +Collision operator using the Rosenbluth-MacDonald-Judd +formulation in a divergence form. The Green's functions +for the Rosenbluth potentials are used to obtain the Rosenbluth +potentials at the boundaries. To find the potentials +everywhere else elliptic solves of the PDEs for the +Rosenbluth potentials are performed with Dirichlet +boundary conditions. These routines provide the default collision operator +used in the code. + +The second set of functions implement the same operator using a "strong" form +where the collision operator is fully expanded out, and repeated interpolation +derivatives are used to calculate the terms in the operator arising from the +test particle distribution function. The Green's functions are used to obtain +the Rosenbluth potentials everywhere. This second implementation shows worse +numerical conservation properties than the first, and is significantly slower +due to the use of the Green's functions for the whole of velocity space. +For nowm, these functions are retained for development purposes. + """ module fokker_planck @@ -43,6 +65,11 @@ using ..fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_sol using ..fokker_planck_test: Cssp_fully_expanded_form, calculate_collisional_fluxes, H_Maxwellian, dGdvperp_Maxwellian using ..fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian, dHdvpa_Maxwellian, dHdvperp_Maxwellian using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian + +######################################################## +# begin functions associated with the weak-form operator +######################################################## + """ allocate the required ancilliary arrays """ @@ -333,6 +360,100 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp return nothing end +# solves A x = b for a matrix of the form +# A00 0 A02 +# 0 A11 A12 +# A02 A12 A22 +# appropriate for the moment numerical conserving terms +function symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + # matrix determinant + detA = A00*(A11*A22 - A12^2) - A11*A02^2 + # cofactors C (also a symmetric matrix) + C00 = A11*A22 - A12^2 + C01 = A12*A02 + C02 = -A11*A02 + C11 = A00*A22 - A02^2 + C12 = -A00*A12 + C22 = A00*A11 + x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA + x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA + x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA + #println("b0: ",b0," b1: ",b1," b2: ",b2) + #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) + #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) + #println("x0: ",x0," x1: ",x1," x2: ",x2) + return x0, x1, x2 +end + +# solves A x = b for a matrix of the form +# A00 A01 A02 +# A01 A11 A12 +# A02 A12 A22 +# appropriate for the moment numerical conserving terms +function symmetric_matrix_inverse(A00,A01,A02,A11,A12,A22,b0,b1,b2) + # matrix determinant + detA = A00*(A11*A22 - A12^2) - A01*(A01*A22 - A12*A02) + A02*(A01*A12 - A11*A02) + # cofactors C (also a symmetric matrix) + C00 = A11*A22 - A12^2 + C01 = A12*A02 - A01*A22 + C02 = A01*A12 -A11*A02 + C11 = A00*A22 - A02^2 + C12 = A01*A02 -A00*A12 + C22 = A00*A11 - A01^2 + x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA + x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA + x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA + #println("b0: ",b0," b1: ",b1," b2: ",b2) + #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) + #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) + #println("x0: ",x0," x1: ",x1," x2: ",x2) + return x0, x1, x2 +end + +function conserving_corrections!(CC,pdf_in,vpa,vperp,dummy_vpavperp) + begin_serial_region() + # compute moments of the input pdf + dens = get_density(@view(pdf_in[:,:]), vpa, vperp) + upar = get_upar(@view(pdf_in[:,:]), vpa, vperp, dens) + ppar = get_ppar(@view(pdf_in[:,:]), vpa, vperp, upar) + pperp = get_pperp(@view(pdf_in[:,:]), vpa, vperp) + pressure = get_pressure(ppar,pperp) + qpar = get_qpar(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) + rmom = get_rmom(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) + + # compute moments of the numerical collision operator + dn = get_density(CC, vpa, vperp) + du = get_upar(CC, vpa, vperp, 1.0) + dppar = get_ppar(CC, vpa, vperp, upar) + dpperp = get_pperp(CC, vpa, vperp) + dp = get_pressure(dppar,dpperp) + + # form the appropriate matrix coefficients + b0, b1, b2 = dn, du - upar*dn, 3.0*dp + A00, A02, A11, A12, A22 = dens, 3.0*pressure, ppar, qpar, rmom + + # obtain the coefficients for the corrections + (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) + + # correct CC + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + wpar = vpa.grid[ivpa] - upar + CC[ivpa,ivperp] -= (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_in[ivpa,ivperp] + end +end + + +###################################################### +# end functions associated with the weak-form operator +###################################################### + + + +########################################################## +# begin functions associated with the strong-form operator +########################################################## + """ function that initialises the arrays needed for Fokker Planck collisions """ @@ -631,89 +752,6 @@ function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_ end end -# solves A x = b for a matrix of the form -# A00 0 A02 -# 0 A11 A12 -# A02 A12 A22 -# appropriate for the moment numerical conserving terms -function symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) - # matrix determinant - detA = A00*(A11*A22 - A12^2) - A11*A02^2 - # cofactors C (also a symmetric matrix) - C00 = A11*A22 - A12^2 - C01 = A12*A02 - C02 = -A11*A02 - C11 = A00*A22 - A02^2 - C12 = -A00*A12 - C22 = A00*A11 - x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA - x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA - x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA - #println("b0: ",b0," b1: ",b1," b2: ",b2) - #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) - #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) - #println("x0: ",x0," x1: ",x1," x2: ",x2) - return x0, x1, x2 -end - -# solves A x = b for a matrix of the form -# A00 A01 A02 -# A01 A11 A12 -# A02 A12 A22 -# appropriate for the moment numerical conserving terms -function symmetric_matrix_inverse(A00,A01,A02,A11,A12,A22,b0,b1,b2) - # matrix determinant - detA = A00*(A11*A22 - A12^2) - A01*(A01*A22 - A12*A02) + A02*(A01*A12 - A11*A02) - # cofactors C (also a symmetric matrix) - C00 = A11*A22 - A12^2 - C01 = A12*A02 - A01*A22 - C02 = A01*A12 -A11*A02 - C11 = A00*A22 - A02^2 - C12 = A01*A02 -A00*A12 - C22 = A00*A11 - A01^2 - x0 = ( C00*b0 + C01*b1 + C02*b2 )/detA - x1 = ( C01*b0 + C11*b1 + C12*b2 )/detA - x2 = ( C02*b0 + C12*b1 + C22*b2 )/detA - #println("b0: ",b0," b1: ",b1," b2: ",b2) - #println("A00: ",A00," A02: ",A02," A11: ",A11," A12: ",A12," A22: ",A22, " detA: ",detA) - #println("C00: ",C00," C02: ",C02," C11: ",C11," C12: ",C12," C22: ",C22) - #println("x0: ",x0," x1: ",x1," x2: ",x2) - return x0, x1, x2 -end - -function conserving_corrections!(CC,pdf_in,vpa,vperp,dummy_vpavperp) - begin_serial_region() - # compute moments of the input pdf - dens = get_density(@view(pdf_in[:,:]), vpa, vperp) - upar = get_upar(@view(pdf_in[:,:]), vpa, vperp, dens) - ppar = get_ppar(@view(pdf_in[:,:]), vpa, vperp, upar) - pperp = get_pperp(@view(pdf_in[:,:]), vpa, vperp) - pressure = get_pressure(ppar,pperp) - qpar = get_qpar(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) - rmom = get_rmom(@view(pdf_in[:,:]), vpa, vperp, upar, dummy_vpavperp) - - # compute moments of the numerical collision operator - dn = get_density(CC, vpa, vperp) - du = get_upar(CC, vpa, vperp, 1.0) - dppar = get_ppar(CC, vpa, vperp, upar) - dpperp = get_pperp(CC, vpa, vperp) - dp = get_pressure(dppar,dpperp) - - # form the appropriate matrix coefficients - b0, b1, b2 = dn, du - upar*dn, 3.0*dp - A00, A02, A11, A12, A22 = dens, 3.0*pressure, ppar, qpar, rmom - - # obtain the coefficients for the corrections - (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) - - # correct CC - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - wpar = vpa.grid[ivpa] - upar - CC[ivpa,ivperp] -= (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_in[ivpa,ivperp] - end -end - # applies the numerical conservation to pdf_out, the advanced distribution function # uses the low-level moment integration routines from velocity moments # conserves n, upar, total pressure of each species @@ -832,4 +870,9 @@ function store_moments_in_buffer!(pdf_out,boundary_distributions, end return nothing end + +######################################################## +# end functions associated with the strong-form operator +######################################################## + end From e1023ce6536bb1c0badc6c7b54fbe57734a6ebaa Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 23 Nov 2023 15:52:47 +0000 Subject: [PATCH 255/331] Bring fkpl_functional_test.jl (for testing the original strong-form implementation of C[F,F]) to a working and interactive form following the merge. --- fkpl_functional_test.jl | 705 +++++++++++++++++++------------------- src/initial_conditions.jl | 2 +- 2 files changed, 347 insertions(+), 360 deletions(-) diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl index 82610fde1..111e62d0d 100644 --- a/fkpl_functional_test.jl +++ b/fkpl_functional_test.jl @@ -1,3 +1,5 @@ +export test_strong_form_collision_operator + using Printf using Plots using LaTeXStrings @@ -8,12 +10,11 @@ using Dates import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input, species_composition, collisions_input, boltzmann_electron_response using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral -using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! +using moment_kinetics.gauss_legendre: gausslegendre_mass_matrix_solve! using moment_kinetics.fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! using moment_kinetics.fokker_planck_test: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -using moment_kinetics.fokker_planck_test: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck_test: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs +using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, dGdvperp_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian +using moment_kinetics.fokker_planck_test: dHdvpa_Maxwellian, dHdvperp_Maxwellian, Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian using moment_kinetics.fokker_planck_test: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian using moment_kinetics.type_definitions: mk_float, mk_int @@ -25,7 +26,7 @@ using moment_kinetics.looping using moment_kinetics.array_allocation: allocate_shared_float, allocate_float using moment_kinetics.time_advance: setup_dummy_and_buffer_arrays using moment_kinetics.advection: setup_advection -using moment_kinetics.initial_conditions: create_and_init_boundary_distributions, create_pdf +using moment_kinetics.initial_conditions: create_boundary_distributions, create_pdf using moment_kinetics.input_structs: advance_info using moment_kinetics.time_advance: setup_runge_kutta_coefficients @@ -61,376 +62,357 @@ function L2norm_vspace(ff_err,vpa,vperp) return L2norm end -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - function init_grids(nelement,ngrid) - discretization = "gausslegendre_pseudospectral" - #discretization = "chebyshev_pseudospectral" - #discretization = "finite_difference" - - # define inputs needed for the test - vpa_ngrid = ngrid #number of points per element - vpa_nelement_local = nelement # number of elements per rank - vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 12.0 #physical box size in reference units - bc = "zero" - vperp_ngrid = ngrid #number of points per element - vperp_nelement_local = nelement # number of elements per rank - vperp_nelement_global = vperp_nelement_local # total number of elements - vperp_L = 6.0 #physical box size in reference units - bc = "zero" - - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - - # create the coordinate structs - #println("made inputs") - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - #println(vperp.grid) - #println(vperp.wgts) - if discretization == "chebyshev_pseudospectral" - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - #println("using chebyshev_pseudospectral") - elseif discretization == "gausslegendre_pseudospectral" - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - #println("using gausslegendre_pseudospectral") - end - return vpa, vperp, vpa_spectral, vperp_spectral - end +function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" - test_Lagrange_integral = false #true - test_Lagrange_integral_scan = true + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + element_spacing_option = "uniform" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) - function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) - # set up grids for input Maxwellian - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - nz = 1 - nr = 1 - n_ion_species = 1 - n_neutral_species = 1 - nvzeta = 1 - nvr = 1 - nvz = 1 - dt = 1.0 - adv_input = advection_input("default", 1.0, 0.0, 0.0) - vr_input = grid_input("vr", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) - vz_input = grid_input("vz", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) - vzeta_input = grid_input("vzeta", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) - r_input = grid_input("r", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) - z_input = grid_input("z", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL) - vr = define_coordinate(vr_input) - vz = define_coordinate(vz_input) - vzeta = define_coordinate(vzeta_input) - r = define_coordinate(r_input) - z = define_coordinate(z_input) - composition = species_composition(n_ion_species, n_ion_species, n_neutral_species, - boltzmann_electron_response, false, 1:n_ion_species, n_ion_species+1:n_ion_species, 1.0, 1.0, - 1.0, 0.0, 0.0, false, 1.0, 1.0, 0.0, allocate_float(n_ion_species)) - nuii = 1.0 - collisions = collisions_input(0.0, 0.0, false, false, nuii, 0.0, 0.0, "none") - rk_coefs = setup_runge_kutta_coefficients(4) - advance = advance_info(false, false, false, false, false, - false, false, false, false, false, false, - false, false, false, false, rk_coefs, - false, false, true, true, false, false, false) + # create the coordinate structs + #println("made inputs") + vpa, vpa_spectral = define_coordinate(vpa_input) + vperp, vperp_spectral = define_coordinate(vperp_input) + return vpa, vperp, vpa_spectral, vperp_spectral +end - # Set up MPI - if standalone - initialize_comms!() - end - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=n_ion_species, sn=n_neutral_species, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - scratch_dummy = setup_dummy_and_buffer_arrays(r.n,z.n,vpa.n,vperp.n,1,1,1, - composition.n_ion_species,1) - - @serial_region begin - println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) +test_Lagrange_integral = false #true +test_Lagrange_integral_scan = true + +function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + nz = 1 + nr = 1 + n_ion_species = 1 + n_neutral_species = 1 + nvzeta = 1 + nvr = 1 + nvz = 1 + dt = 1.0 + adv_input = advection_input("default", 1.0, 0.0, 0.0) + vr_input = grid_input("vr", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") + vz_input = grid_input("vz", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") + vzeta_input = grid_input("vzeta", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") + r_input = grid_input("r", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") + z_input = grid_input("z", 1, 1, 1, + 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") + vr, vr_spectral = define_coordinate(vr_input) + vz, vz_spectral = define_coordinate(vz_input) + vzeta, vzeta_spectral = define_coordinate(vzeta_input) + r, r_spectral = define_coordinate(r_input) + z, z_spectral = define_coordinate(z_input) + composition = species_composition(n_ion_species, n_ion_species, n_neutral_species, + boltzmann_electron_response, false, 1.0, 1.0, + 1.0, 0.0, 1.0, 0.0, allocate_float(n_ion_species)) + nuii = 1.0 + collisions = collisions_input(0.0, 0.0, false, 0.0, "", false, nuii, 0.0, "none") + rk_coefs = setup_runge_kutta_coefficients(4) + advance = advance_info(false, false, false, false, false, false, + false, false, false, false, false, false, + true, false, false, false, false, false, + true, false, false, false, false, false, + rk_coefs, + false, false, true, false) + + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=n_ion_species, sn=n_neutral_species, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + scratch_dummy = setup_dummy_and_buffer_arrays(r.n,z.n,vpa.n,vperp.n,1,1,1, + composition.n_ion_species,1) + + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + fs_in = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) + fs_out = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) + + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) + + Cssp_err = allocate_shared_float(nvpa,nvperp) + Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # set up test Maxwellian + denss = 1.0 #3.0/4.0 + upars = 0.0 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + + nussp = nuii + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, + denss,upars,vths,ms, + nussp,vpa,vperp,ivpa,ivperp) end + end + vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) + # initialise the vpa advection speed + begin_s_r_z_vperp_region() + begin_serial_region() + z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) + r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) + pdf_unused = create_pdf(composition, r, z, vperp, vpa, vzeta, vr, vz) + boundary_distributions = create_boundary_distributions(vz, vr, vzeta, vpa, vperp, z, + composition) + dSdt = 0.0 + + # initialise the weights + fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + # evaluate the collision operator + explicit_fokker_planck_collisions!(fs_out,fs_in,dSdt,composition,collisions,dt,fokkerplanck_arrays, + scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, + boundary_distributions, advance, + vpa_advect, z_advect, r_advect, + diagnose_entropy_production = false) + + fka = fokkerplanck_arrays + # error analysis of distribution function + begin_serial_region() + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. d2fsdvpa2_err = abs(fka.d2fdvpa2 - d2fsdvpa2_Maxwell) + max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) + println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) + @. dfsdvperp_err = abs(fka.dfdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(fka.d2fdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + @. d2fsdvperp2_err = abs(fka.d2fdvperp2 - d2fsdvperp2_Maxwell) + max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) + println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) - fs_in = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) - fs_out = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) - - dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) - dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dGdvperp_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) - - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = allocate_shared_float(nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = allocate_shared_float(nvpa,nvperp) - - Cssp_err = allocate_shared_float(nvpa,nvperp) - Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + end + + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + + @serial_region begin + @. Cssp_err = abs(fka.Cssp_result_vpavperp - Cssp_Maxwell) + max_C_err, max_C_index = findmax(Cssp_err) + println("max_C_err: ",max_C_err," ",max_C_index) + println("spot check C_err: ",Cssp_err[end,end], " Cssp: ",fka.Cssp_result_vpavperp[end,end]) + @. dHdvperp_err = abs(fka.dHdvperp - dHdvperp_Maxwell) + max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",fka.dHdvperp[end,end]) + @. dHdvpa_err = abs(fka.dHdvpa - dHdvpa_Maxwell) + max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",fka.dHdvpa[end,end]) - @serial_region begin - println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) end - - # set up test Maxwellian - denss = 1.0 #3.0/4.0 - upars = 0.0 #2.0/3.0 - ppars = 1.0 #2.0/3.0 - pperps = 1.0 #2.0/3.0 - press = get_pressure(ppars,pperps) - ms = 1.0 - vths = get_vth(press,denss,ms) - - nussp = nuii - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - denss,upars,vths,ms, - nussp,vpa,vperp,ivpa,ivperp) - end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) end - vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) - # initialise the vpa advection speed - begin_s_r_z_vperp_region() - begin_serial_region() - z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) - r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) - pdf_unused = create_pdf(vz, vr, vzeta, vpa, vperp, z, r, n_ion_species, n_neutral_species) - boundary_distributions = create_and_init_boundary_distributions(pdf_unused, vz, vr, vzeta, vpa, vperp, z, r, composition) - dSdt = 0.0 - - # initialise the weights - fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) - # evaluate the collision operator - explicit_fokker_planck_collisions!(fs_out,fs_in,dSdt,composition,collisions,dt,fokkerplanck_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, - boundary_distributions, advance, - vpa_advect, z_advect, r_advect, - diagnose_entropy_production = false) - - fka = fokkerplanck_arrays - # error analysis of distribution function - begin_serial_region() - @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) - max_dfsdvpa_err = maximum(dfsdvpa_err) - println("max_dfsdvpa_err: ",max_dfsdvpa_err) - @. d2fsdvpa2_err = abs(fka.d2fdvpa2 - d2fsdvpa2_Maxwell) - max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) - println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) - @. dfsdvperp_err = abs(fka.dfdvperp - dfsdvperp_Maxwell) - max_dfsdvperp_err = maximum(dfsdvperp_err) - println("max_dfsdvperp_err: ",max_dfsdvperp_err) - @. d2fsdvperpdvpa_err = abs(fka.d2fdvperpdvpa - d2fsdvperpdvpa_Maxwell) - max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) - println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) - @. d2fsdvperp2_err = abs(fka.d2fdvperp2 - d2fsdvperp2_Maxwell) - max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) - println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) - + @. d2Gdvperp2_err = abs(fka.d2Gdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",fka.d2Gdvperp2[end,end]) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) end - - plot_dHdvpa = false #true - plot_dHdvperp = false #true - plot_d2Gdvperp2 = false #true - plot_d2Gdvperpdvpa = false #true - plot_dGdvperp = false #true - plot_d2Gdvpa2 = false #true - - @serial_region begin - @. Cssp_err = abs(fka.Cssp_result_vpavperp - Cssp_Maxwell) - max_C_err, max_C_index = findmax(Cssp_err) - println("max_C_err: ",max_C_err," ",max_C_index) - println("spot check C_err: ",Cssp_err[end,end], " Cssp: ",fka.Cssp_result_vpavperp[end,end]) - @. dHdvperp_err = abs(fka.dHdvperp - dHdvperp_Maxwell) - max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) - println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) - println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",fka.dHdvperp[end,end]) - @. dHdvpa_err = abs(fka.dHdvpa - dHdvpa_Maxwell) - max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) - println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) - println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",fka.dHdvpa[end,end]) - - if plot_dHdvpa - @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_err.pdf") - savefig(outfile) - end - if plot_dHdvperp - @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvperp2_err = abs(fka.d2Gdvperp2 - d2Gdvperp2_Maxwell) - max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) - println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) - println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",fka.d2Gdvperp2[end,end]) - if plot_d2Gdvperp2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_err.pdf") - savefig(outfile) - end - @. d2Gdvperpdvpa_err = abs(fka.d2Gdvperpdvpa - d2Gdvperpdvpa_Maxwell) - max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) - println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) - println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",fka.d2Gdvperpdvpa[end,end]) - if plot_d2Gdvperpdvpa - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") - savefig(outfile) - end - @. dGdvperp_err = abs(fka.dGdvperp - dGdvperp_Maxwell) - max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) - println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) - println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",fka.dGdvperp[end,end]) - if plot_dGdvperp - @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvpa2_err = abs(fka.d2Gdvpa2 - d2Gdvpa2_Maxwell) - max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) - println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) - println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",fka.d2Gdvpa2[end,end]) - if plot_d2Gdvpa2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_err.pdf") - savefig(outfile) - end - + @. d2Gdvperpdvpa_err = abs(fka.d2Gdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",fka.d2Gdvperpdvpa[end,end]) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) end - _block_synchronize() - if standalone - finalize_comms!() + @. dGdvperp_err = abs(fka.dGdvperp - dGdvperp_Maxwell) + max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",fka.dGdvperp[end,end]) + if plot_dGdvperp + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) end - #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - (results = (maximum(Cssp_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), - maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err))) - return results + @. d2Gdvpa2_err = abs(fka.d2Gdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",fka.d2Gdvpa2[end,end]) + if plot_d2Gdvpa2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end + end - - if test_Lagrange_integral - ngrid = 9 - nelement = 4 + _block_synchronize() + if standalone + finalize_comms!() + end + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + (results = (maximum(Cssp_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err))) + return results +end + +function test_strong_form_collision_operator(;ngrid=5,nelement_list=[2],plot_scan=true) + if size(nelement_list,1) == 1 + nelement = nelement_list[1] test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) end if test_Lagrange_integral_scan initialize_comms!() - ngrid = 5 - plot_scan = true - #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] - #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] - nelement_list = Int[2, 4] - #nelement_list = Int[100] - #nelement_list = Int[2,4,8,16] nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) max_dHdvpa_err = Array{mk_float,1}(undef,nscan) @@ -512,5 +494,10 @@ if abspath(PROGRAM_FILE) == @__FILE__ end finalize_comms!() end - +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + test_strong_form_collision_operator() end diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index c9210521c..58218f2ff 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -8,7 +8,7 @@ export enforce_boundary_conditions! export enforce_neutral_boundary_conditions! # functional testing -export create_and_init_boundary_distributions +export create_boundary_distributions export create_pdf # package From 6045a81c648a06b674faba11bb77a5da4bb95ba2 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 23 Nov 2023 16:10:34 +0000 Subject: [PATCH 256/331] Corrected typo in the last commit. --- fkpl_functional_test.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl index 111e62d0d..134facb45 100644 --- a/fkpl_functional_test.jl +++ b/fkpl_functional_test.jl @@ -410,8 +410,7 @@ function test_strong_form_collision_operator(;ngrid=5,nelement_list=[2],plot_sca if size(nelement_list,1) == 1 nelement = nelement_list[1] test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) - end - if test_Lagrange_integral_scan + else initialize_comms!() nscan = size(nelement_list,1) max_C_err = Array{mk_float,1}(undef,nscan) From e10c6105e26ef52823fe4a791201e026614a77da Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 09:34:53 +0000 Subject: [PATCH 257/331] Set default 'advance flags' for Fokker-Planck collisions to false `false` is the expected default setting for operator-splitting mode (if we ever re-enable that...). --- src/time_advance.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time_advance.jl b/src/time_advance.jl index df3b42bcc..65ce12da6 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -486,8 +486,8 @@ function setup_advance_flags(moments, composition, t_input, collisions, vpa_diffusion = false vz_diffusion = false explicit_fp_collisions = false - explicit_weakform_fp_collisions = true - explicit_fp_F_FM_collisions = true + explicit_weakform_fp_collisions = false + explicit_fp_F_FM_collisions = false # all advance flags remain false if using operator-splitting # otherwise, check to see if the flags need to be set to true if !t_input.split_operators From 2c562519ec700866f69248836d31924a71fad3b8 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 09:36:28 +0000 Subject: [PATCH 258/331] Remove unused dependencies --- Project.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Project.toml b/Project.toml index 1f95173c6..233c22b1b 100644 --- a/Project.toml +++ b/Project.toml @@ -4,14 +4,11 @@ authors = ["Michael Barnes "] version = "0.1.0" [deps] -ArbNumerics = "7e558dbc-694d-5a72-987c-6f4ebed21442" ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -Cubature = "667455a9-e2ce-5579-9412-b964f529a492" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" -Elliptic = "b305315f-e792-5b7a-8f41-49f472929428" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" @@ -20,7 +17,6 @@ HDF5_jll = "0234f1f7-429e-5d53-9886-15a909be8d59" IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" LegendrePolynomials = "3db4a2ba-fc88-11e8-3e01-49c72059a882" LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" From e996a762eef484118ccc42bcf554657e4fa8cf08 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 10:25:35 +0000 Subject: [PATCH 259/331] Remove unused 'buffer_dfn' arguments --- src/initial_conditions.jl | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 7d9a142ae..7cd3c61b4 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1150,8 +1150,7 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v @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, - scratch_dummy.buffer_vpavperpzrs_1) + scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4) end if r.n > 1 @@ -1159,7 +1158,7 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v @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, - scratch_dummy.buffer_vpavperpzrs_1, r_diffusion) + r_diffusion) end end function enforce_boundary_conditions!(fvec_out::scratch_pdf, moments, f_r_bc, vpa_bc, @@ -1175,9 +1174,9 @@ end enforce boundary conditions on f in r """ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc::String, - adv::T, vpa, vperp, z, r, composition, end1::AbstractArray{mk_float,4}, + 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}, buffer_dfn::AbstractArray{mk_float,5}, r_diffusion::Bool) where T + buffer2::AbstractArray{mk_float,4}, r_diffusion::Bool) nr = r.n @@ -1227,7 +1226,7 @@ enforce boundary conditions on charged particle f in z function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, adv, z, vperp, vpa, composition, end1::AbstractArray{mk_float,4}, end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, - buffer2::AbstractArray{mk_float,4}, buffer_dfn::AbstractArray{mk_float,5}) + 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 @@ -1341,8 +1340,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, - scratch_dummy.buffer_vzvrvzetazrsn_1) + scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4) end if r.n > 1 begin_sn_z_vzeta_vr_vz_region() @@ -1350,15 +1348,15 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, - scratch_dummy.buffer_vzvrvzetazrsn_1, r_diffusion) + r_diffusion) end end function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, - f_r_bc::AbstractArray{mk_float,6}, adv::T, vz, vr, vzeta, z, r, composition, + 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}, - buffer_dfn::AbstractArray{mk_float,6}, r_diffusion) where T #f_initial, + r_diffusion) #f_initial, bc = r.bc nr = r.n @@ -1409,8 +1407,7 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de 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}, - buffer_dfn::AbstractArray{mk_float,6}) + buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}) if z.nelement_global > z.nelement_local From a349cd90de3d6116f88e38ad6951a05a84e67ff3 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 10:30:35 +0000 Subject: [PATCH 260/331] Only load perpendicular_pressure, entropy_production when needed --- src/load_data.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/load_data.jl b/src/load_data.jl index 388a8dd80..7a2152a76 100644 --- a/src/load_data.jl +++ b/src/load_data.jl @@ -443,11 +443,13 @@ function load_charged_particle_moments_data(fid; printout=false, extended_moment # Read charged species thermal speed thermal_speed = load_variable(group, "thermal_speed") - # Read charged species perpendicular pressure - perpendicular_pressure = load_variable(group, "perpendicular_pressure") + if extended_moments + # Read charged species perpendicular pressure + perpendicular_pressure = load_variable(group, "perpendicular_pressure") - # Read charged species entropy_production - entropy_production = load_variable(group, "entropy_production") + # Read charged species entropy_production + entropy_production = load_variable(group, "entropy_production") + end if printout println("done.") From 5cb8e4728378a9ce5463a66e7ad7fad76df7b91b Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 13:19:08 +0000 Subject: [PATCH 261/331] Fix merge of vpa boundary condition For 'zero' boundary condition, need to use the bug-fixed version from 'master' (to support moment-kinetic modes). For 'zero_gradient', need to incorporate new implementation from 'merge_fkpl_collisions' into the `enforce_v_boundary_condition_local!()` function. --- src/initial_conditions.jl | 48 +++++++++++++-------------------------- src/time_advance.jl | 16 +++++++------ 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 7cd3c61b4..b31c0539e 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1134,7 +1134,7 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v @loop_s_r_z_vperp is ir iz ivperp begin # enforce the vpa BC # use that adv.speed independent of vpa - @views enforce_vpa_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, + @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 @@ -1297,8 +1297,9 @@ enforce boundary conditions on neutral particle distribution function """ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, boundary_distributions, density_neutral, uz_neutral, pz_neutral, moments, - density_ion, upar_ion, Er, r_adv, z_adv, vzeta_adv, vr_adv, vz_adv, r, z, vzeta, - vr, vz, composition, geometry, scratch_dummy, r_diffusion, vz_diffusion) + 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 @@ -1310,7 +1311,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, @views enforce_v_boundary_condition_local!(f_neutral[ivz,ivr,:,iz,ir,isn], vzeta.bc, vzeta_adv[isn].speed[ivz,ivr,:,iz,ir], - false) + false, vzeta, vzeta_spectral) end end if vr_adv !== nothing && vr.n_global > 1 && vr.bc != "none" @@ -1320,7 +1321,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, @views enforce_v_boundary_condition_local!(f_neutral[ivz,:,ivzeta,iz,ir,isn], vr.bc, vr_adv[isn].speed[ivz,:,ivzeta,iz,ir], - false) + false, vr, vr_spectral) end end if vz_adv !== nothing && vz.n_global > 1 && vz.bc != "none" @@ -1330,7 +1331,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, @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_diffusion, vz, vz_spectral) end end # f_initial contains the initial condition for enforcing a fixed-boundary-value condition @@ -2133,7 +2134,7 @@ end """ """ -function enforce_v_boundary_condition_local!(f, bc, speed, v_diffusion) +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 @@ -2146,43 +2147,25 @@ function enforce_v_boundary_condition_local!(f, bc, speed, v_diffusion) elseif bc == "both_zero" f[1] = 0.0 f[end] = 0.0 - elseif bc == "periodic" - f[1] = 0.5*(f[1]+f[end]) - f[end] = f[1] - end -end - -""" -""" -function enforce_vpa_boundary_condition_local!(f, bc, adv_speed, vpa_diffusion::Bool, vpa, vpa_spectral) - # define a zero that accounts for finite precision - zero = 1.0e-10 - dvpadt = adv_speed[1] #use that dvpa/dt is indendent of vpa in the current model - nvpa = size(f,1) - ngrid = vpa.ngrid - if bc == "zero" - if dvpadt > zero || vpa_diffusion - f[1] = 0.0 # -infty forced to zero - end - if dvpadt < zero || vpa_diffusion - f[end] = 0.0 # +infty forced to zero - end elseif bc == "zero_gradient" - D0 = vpa_spectral.lobatto.Dmat[1,:] + D0 = v_spectral.lobatto.Dmat[1,:] @loop_s_r_z_vperp is ir iz ivperp begin # adjust F(vpa = -L/2) so that d F / d vpa = 0 at vpa = -L/2 f[1,ivperp,iz,ir,is] = -sum(D0[2:ngrid].*f[2:ngrid,ivperp,iz,ir,is])/D0[1] end - D0 = vpa_spectral.lobatto.Dmat[end,:] + D0 = v_spectral.lobatto.Dmat[end,:] @loop_s_r_z_vperp is ir iz ivperp begin # adjust F(vpa = L/2) so that d F / d vpa = 0 at vpa = L/2 f[nvpa,ivperp,iz,ir,is] = -sum(D0[1:ngrid-1].*f[nvpa-ngrid+1:nvpa-1,ivperp,iz,ir,is])/D0[ngrid] end elseif bc == "periodic" - f[1] = 0.5*(f[nvpa]+f[1]) - f[nvpa] = f[1] + 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 """ @@ -2204,4 +2187,5 @@ function enforce_vperp_boundary_condition!(f,vperp,vperp_spectral) println("vperp bc not supported") end end + end diff --git a/src/time_advance.jl b/src/time_advance.jl index 65ce12da6..b5ee90ec1 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -403,10 +403,10 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, enforce_neutral_boundary_conditions!( pdf.neutral.norm, pdf.charged.norm, boundary_distributions, moments.neutral.dens, moments.neutral.uz, moments.neutral.pz, moments, - moments.charged.dens, moments.charged.upar, fields.Er, 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) + moments.charged.dens, moments.charged.upar, fields.Er, vzeta_spectral, + vr_spectral, vz_spectral, neutral_r_advect, neutral_z_advect, nothing, + nothing, neutral_vz_advect, r, z, vzeta, vr, vz, composition, geometry, + scratch_dummy, advance.r_diffusion, advance.vz_diffusion) begin_sn_r_z_region() @loop_sn_r_z isn ir iz begin @views hard_force_moment_constraints_neutral!( @@ -1359,6 +1359,7 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v old_scratch = scratch[istage] z_spectral, r_spectral, vpa_spectral, vperp_spectral = spectral_objects.z_spectral, spectral_objects.r_spectral, spectral_objects.vpa_spectral, spectral_objects.vperp_spectral + vzeta_spectral, vr_spectral, vz_spectral = spectral_objects.vzeta_spectral, spectral_objects.vr_spectral, spectral_objects.vz_spectral vpa_advect, r_advect, z_advect = advect_objects.vpa_advect, advect_objects.r_advect, advect_objects.z_advect neutral_z_advect, neutral_r_advect, neutral_vz_advect = advect_objects.neutral_z_advect, advect_objects.neutral_r_advect, advect_objects.neutral_vz_advect @@ -1456,9 +1457,10 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v enforce_neutral_boundary_conditions!(new_scratch.pdf_neutral, new_scratch.pdf, boundary_distributions, new_scratch.density_neutral, new_scratch.uz_neutral, new_scratch.pz_neutral, moments, new_scratch.density, new_scratch.upar, - fields.Er, 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) + 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) if moments.evolve_density && moments.enforce_conservation begin_sn_r_z_region() From 7210a0a65398dac29009635fa46f3689eb4237bf Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 22:25:49 +0000 Subject: [PATCH 262/331] Fix @debug_track_initialized for running in parallel The array that tracks whether each element is initialized must also be a shared-memory array, otherwise (which was happening before this commit) an error would be raised if an element was initialized on one process but read on another. This also means we have to use an array of `mk_int` for `is_initialized`, rather than an array of `Bool`, because MPI does not have a native Bool type, so we cannot create a shared array of `Bool`. --- src/communication.jl | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/communication.jl b/src/communication.jl index 226974f23..f18fe6729 100644 --- a/src/communication.jl +++ b/src/communication.jl @@ -357,7 +357,7 @@ end """ struct DebugMPISharedArray{T, N} <: AbstractArray{T, N} data::Array{T,N} - is_initialized::Array{Bool,N} + is_initialized::Array{mk_int,N} is_read::Array{Bool,N} is_written::Array{Bool, N} creation_stack_trace::String @@ -372,8 +372,10 @@ end # Constructors function DebugMPISharedArray(array::Array) dims = size(array) - is_initialized = Array{Bool}(undef, dims) - is_initialized .= false + is_initialized = allocate_shared(mk_int, dims; maybe_debug=false) + if block_rank[] == 0 + is_initialized .= 0 + end is_read = Array{Bool}(undef, dims) is_read .= false is_written = Array{Bool}(undef, dims) @@ -402,7 +404,7 @@ end Base.size(A::DebugMPISharedArray{T, N}) where {T, N} = size(A.data) function Base.getindex(A::DebugMPISharedArray{T, N}, I::Vararg{mk_int,N}) where {T, N} @debug_track_initialized begin - if !all(A.is_initialized[I...]) + if !all(A.is_initialized[I...] .== 1) if A.creation_stack_trace != "" error("Shared memory array read at $I before being initialized. " * "Array was created at:\n" @@ -419,7 +421,7 @@ end end function Base.setindex!(A::DebugMPISharedArray{T, N}, v::T, I::Vararg{mk_int,N}) where {T, N} @debug_track_initialized begin - A.is_initialized[I...] = true + A.is_initialized[I...] = 1 end A.is_written[I...] = true return setindex!(A.data, v, I...) @@ -474,12 +476,16 @@ dims - mk_int or Tuple{mk_int} Dimensions of the array to be created. Dimensions passed define the size of the array which is being handled by the 'block' (rather than the global array, or a subset for a single process). +maybe_debug - Bool + Can be set to `false` to force not creating a DebugMPISharedArray when debugging is + active. This avoids recursion when including a shared-memory array as a member of a + DebugMPISharedArray for debugging purposes. Returns ------- Array{mk_float} """ -function allocate_shared(T, dims) +function allocate_shared(T, dims; maybe_debug=true) br = block_rank[] bs = block_size[] n = prod(dims) @@ -491,7 +497,9 @@ function allocate_shared(T, dims) @debug_shared_array begin # If @debug_shared_array is active, create DebugMPISharedArray instead of Array - array = DebugMPISharedArray(array) + if maybe_debug + array = DebugMPISharedArray(array) + end end return array @@ -541,9 +549,11 @@ function allocate_shared(T, dims) @debug_shared_array begin # If @debug_shared_array is active, create DebugMPISharedArray instead of Array - debug_array = DebugMPISharedArray(array) - push!(global_debugmpisharedarray_store, debug_array) - return debug_array + if maybe_debug + debug_array = DebugMPISharedArray(array) + push!(global_debugmpisharedarray_store, debug_array) + return debug_array + end end return array From c6b081bb05dea245adcc13546d535aac766fc409 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Thu, 23 Nov 2023 22:28:14 +0000 Subject: [PATCH 263/331] Fix a couple of shared-memory errors --- src/fokker_planck.jl | 9 +++- src/fokker_planck_calculus.jl | 87 ++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 237f15fac..51dcfcafb 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -43,7 +43,7 @@ using LinearAlgebra: lu using ..initial_conditions: enforce_boundary_conditions! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float -using ..communication: MPISharedArray, global_rank +using ..communication: MPISharedArray, global_rank, _block_synchronize using ..velocity_moments: integrate_over_vspace using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom using ..calculus: derivative!, second_derivative! @@ -312,6 +312,9 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp dHdvpa[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) dHdvperp[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) end + # Need to synchronize as these arrays may be read outside the locally-owned set of + # ivperp, ivpa indices in assemble_explicit_collision_operator_rhs_parallel!() + _block_synchronize() else calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,@view(ffsp_in[:,:]), @@ -334,6 +337,10 @@ function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp dFdvpa[ivpa,ivperp] = dFdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) dFdvperp[ivpa,ivperp] = dFdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) end + # Need to synchronize as FF, dFdvpa, dFdvperp may be read outside the + # locally-owned set of ivperp, ivpa indices in + # assemble_explicit_collision_operator_rhs_parallel_analytical_inputs!() + _block_synchronize() assemble_explicit_collision_operator_rhs_parallel_analytical_inputs!(rhsc,rhsvpavperp, FF,dFdvpa,dFdvperp, d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2, diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 2d846a431..3f8884d1c 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1804,56 +1804,59 @@ end function assemble_explicit_collision_operator_rhs_serial!(rhsc,pdfs,d2Gspdvpa2,d2Gspdvperpdvpa, d2Gspdvperp2,dHspdvpa,dHspdvperp,ms,msp,nussp, vpa,vperp,YY_arrays::YY_collision_operator_arrays) - # assemble RHS of collision operator - @. rhsc = 0.0 - - # loop over elements - for ielement_vperp in 1:vperp.nelement_local - YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] - YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] - YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] - YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] + begin_serial_region() + @serial_region begin + # assemble RHS of collision operator + @. rhsc = 0.0 - for ielement_vpa in 1:vpa.nelement_local - YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] - YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] - YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] - YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + # loop over elements + for ielement_vperp in 1:vperp.nelement_local + YY0perp = YY_arrays.YY0perp[:,:,:,ielement_vperp] + YY1perp = YY_arrays.YY1perp[:,:,:,ielement_vperp] + YY2perp = YY_arrays.YY2perp[:,:,:,ielement_vperp] + YY3perp = YY_arrays.YY3perp[:,:,:,ielement_vperp] - # loop over field positions in each element - for ivperp_local in 1:vperp.ngrid - for ivpa_local in 1:vpa.ngrid - ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) - # carry out the matrix sum on each 2D element - for jvperpp_local in 1:vperp.ngrid - jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] - for kvperpp_local in 1:vperp.ngrid - kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] - for jvpap_local in 1:vpa.ngrid - jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] - pdfjj = pdfs[jvpap,jvperpp] - for kvpap_local in 1:vpa.ngrid - kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] - # first three lines represent parallel flux terms - # second three lines represent perpendicular flux terms - rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + - YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - - 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + - # end parallel flux, start of perpendicular flux - YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + - YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - - 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + for ielement_vpa in 1:vpa.nelement_local + YY0par = YY_arrays.YY0par[:,:,:,ielement_vpa] + YY1par = YY_arrays.YY1par[:,:,:,ielement_vpa] + YY2par = YY_arrays.YY2par[:,:,:,ielement_vpa] + YY3par = YY_arrays.YY3par[:,:,:,ielement_vpa] + + # loop over field positions in each element + for ivperp_local in 1:vperp.ngrid + for ivpa_local in 1:vpa.ngrid + ic_global, ivpa_global, ivperp_global = get_global_compound_index(vpa,vperp,ielement_vpa,ielement_vperp,ivpa_local,ivperp_local) + # carry out the matrix sum on each 2D element + for jvperpp_local in 1:vperp.ngrid + jvperpp = vperp.igrid_full[jvperpp_local,ielement_vperp] + for kvperpp_local in 1:vperp.ngrid + kvperpp = vperp.igrid_full[kvperpp_local,ielement_vperp] + for jvpap_local in 1:vpa.ngrid + jvpap = vpa.igrid_full[jvpap_local,ielement_vpa] + pdfjj = pdfs[jvpap,jvperpp] + for kvpap_local in 1:vpa.ngrid + kvpap = vpa.igrid_full[kvpap_local,ielement_vpa] + # first three lines represent parallel flux terms + # second three lines represent perpendicular flux terms + rhsc[ic_global] += (YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY2par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvpa2[kvpap,kvperpp] + + YY3perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] - + 2.0*(ms/msp)*YY0perp[kvperpp_local,jvperpp_local,ivperp_local]*YY1par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvpa[kvpap,kvperpp] + + # end parallel flux, start of perpendicular flux + YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY3par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperpdvpa[kvpap,kvperpp] + + YY2perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*d2Gspdvperp2[kvpap,kvperpp] - + 2.0*(ms/msp)*YY1perp[kvperpp_local,jvperpp_local,ivperp_local]*YY0par[kvpap_local,jvpap_local,ivpa_local]*pdfjj*dHspdvperp[kvpap,kvperpp]) + end end end end end - end - end + end + end end + # correct for minus sign due to integration by parts + # and multiply by the normalised collision frequency + @. rhsc *= -nussp end - # correct for minus sign due to integration by parts - # and multiply by the normalised collision frequency - @. rhsc *= -nussp return nothing end From ed1a8bd64136eb2fd3955db28dc3274fcd9d9f5e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 10 Sep 2023 12:23:37 +0100 Subject: [PATCH 264/331] Method for append_to_dynamic_var() that handles DebugMPISharedArray Use a method to extract the .data array from a DebugMPISharedArray - removes the need for debug copies of write_moments_data_to_binary() and write_dfns_data_to_binary(). --- src/file_io.jl | 150 ++----------------------------------------------- 1 file changed, 6 insertions(+), 144 deletions(-) diff --git a/src/file_io.jl b/src/file_io.jl index 91671f476..98b3d1c54 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -1119,6 +1119,12 @@ range is `1:n`). """ function append_to_dynamic_var() end +@debug_shared_array begin + function append_to_dynamic_var(data::DebugMPISharedArray, args...; kwargs...) + return append_to_dynamic_var(data.data, args...; kwargs...) + end +end + """ write time-dependent moments data to the binary output file """ @@ -1299,150 +1305,6 @@ function write_dfns_data_to_binary(ff, ff_neutral, moments, fields, t, n_ion_spe return nothing end -@debug_shared_array begin - # Special versions when using DebugMPISharedArray to avoid implicit conversion to - # Array, which is forbidden. - 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, 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 time for this time slice to the hdf5 file - append_to_dynamic_var(io_moments.time, t, t_idx, 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.data, t_idx, parallel_io, z, - r) - append_to_dynamic_var(io_moments.Er, fields.Er.data, t_idx, parallel_io, z, r) - append_to_dynamic_var(io_moments.Ez, fields.Ez.data, 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.data, t_idx, - parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_flow, moments.charged.upar.data, - t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_pressure, moments.charged.ppar.data, - t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.perpendicular_pressure, moments.charged.pperp.data, - t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_heat_flux, - moments.charged.qpar.data, t_idx, parallel_io, z, r, - n_ion_species) - append_to_dynamic_var(io_moments.thermal_speed, moments.charged.vth.data, - t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.entropy_production, moments.charged.dSdt.data, - t_idx, z, r, n_ion_species) - append_to_dynamic_var(io_moments.chodura_integral_lower, moments.charged.chodura_integral_lower.data, - t_idx, parallel_io, r, n_ion_species) - append_to_dynamic_var(io_moments.chodura_integral_upper, moments.charged.chodura_integral_upper.data, - t_idx, parallel_io, r, n_ion_species) - if io_moments.external_source_amplitude !== nothing - append_to_dynamic_var(io_moments.external_source_amplitude, - moments.charged.external_source_amplitude.data, - t_idx, parallel_io, z, r) - end - if io_moments.external_source_controller_integral !== nothing - if size(moments.charged.external_source_controller_integral) == (1,1) - append_to_dynamic_var(io_moments.external_source_controller_integral, - moments.charged.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,data, - t_idx, parallel_io, z, r) - end - end - if n_neutral_species > 0 - append_to_dynamic_var(io_moments.density_neutral, - moments.neutral.dens.data, t_idx, parallel_io, z, r, - n_neutral_species) - append_to_dynamic_var(io_moments.uz_neutral, moments.neutral.uz.data, - t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.pz_neutral, moments.neutral.pz.data, - t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.qz_neutral, moments.neutral.qz.data, - t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.thermal_speed_neutral, - moments.neutral.vth.data, 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) - end - if io_moments.external_source_neutral_controller_integral !== nothing - if size(moments.neutral.external_source_neutral_controller_integral) == (1,1) - append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, - moments.neutral.external_source_controller_integral[1,1], - t_idx, parallel_io) - else - append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, - moments.neutral.external_source_controller_integral, - t_idx, parallel_io, z, r) - end - end - end - - append_to_dynamic_var(io_moments.time_for_run, time_for_run, t_idx, - parallel_io) - - closefile && close(io_moments.fid) - end - return nothing - end - - # Special versions when using DebugMPISharedArray to avoid implicit conversion to - # Array, which is forbidden. - function write_dfns_data_to_binary(ff::DebugMPISharedArray, - ff_neutral::DebugMPISharedArray, moments, fields, - t, n_ion_species, n_neutral_species, - io_or_file_info_dfns, t_idx, r, z, vperp, vpa, - 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 - - # 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, r, - z) - - 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.data, t_idx, parallel_io, vpa, vperp, z, - r, n_ion_species) - if n_neutral_species > 0 - append_to_dynamic_var(io_dfns.f_neutral, ff_neutral.data, t_idx, - parallel_io, vz, vr, vzeta, z, r, n_neutral_species) - end - - closefile && close(io_dfns.fid) - end - return nothing - end -end - """ close all opened output files """ From bf8eda6b70ff051bc28cf0ff03679f474c8223a3 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 08:17:50 +0000 Subject: [PATCH 265/331] Support `vperp.bc = "none"` option ...and make it the default when Fokker-Planck collisions are not active. This prevents an error (when not using "gausslegendre" discretization) where the previously-implemented option for the vperp boundary condition (which supports collisions) was imposed regardless of the `vperp.bc` setting, but was only implemented for `vperp.discretization = "gausslegendre"`. --- src/initial_conditions.jl | 35 +++++++++++++++++++++-------------- src/moment_kinetics_input.jl | 6 +----- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index b7c4d5478..97bc46b3b 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1141,7 +1141,7 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v end if vperp.n > 1 begin_s_r_z_vpa_region() - @views enforce_vperp_boundary_condition!(f,vperp,vperp_spectral) + @views enforce_vperp_boundary_condition!(f, vperp.bc, vperp, vperp_spectral) end if z.n > 1 begin_s_r_vperp_vpa_region() @@ -2169,22 +2169,29 @@ end """ enforce zero boundary condition at vperp -> infinity """ -function enforce_vperp_boundary_condition!(f,vperp,vperp_spectral) - nvperp = vperp.n - ngrid = vperp.ngrid - # set zero boundary condition - @loop_s_r_z_vpa is ir iz ivpa begin - f[ivpa,nvperp,iz,ir,is] = 0.0 - end - # set regularity condition d F / d vperp = 0 at vperp = 0 - if vperp.discretization == "gausslegendre_pseudospectral" - D0 = vperp_spectral.radau.D0 +function enforce_vperp_boundary_condition!(f, bc, vperp, vperp_spectral) + if bc == "zero" + nvperp = vperp.n + ngrid = vperp.ngrid + # set zero boundary condition @loop_s_r_z_vpa is ir iz ivpa begin - # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 - f[ivpa,1,iz,ir,is] = -sum(D0[2:ngrid].*f[ivpa,2:ngrid,iz,ir,is])/D0[1] + f[ivpa,nvperp,iz,ir,is] = 0.0 + end + # set regularity condition d F / d vperp = 0 at vperp = 0 + if vperp.discretization == "gausslegendre_pseudospectral" + D0 = vperp_spectral.radau.D0 + @loop_s_r_z_vpa is ir iz ivpa begin + # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 + f[ivpa,1,iz,ir,is] = -sum(D0[2:ngrid].*f[ivpa,2:ngrid,iz,ir,is])/D0[1] + end + else + println("vperp.bc=\"$bc\" not supported by discretization " + * "$(vperp.discretization)") end + elseif bc == "none" + # Do nothing else - println("vperp bc not supported") + error("Unsupported boundary condition option '$bc' for vperp") end end diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 8e4bc4735..ff536f3a9 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -302,10 +302,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) # 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)) # determine the boundary condition - # only supported option at present is "zero" and "periodic" - # MRH probably need to add new bc option here - # MRH no vperp bc currently imposed so option below not used - vperp.bc = get(scan_input, "vperp_bc", "periodic") + vperp.bc = get(scan_input, "vperp_bc", collisions.nuii > 0.0 ? "zero" : "none") # determine the discretization option for the vperp grid # supported options are "finite_difference_vperp" "chebyshev_pseudospectral" vperp.discretization = get(scan_input, "vperp_discretization", "chebyshev_pseudospectral") @@ -1085,7 +1082,6 @@ function check_coordinate_input(coord, coord_name, io) input_option_error("$coord_name.discretization", coord.discretization) end # boundary_option determines coord boundary condition - # supported options are "constant" and "periodic" if coord.bc == "constant" println(io,">$coord_name.bc = 'constant'. enforcing constant incoming BC in $coord_name.") elseif coord.bc == "zero" From 22a3916d7f24b9153fbd713b8d6cda5498be1b99 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 08:41:40 +0000 Subject: [PATCH 266/331] Skip finite-difference inputs in debug_test The finite difference options are never used any more, so as the debug checks take a long time it is better not to waste the time on finite difference runs. --- debug_test/harrisonthompson_inputs.jl | 2 +- debug_test/sound_wave_inputs.jl | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/debug_test/harrisonthompson_inputs.jl b/debug_test/harrisonthompson_inputs.jl index 1ead157cc..bf388c61e 100644 --- a/debug_test/harrisonthompson_inputs.jl +++ b/debug_test/harrisonthompson_inputs.jl @@ -72,6 +72,6 @@ test_input_chebyshev = merge(test_input_finite_difference, "vz_nelement" => 2)) test_input_list = [ - test_input_finite_difference, + #test_input_finite_difference, test_input_chebyshev, ] diff --git a/debug_test/sound_wave_inputs.jl b/debug_test/sound_wave_inputs.jl index 83721b630..dee7158cf 100644 --- a/debug_test/sound_wave_inputs.jl +++ b/debug_test/sound_wave_inputs.jl @@ -258,22 +258,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 b1902a448578126d9b6180ef4ac7329910001143 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 09:48:13 +0000 Subject: [PATCH 267/331] Update default for vperp.bc to be zero when vperp-diffusion is used Also print warning when vperp dimension is used (vperp.n_global>1) but the "zero" boundary condition (which imposes regularity at vperp=0) is not set. --- src/moment_kinetics_input.jl | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index ff536f3a9..15885d3c1 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -289,9 +289,6 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) vpa.fd_option = get(scan_input, "vpa_finite_difference_option", "third_order_upwind") vpa.element_spacing_option = get(scan_input, "vpa_element_spacing_option", "uniform") - num_diss_params = setup_numerical_dissipation( - get(scan_input, "numerical_dissipation", Dict{String,Any}()), true) - # overwrite some default parameters related to the vperp grid # ngrid is the number of grid points per element vperp.ngrid = get(scan_input, "vperp_ngrid", 1) @@ -301,8 +298,9 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) 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)) - # determine the boundary condition - vperp.bc = get(scan_input, "vperp_bc", collisions.nuii > 0.0 ? "zero" : "none") + # 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. + # # determine the discretization option for the vperp grid # supported options are "finite_difference_vperp" "chebyshev_pseudospectral" vperp.discretization = get(scan_input, "vperp_discretization", "chebyshev_pseudospectral") @@ -374,6 +372,13 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) 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) + + # 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. + vperp.bc = get(scan_input, "vperp_bc", + (collisions.nuii > 0.0 || + num_diss_params.vperp_dissipation_coefficient > 0.0) ? + "zero" : "none") ######################################################################### ########## end user inputs. do not modify following code! ############### @@ -533,10 +538,10 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) io = devnull end - # check input to catch errors/unsupported options - check_input(io, output_dir, nstep, dt, r_immutable, z_immutable, vpa_immutable, vperp_immutable, - composition, species_immutable, evolve_moments, num_diss_params, - save_inputs_to_txt, collisions) + # check input (and initialized coordinate structs) to catch errors/unsupported options + check_input(io, output_dir, nstep, dt, r, z, vpa, vperp, composition, + species_immutable, evolve_moments, num_diss_params, save_inputs_to_txt, + collisions) # return immutable structs for z, vpa, species and composition all_inputs = (io_immutable, evolve_moments, t_input, z, z_spectral, r, r_spectral, @@ -1103,6 +1108,11 @@ function check_coordinate_input(coord, coord_name, io) println(io,">using ", coord.ngrid, " grid points per $coord_name element on ", coord.nelement_global, " elements across the $coord_name domain [", 0.0, ",", coord.L, "].") + + if vperp.bc != "zero" && vperp.n_global > 1 + println("WARNING: regularity condition (df/dvperp=0 at vperp=0) not being " + * "imposed. Collisions or vperp-diffusion will be unstable.") + end else println(io,">using ", coord.ngrid, " grid points per $coord_name element on ", coord.nelement_global, " elements across the $coord_name domain [", From aa59d3bc2d8fc60b72ef09568da6388f7ff709e3 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 09:52:52 +0000 Subject: [PATCH 268/331] Remove vperp_bc="periodic" from tests, examples, saved inputs This option was never actually used previously, and is now explicitly unsupported. The default for `vperp_bc` should do sensible things in all existing cases. --- debug_test/mms_inputs.jl | 1 - examples/fokker-planck/fokker-planck-relaxation.toml | 2 -- runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml | 1 - runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml | 1 - runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml | 1 - runs/2D-sound-wave_cheb-manf-Dirichlet.toml | 1 - runs/2D-sound-wave_cheb-vperp-manf.toml | 1 - runs/2D-sound-wave_cheb-vperp.toml | 1 - runs/2D-sound-wave_cheb-with-neutrals-manf.toml | 1 - runs/2D-sound-wave_cheb-with-neutrals-small.toml | 1 - runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_16_vperp_16.toml | 1 - runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_2_vperp_2.toml | 1 - runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_4_vperp_4.toml | 1 - runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_8_vperp_8.toml | 1 - ...-sound-wave_cheb_ion_only_nel_r_12_z_12_vpa_12_vperp_12.toml | 1 - ...-sound-wave_cheb_ion_only_nel_r_16_z_16_vpa_16_vperp_16.toml | 1 - runs/2D-sound-wave_cheb_ion_only_nel_r_2_z_2_vpa_2_vperp_2.toml | 1 - runs/2D-sound-wave_cheb_ion_only_nel_r_4_z_4_vpa_4_vperp_4.toml | 1 - runs/2D-sound-wave_cheb_ion_only_nel_r_8_z_8_vpa_8_vperp_8.toml | 1 - runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_16_vperp_16.toml | 1 - runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_2_vperp_2.toml | 1 - runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_4_vperp_4.toml | 1 - runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_8_vperp_8.toml | 1 - runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_16.toml | 1 - runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_2.toml | 1 - runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_4.toml | 1 - runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_8.toml | 1 - runs/2D-wall-bc_cheb.toml | 1 - runs/2D-wall_MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml | 1 - runs/2D-wall_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml | 1 - runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml | 1 - runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml | 1 - runs/2D-wall_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml | 1 - runs/2D-wall_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml | 1 - runs/2D-wall_cheb-with-neutrals-small.toml | 1 - ...b-with-neutrals-with-sheath_nel_r_1_z_2_vpa_12_vperp_12.toml | 1 - ...b-with-neutrals-with-sheath_nel_r_1_z_2_vpa_16_vperp_16.toml | 1 - ...b-with-neutrals-with-sheath_nel_r_1_z_2_vpa_24_vperp_24.toml | 1 - ...heb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_2_vperp_2.toml | 1 - ...heb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_4_vperp_4.toml | 1 - ...heb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_8_vperp_8.toml | 1 - runs/2D-wall_cheb-with-neutrals.toml | 1 - ...D-wall_cheb-with-neutrals_nel_r_16_z_16_vpa_16_vperp_16.toml | 1 - ...2D-wall_cheb-with-neutrals_nel_r_1_z_12_vpa_12_vperp_12.toml | 1 - ...2D-wall_cheb-with-neutrals_nel_r_1_z_16_vpa_16_vperp_16.toml | 1 - .../2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_12_vperp_12.toml | 1 - .../2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_16_vperp_16.toml | 1 - .../2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_24_vperp_24.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_2_vperp_2.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_4_vperp_4.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_8_vperp_8.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_1_z_4_vpa_4_vperp_4.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_1_z_8_vpa_8_vperp_8.toml | 1 - .../2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_12_vperp_12.toml | 1 - .../2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_16_vperp_16.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_2_vperp_2.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_4_vperp_4.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_8_vperp_8.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_4_z_4_vpa_4_vperp_4.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_6_z_6_vpa_6_vperp_6.toml | 1 - runs/2D-wall_cheb-with-neutrals_nel_r_8_z_8_vpa_8_vperp_8.toml | 1 - runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml | 1 - test/fokker_planck_time_evolution_tests.jl | 1 - test/sound_wave_tests.jl | 1 - 64 files changed, 65 deletions(-) diff --git a/debug_test/mms_inputs.jl b/debug_test/mms_inputs.jl index c9d9bf5f3..328e7aa3c 100644 --- a/debug_test/mms_inputs.jl +++ b/debug_test/mms_inputs.jl @@ -58,7 +58,6 @@ test_input = Dict( "vperp_ngrid" => 3, "vperp_nelement" => 2, "vperp_L" => 6.0, - "vperp_bc" => "periodic", "vperp_discretization" => "chebyshev_pseudospectral", "vz_ngrid" => 3, "vz_nelement" => 2, diff --git a/examples/fokker-planck/fokker-planck-relaxation.toml b/examples/fokker-planck/fokker-planck-relaxation.toml index 45c4bd352..b12454e6f 100644 --- a/examples/fokker-planck/fokker-planck-relaxation.toml +++ b/examples/fokker-planck/fokker-planck-relaxation.toml @@ -59,8 +59,6 @@ vpa_discretization = "gausslegendre_pseudospectral" vperp_ngrid = 3 vperp_nelement = 3 vperp_L = 3.0 -# vperp_bc variable not used -vperp_bc = "periodic" vperp_discretization = "gausslegendre_pseudospectral" # Fokker-Planck operator requires the "gausslegendre_pseudospectral # options for the vpa and vperp grids 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 0fb7e9edd..60f09b084 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 e9d44cc5b..98efd587f 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 @@ -58,7 +58,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" vperp_discretization = "chebyshev_pseudospectral" vz_ngrid = 17 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 ae02bfd9c..135f5817e 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 @@ -58,7 +58,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 17 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" vperp_discretization = "chebyshev_pseudospectral" vz_ngrid = 17 diff --git a/runs/2D-sound-wave_cheb-manf-Dirichlet.toml b/runs/2D-sound-wave_cheb-manf-Dirichlet.toml index 9f19ed6c4..8efc5dfd0 100644 --- a/runs/2D-sound-wave_cheb-manf-Dirichlet.toml +++ b/runs/2D-sound-wave_cheb-manf-Dirichlet.toml @@ -49,7 +49,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 8.0 -vperp_bc = "periodic" vperp_discretization = "finite_difference" [manufactured_solns] diff --git a/runs/2D-sound-wave_cheb-vperp-manf.toml b/runs/2D-sound-wave_cheb-vperp-manf.toml index 8bfce7cdc..a990f3455 100644 --- a/runs/2D-sound-wave_cheb-vperp-manf.toml +++ b/runs/2D-sound-wave_cheb-vperp-manf.toml @@ -49,7 +49,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 8.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb-vperp.toml b/runs/2D-sound-wave_cheb-vperp.toml index 5ff7a9c11..2c4aad8f2 100644 --- a/runs/2D-sound-wave_cheb-vperp.toml +++ b/runs/2D-sound-wave_cheb-vperp.toml @@ -49,5 +49,4 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 8.0 -vperp_bc = "periodic" vperp_discretization = "finite_difference_vperp" diff --git a/runs/2D-sound-wave_cheb-with-neutrals-manf.toml b/runs/2D-sound-wave_cheb-with-neutrals-manf.toml index 689af6f5d..0db504551 100644 --- a/runs/2D-sound-wave_cheb-with-neutrals-manf.toml +++ b/runs/2D-sound-wave_cheb-with-neutrals-manf.toml @@ -53,7 +53,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 8.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb-with-neutrals-small.toml b/runs/2D-sound-wave_cheb-with-neutrals-small.toml index 60134f5d1..cf6c210d7 100644 --- a/runs/2D-sound-wave_cheb-with-neutrals-small.toml +++ b/runs/2D-sound-wave_cheb-with-neutrals-small.toml @@ -49,7 +49,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 2 vperp_L = 8.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_16_vperp_16.toml b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_16_vperp_16.toml index 24c9a8d48..2f8a0ed55 100644 --- a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_16_vperp_16.toml +++ b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_16_vperp_16.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_2_vperp_2.toml b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_2_vperp_2.toml index 37f1c2022..53d15bda5 100644 --- a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_2_vperp_2.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_4_vperp_4.toml b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_4_vperp_4.toml index 8183821d4..27dcd15dc 100644 --- a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_4_vperp_4.toml +++ b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_4_vperp_4.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_8_vperp_8.toml b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_8_vperp_8.toml index 417d58147..73805d403 100644 --- a/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_8_vperp_8.toml +++ b/runs/2D-sound-wave_cheb_cxiz_nel_r_2_z_2_vpa_8_vperp_8.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_ion_only_nel_r_12_z_12_vpa_12_vperp_12.toml b/runs/2D-sound-wave_cheb_ion_only_nel_r_12_z_12_vpa_12_vperp_12.toml index 4099fe115..8d5dea85c 100644 --- a/runs/2D-sound-wave_cheb_ion_only_nel_r_12_z_12_vpa_12_vperp_12.toml +++ b/runs/2D-sound-wave_cheb_ion_only_nel_r_12_z_12_vpa_12_vperp_12.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 12 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_ion_only_nel_r_16_z_16_vpa_16_vperp_16.toml b/runs/2D-sound-wave_cheb_ion_only_nel_r_16_z_16_vpa_16_vperp_16.toml index 94f315ddc..bd58a2fb6 100644 --- a/runs/2D-sound-wave_cheb_ion_only_nel_r_16_z_16_vpa_16_vperp_16.toml +++ b/runs/2D-sound-wave_cheb_ion_only_nel_r_16_z_16_vpa_16_vperp_16.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_ion_only_nel_r_2_z_2_vpa_2_vperp_2.toml b/runs/2D-sound-wave_cheb_ion_only_nel_r_2_z_2_vpa_2_vperp_2.toml index a7b0ae9f3..9535c9525 100644 --- a/runs/2D-sound-wave_cheb_ion_only_nel_r_2_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-sound-wave_cheb_ion_only_nel_r_2_z_2_vpa_2_vperp_2.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_ion_only_nel_r_4_z_4_vpa_4_vperp_4.toml b/runs/2D-sound-wave_cheb_ion_only_nel_r_4_z_4_vpa_4_vperp_4.toml index 5da1bc0f4..4df97d26b 100644 --- a/runs/2D-sound-wave_cheb_ion_only_nel_r_4_z_4_vpa_4_vperp_4.toml +++ b/runs/2D-sound-wave_cheb_ion_only_nel_r_4_z_4_vpa_4_vperp_4.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_ion_only_nel_r_8_z_8_vpa_8_vperp_8.toml b/runs/2D-sound-wave_cheb_ion_only_nel_r_8_z_8_vpa_8_vperp_8.toml index bfc3b50e2..d610acab7 100644 --- a/runs/2D-sound-wave_cheb_ion_only_nel_r_8_z_8_vpa_8_vperp_8.toml +++ b/runs/2D-sound-wave_cheb_ion_only_nel_r_8_z_8_vpa_8_vperp_8.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_16_vperp_16.toml b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_16_vperp_16.toml index 120213b7d..103e0949a 100644 --- a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_16_vperp_16.toml +++ b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_16_vperp_16.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_2_vperp_2.toml b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_2_vperp_2.toml index e2ded34e1..4903417f0 100644 --- a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_2_vperp_2.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_4_vperp_4.toml b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_4_vperp_4.toml index f7f2ab310..f1b160980 100644 --- a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_4_vperp_4.toml +++ b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_4_vperp_4.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_8_vperp_8.toml b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_8_vperp_8.toml index e6af36280..59424f32a 100644 --- a/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_8_vperp_8.toml +++ b/runs/2D-sound-wave_cheb_nel_r_2_z_2_vpa_8_vperp_8.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_16.toml b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_16.toml index 2848ab359..b51c20e82 100644 --- a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_16.toml +++ b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_16.toml @@ -53,7 +53,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_2.toml b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_2.toml index 71f5912a2..468a3e838 100644 --- a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_2.toml +++ b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_2.toml @@ -53,7 +53,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_4.toml b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_4.toml index 0e21a43a4..77521a9d9 100644 --- a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_4.toml +++ b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_4.toml @@ -53,7 +53,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_8.toml b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_8.toml index f7dcd08c8..dab39facd 100644 --- a/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_8.toml +++ b/runs/2D-wall-Dirichlet_nel_r_2_z_2_vpa_8.toml @@ -53,7 +53,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall-bc_cheb.toml b/runs/2D-wall-bc_cheb.toml index ac8bb46d0..93d4da750 100644 --- a/runs/2D-wall-bc_cheb.toml +++ b/runs/2D-wall-bc_cheb.toml @@ -65,7 +65,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 8 vperp_nelement = 8 vperp_L = 8.0 -vperp_bc = "periodic" vperp_discretization = "chebyshev_pseudospectral" [manufactured_solns] 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 9721aa455..52f761612 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 127af686c..e67fc2df9 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 ddc151d63..5bbb30479 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 60623634b..0ebf89144 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 60defe401..7ace633dd 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 b2ec23681..672f7d289 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 @@ -56,7 +56,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 1 vperp_nelement = 1 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-small.toml b/runs/2D-wall_cheb-with-neutrals-small.toml index 655bbd490..a8134e276 100644 --- a/runs/2D-wall_cheb-with-neutrals-small.toml +++ b/runs/2D-wall_cheb-with-neutrals-small.toml @@ -50,7 +50,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 5 vperp_nelement = 2 vperp_L = 8.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_12_vperp_12.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_12_vperp_12.toml index ac9ab4880..2d5ba9fc2 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_12_vperp_12.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_12_vperp_12.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 12 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_16_vperp_16.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_16_vperp_16.toml index 02899d9fa..6ed59d97d 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_16_vperp_16.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_16_vperp_16.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_24_vperp_24.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_24_vperp_24.toml index 416d6b116..566d6744d 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_24_vperp_24.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_24_vperp_24.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 24 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_2_vperp_2.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_2_vperp_2.toml index 2fe1ec1fc..d6e47771d 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_2_vperp_2.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_4_vperp_4.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_4_vperp_4.toml index f424f965a..f9e95e223 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_4_vperp_4.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_4_vperp_4.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_8_vperp_8.toml b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_8_vperp_8.toml index 5fdc2e510..8f2478bd2 100644 --- a/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_8_vperp_8.toml +++ b/runs/2D-wall_cheb-with-neutrals-with-sheath_nel_r_1_z_2_vpa_8_vperp_8.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals.toml b/runs/2D-wall_cheb-with-neutrals.toml index 6288ec559..e7d60078f 100644 --- a/runs/2D-wall_cheb-with-neutrals.toml +++ b/runs/2D-wall_cheb-with-neutrals.toml @@ -50,7 +50,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 11 vperp_L = 16.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_16_z_16_vpa_16_vperp_16.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_16_z_16_vpa_16_vperp_16.toml index 147cfc805..c084f3d28 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_16_z_16_vpa_16_vperp_16.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_16_z_16_vpa_16_vperp_16.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_12_vpa_12_vperp_12.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_12_vpa_12_vperp_12.toml index a1d8ebd6d..976392adc 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_12_vpa_12_vperp_12.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_12_vpa_12_vperp_12.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 12 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_16_vpa_16_vperp_16.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_16_vpa_16_vperp_16.toml index fb25575bc..5b22dd944 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_16_vpa_16_vperp_16.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_16_vpa_16_vperp_16.toml @@ -54,7 +54,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_12_vperp_12.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_12_vperp_12.toml index 3673d632a..4ae06bc8c 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_12_vperp_12.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_12_vperp_12.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 12 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_16_vperp_16.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_16_vperp_16.toml index 749dc2f14..cb5e4038c 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_16_vperp_16.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_16_vperp_16.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_24_vperp_24.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_24_vperp_24.toml index aa25d5783..cb6dd76b3 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_24_vperp_24.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_24_vperp_24.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 24 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_2_vperp_2.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_2_vperp_2.toml index cf8f33021..6f38efa9d 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_2_vperp_2.toml @@ -54,7 +54,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_4_vperp_4.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_4_vperp_4.toml index 7a53c6bcc..2ad591e4e 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_4_vperp_4.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_4_vperp_4.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_8_vperp_8.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_8_vperp_8.toml index 94feaf81f..65920af8b 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_8_vperp_8.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_2_vpa_8_vperp_8.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_4_vpa_4_vperp_4.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_4_vpa_4_vperp_4.toml index 33eabe22e..7a7673f95 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_4_vpa_4_vperp_4.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_4_vpa_4_vperp_4.toml @@ -54,7 +54,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_8_vpa_8_vperp_8.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_8_vpa_8_vperp_8.toml index 28de00684..86725b3a7 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_8_vpa_8_vperp_8.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_1_z_8_vpa_8_vperp_8.toml @@ -54,7 +54,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_12_vperp_12.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_12_vperp_12.toml index 5c8e53a4a..69a41b50d 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_12_vperp_12.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_12_vperp_12.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 12 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_16_vperp_16.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_16_vperp_16.toml index 54f59efbc..f5101a689 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_16_vperp_16.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_16_vperp_16.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 16 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_2_vperp_2.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_2_vperp_2.toml index ed9beb1c8..a5fe8f4b8 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_2_vperp_2.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_2_vperp_2.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 2 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_4_vperp_4.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_4_vperp_4.toml index e2df0c0de..3a28baf53 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_4_vperp_4.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_4_vperp_4.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_8_vperp_8.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_8_vperp_8.toml index e8af4a3ba..84ba8ade6 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_8_vperp_8.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_2_z_2_vpa_8_vperp_8.toml @@ -52,7 +52,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 6 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_4_z_4_vpa_4_vperp_4.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_4_z_4_vpa_4_vperp_4.toml index 2153ff79b..c22916ed8 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_4_z_4_vpa_4_vperp_4.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_4_z_4_vpa_4_vperp_4.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 4 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_6_z_6_vpa_6_vperp_6.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_6_z_6_vpa_6_vperp_6.toml index 3b47196eb..c6b74b038 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_6_z_6_vpa_6_vperp_6.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_6_z_6_vpa_6_vperp_6.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 6 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" diff --git a/runs/2D-wall_cheb-with-neutrals_nel_r_8_z_8_vpa_8_vperp_8.toml b/runs/2D-wall_cheb-with-neutrals_nel_r_8_z_8_vpa_8_vperp_8.toml index 82c82d23a..af575b988 100644 --- a/runs/2D-wall_cheb-with-neutrals_nel_r_8_z_8_vpa_8_vperp_8.toml +++ b/runs/2D-wall_cheb-with-neutrals_nel_r_8_z_8_vpa_8_vperp_8.toml @@ -51,7 +51,6 @@ vpa_discretization = "chebyshev_pseudospectral" vperp_ngrid = 9 vperp_nelement = 8 vperp_L = 6.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" vperp_discretization = "chebyshev_pseudospectral" 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 2d5cc827e..2f32d9ca3 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 @@ -67,7 +67,6 @@ vpa_discretization = "gausslegendre_pseudospectral" vperp_ngrid = 3 vperp_nelement = 3 vperp_L = 3.0 -vperp_bc = "periodic" #vperp_discretization = "finite_difference" #vperp_discretization = "chebyshev_pseudospectral" vperp_discretization = "gausslegendre_pseudospectral" diff --git a/test/fokker_planck_time_evolution_tests.jl b/test/fokker_planck_time_evolution_tests.jl index 88b686f47..6ce83ef66 100644 --- a/test/fokker_planck_time_evolution_tests.jl +++ b/test/fokker_planck_time_evolution_tests.jl @@ -115,7 +115,6 @@ test_input_gauss_legendre = Dict("run_name" => "gausslegendre_pseudospectral", "vperp_ngrid" => 3, "vperp_nelement" => 3, "vperp_L" => 3.0, - "vperp_bc" => "periodic", "vperp_discretization" => "gausslegendre_pseudospectral", "n_rk_stages" => 4, "split_operators" => false, diff --git a/test/sound_wave_tests.jl b/test/sound_wave_tests.jl index dc18220c1..21c187288 100644 --- a/test/sound_wave_tests.jl +++ b/test/sound_wave_tests.jl @@ -63,7 +63,6 @@ test_input_finite_difference = Dict("n_ion_species" => 1, "vperp_ngrid" => 1, "vperp_nelement" => 1, "vperp_L" => 1.0, - "vperp_bc" => "periodic", "vperp_discretization" => "finite_difference", "vpa_ngrid" => 180, "vpa_nelement" => 1, From 7d5d1c539363e0d293e50a23c356372973e87328 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Fri, 24 Nov 2023 10:46:05 +0000 Subject: [PATCH 269/331] Fix typo in check_coordinate_input. Tests still fail. --- src/moment_kinetics_input.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 15885d3c1..0b0c56e4c 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -1109,7 +1109,7 @@ function check_coordinate_input(coord, coord_name, io) coord.nelement_global, " elements across the $coord_name domain [", 0.0, ",", coord.L, "].") - if vperp.bc != "zero" && vperp.n_global > 1 + if coord.bc != "zero" && coord.n_global > 1 println("WARNING: regularity condition (df/dvperp=0 at vperp=0) not being " * "imposed. Collisions or vperp-diffusion will be unstable.") end From 1396824dbdf22a5033adbe6efbce80ac73250d32 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 14:48:55 +0000 Subject: [PATCH 270/331] Fix a couple of merge errors in post_processing.jl --- src/post_processing.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/post_processing.jl b/src/post_processing.jl index 85e625dc1..34823dc47 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -384,6 +384,7 @@ function get_geometry_and_composition(scan_input,n_ion_species,n_neutral_species use_test_neutral_wall_pdf = get(scan_input, "use_test_neutral_wall_pdf", false) # constant to be used to test nonzero Er in wall boundary condition Er_constant = get(scan_input, "Er_constant", 0.0) + recycling_fraction = get(scan_input, "recycling_fraction", 1.0) # constant to be used to control Ez divergences epsilon_offset = get(scan_input, "epsilon_offset", 0.001) # bool to control if dfni is a function of vpa or vpabar in MMS test @@ -399,7 +400,7 @@ function get_geometry_and_composition(scan_input,n_ion_species,n_neutral_species me_over_mi = 1.0/1836.0 composition = species_composition(n_species, n_ion_species, n_neutral_species, electron_physics, use_test_neutral_wall_pdf, T_e, T_wall, phi_wall, Er_constant, - mn_over_mi, me_over_mi, allocate_float(n_species)) + mn_over_mi, me_over_mi, recycling_fraction, allocate_float(n_species)) return geometry, composition end @@ -696,8 +697,9 @@ function analyze_and_plot_data(prefix...; run_index=nothing) Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), ntime_pdfs) 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 = + 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, Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), n_ion_species, From 0905d653f99da78f1d151e755ea8003b4a6e6e94 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 24 Nov 2023 17:16:42 +0000 Subject: [PATCH 271/331] Updated expected output for fokker_planck_time_evolution_tests.jl Previous output was affected by a default `vperp_bc = "periodic"` which had unintended effects in `reconcile_element_boundaries_centered!()`, which is used by `derivative!()`, etc. --- test/fokker_planck_time_evolution_tests.jl | 60 +++++++++++++--------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/test/fokker_planck_time_evolution_tests.jl b/test/fokker_planck_time_evolution_tests.jl index 6ce83ef66..a022170cb 100644 --- a/test/fokker_planck_time_evolution_tests.jl +++ b/test/fokker_planck_time_evolution_tests.jl @@ -36,39 +36,51 @@ end const expected = expected_data( - vec([-3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 ]), - vec([0.155051025721682 0.644948974278318 1.000000000000000 1.500000000000000 2.000000000000000 2.500000000000000 3.000000000000000 ]), + [-3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0], + [0.155051025721682, 0.644948974278318, 1.000000000000000, 1.500000000000000, 2.000000000000000, 2.500000000000000, 3.000000000000000], # Expected phi: - vec([-1.267505494648937 -1.275240686416660 ]), + [-1.267505494648937, -1.275683298550937], # Expected n_charged: - vec([0.281533032234007 0.279363721076024 ]), + [0.2815330322340072, 0.2792400986636072], # Expected upar_charged: - vec([0.0 0.0 ]), + [0.0, 0.0], # Expected ppar_charged: - vec([0.179822802480489 0.147624463307870 ]), + [0.17982280248048935, 0.14891126175332367], # Expected pperp_charged - vec([0.143401466675068 0.158821758223731 ]), + [0.14340146667506784, 0.1581377822859991], # Expected qpar_charged - vec([0.0 0.0 ]), + [0.0, 0.0], # Expected v_t_charged - vec([1.051172608301042 1.053709630977256 ]), + [1.0511726083010418, 1.0538509291794658], # Expected dSdt - vec([0.0 0.000008786074305 ]), + [0.0, 1.1853081348031516e-5], # Expected f_charged: - [ 0.000000000000000 ; 0.000619960016181 ; 0.005882016862627 ; 0.033848669972256 ; 0.118138103172003 ; 0.229579465209362 ; 0.000000000000000 ; 0.229579465209362 ; 0.118138103172003 ; 0.033848669972256 ; 0.005882016862627 ; 0.000619960016181 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000478053009971 ; 0.004535640674379 ; 0.026100809957764 ; 0.091096642266611 ; 0.177029407552679 ; 0.000000000000000 ; 0.177029407552679 ; 0.091096642266611 ; 0.026100809957764 ; 0.004535640674379 ; 0.000478053009971 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000266581711212 ; 0.002529256854782 ; 0.014554868262370 ; 0.050799175561231 ; 0.098718764270694 ; 0.000000000000000 ; 0.098718764270694 ; 0.050799175561231 ; 0.014554868262370 ; 0.002529256854782 ; 0.000266581711212 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000076376939017 ; 0.000724644221386 ; 0.004170039574837 ; 0.014554207474836 ; 0.028283399503664 ; 0.000000000000000 ; 0.028283399503664 ; 0.014554207474836 ; 0.004170039574837 ; 0.000724644221386 ; 0.000076376939017 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000013272321882 ; 0.000125924283949 ; 0.000724644221264 ; 0.002529142026698 ; 0.004914917865936 ; 0.000000000000000 ; 0.004914917865936 ; 0.002529142026698 ; 0.000724644221264 ; 0.000125924283949 ; 0.000013272321882 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000001398892434 ; 0.000013272321882 ; 0.000076376939004 ; 0.000266569608421 ; 0.000518028531855 ; 0.000000000000000 ; 0.000518028531855 ; 0.000266569608421 ; 0.000076376939004 ; 0.000013272321882 ; 0.000001398892434 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ;;; - 0.000000000000000 ; 0.000138780288865 ; 0.005791061761509 ; 0.027815242205125 ; 0.081445074775077 ; 0.152354938126957 ; 0.186355844666422 ; 0.152354938126957 ; 0.081445074775077 ; 0.027815242205125 ; 0.005791061761509 ; 0.000138780288865 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000041802336103 ; 0.004577414398302 ; 0.022242056573360 ; 0.065733717950559 ; 0.123790379285355 ; 0.151688746487743 ; 0.123790379285355 ; 0.065733717950559 ; 0.022242056573360 ; 0.004577414398302 ; 0.000041802336103 ; 0.000000000000000 ;; - 0.000000000000000 ; -0.000102715219225 ; 0.002768824456286 ; 0.013936837294540 ; 0.042320490360991 ; 0.081223176170832 ; 0.100027475688989 ; 0.081223176170832 ; 0.042320490360991 ; 0.013936837294540 ; 0.002768824456286 ; -0.000102715219225 ; 0.000000000000000 ;; - 0.000000000000000 ; -0.000164682482058 ; 0.000767345547797 ; 0.004535511738255 ; 0.014877992136829 ; 0.029791500386470 ; 0.037097641503996 ; 0.029791500386470 ; 0.014877992136829 ; 0.004535511738255 ; 0.000767345547797 ; -0.000164682482058 ; 0.000000000000000 ;; - 0.000000000000000 ; -0.000091767217558 ; 0.000022353567868 ; 0.000693446611185 ; 0.002889786006700 ; 0.006284514040367 ; 0.007979739613849 ; 0.006284514040367 ; 0.002889786006700 ; 0.000693446611185 ; 0.000022353567868 ; -0.000091767217558 ; 0.000000000000000 ;; - 0.000000000000000 ; -0.000032024960906 ; -0.000089243114443 ; -0.000201682249005 ; -0.000175449913808 ; 0.000111463339173 ; 0.000290773816557 ; 0.000111463339173 ; -0.000175449913808 ; -0.000201682249005 ; -0.000089243114443 ; -0.000032024960906 ; 0.000000000000000 ;; - 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ; 0.000000000000000 ]) + [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; + 0.03384866997225574 0.026100809957763767 0.01455486826237011 0.004170039574837177 0.000724644221263874 7.637693900444835e-5 0.0; + 0.11813810317200342 0.09109664226661075 0.05079917556123135 0.01455420747483572 0.0025291420266981487 0.0002665696084208068 0.0; + 0.22957946520936198 0.17702940755267918 0.0987187642706944 0.0282833995036643 0.004914917865936108 0.0005180285318549189 0.0; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0; + 0.22957946520936204 0.1770294075526792 0.0987187642706944 0.0282833995036643 0.004914917865936108 0.0005180285318549189 0.0; + 0.11813810317200349 0.0910966422666108 0.050799175561231376 0.01455420747483573 0.0025291420266981487 0.0002665696084208068 0.0; + 0.03384866997225574 0.026100809957763767 0.01455486826237011 0.004170039574837177 0.000724644221263874 7.637693900444835e-5 0.0; + 0.005882016862626724 0.0045356406743786385 0.002529256854781707 0.0007246442213864763 0.00012592428394890537 1.3272321881722645e-5 0.0; + 0.0006199600161806666 0.00047805300997075977 0.0002665817112117718 7.637693901737056e-5 1.3272321881722645e-5 1.3988924344690309e-6 0.0; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0;;; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0; + 0.0001712743622973216 7.105465094508053e-5 -7.829380680167827e-5 -0.00015364081956318698 -9.097098213067502e-5 -3.311284120491419e-5 0.0; + 0.005883280697248667 0.004667594200766182 0.002855965521103658 0.0008138347136178689 2.44260649525292e-5 -9.753249634264602e-5 0.0; + 0.02792209301450194 0.022385716644538384 0.01413535091105969 0.004677801530322722 0.0007105315221401102 -0.00022400635166536323 0.0; + 0.08117458037332098 0.06563459159004267 0.04247673844050208 0.015087784332275832 0.0029056314178876035 -0.00023019804543218203 0.0; + 0.15133793170654106 0.12313903060106579 0.08111673445361306 0.029975277983613262 0.00626735398468981 7.553501812465833e-6 0.0; + 0.18493902160817713 0.15073513412904313 0.09976414473955808 0.037251581926306565 0.007941836186495122 0.00016196175024033304 0.0; + 0.15133793170654092 0.12313903060106571 0.08111673445361306 0.02997527798361324 0.006267353984689816 7.553501812469816e-6 0.0; + 0.081174580373321 0.06563459159004267 0.042476738440502065 0.015087784332275821 0.002905631417887614 -0.0002301980454321778 0.0; + 0.027922093014501933 0.022385716644538384 0.014135350911059698 0.004677801530322729 0.0007105315221401184 -0.00022400635166536134 0.0; + 0.005883280697248667 0.004667594200766184 0.002855965521103663 0.0008138347136178759 2.4426064952530956e-5 -9.753249634264635e-5 0.0; + 0.0001712743622973275 7.105465094508572e-5 -7.829380680167411e-5 -0.00015364081956318568 -9.097098213067551e-5 -3.311284120491447e-5 0.0; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0]) ########################################################################################### # to modify the test, with a new expected f, print the new f using the following commands # in an interative Julia REPL. The path is the path to the .dfns file. From 67a4e3cbbebde69a495ed0a1f849a557211ed700 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 15:50:13 +0000 Subject: [PATCH 272/331] Add automated debug checks for Fokker-Planck collision operator --- debug_test/fokker_planck_collisions_inputs.jl | 67 +++++++++++++++++++ debug_test/fokker_planck_collisions_tests.jl | 23 +++++++ debug_test/runtests.jl | 1 + 3 files changed, 91 insertions(+) create mode 100644 debug_test/fokker_planck_collisions_inputs.jl create mode 100644 debug_test/fokker_planck_collisions_tests.jl diff --git a/debug_test/fokker_planck_collisions_inputs.jl b/debug_test/fokker_planck_collisions_inputs.jl new file mode 100644 index 000000000..9f673ebf1 --- /dev/null +++ b/debug_test/fokker_planck_collisions_inputs.jl @@ -0,0 +1,67 @@ +test_type = "Fokker-Planck collisions" + +# default input for test +test_input_full_f = Dict( + "run_name" => "full_f", + "dt" => 0.0, + "nstep" => 3, + "nwrite" => 2, + "nwrite_dfns" => 2, + "Bmag" => 1.0, + "Bzed" => 1.0, + "T_e" => 1.0, + "T_wall" => 1.0, + "electron_physics" => "boltzmann_electron_response", + "evolve_moments_conservation" => false, + "evolve_moments_density" => false, + "evolve_moments_parallel_flow" => false, + "evolve_moments_parallel_pressure" => false, + "initial_density1" => 0.5, + "initial_density2" => 0.5, + "initial_temperature1" => 1.0, + "initial_temperature2" => 1.0, + "charge_exchange_frequency" => 0.0, + "ionization_frequency" => 0.0, + "constant_ionization_rate" => false, + "n_ion_species" => 1, + "n_neutral_species" => 0, + "n_rk_stages" => 4, + "nuii" => 1.0, + "r_bc" => "periodic", + "r_discretization" => "chebyshev_pseudospectral", + "r_nelement" => 2, + "r_ngrid" => 3, + "rhostar" => 1.0, + "split_operators" => false, + "vpa_L" => 6.0, + "vpa_bc" => "zero", + "vpa_discretization" => "gausslegendre_pseudospectral", + "vpa_nelement" => 2, + "vpa_ngrid" => 3, + "vperp_L" => 3.0, + "vperp_discretization" => "gausslegendre_pseudospectral", + "vperp_nelement" => 2, + "vperp_ngrid" => 3, + "z_IC_density_amplitude1" => 0.001, + "z_IC_density_amplitude2" => 0.001, + "z_IC_density_phase1" => 0.0, + "z_IC_density_phase2" => 0.0, + "z_IC_option1" => "sinusoid", + "z_IC_option2" => "sinusoid", + "z_IC_temperature_amplitude1" => 0.0, + "z_IC_temperature_amplitude2" => 0.0, + "z_IC_temperature_phase1" => 0.0, + "z_IC_temperature_phase2" => 0.0, + "z_IC_upar_amplitude1" => 0.0, + "z_IC_upar_amplitude2" => 0.0, + "z_IC_upar_phase1" => 0.0, + "z_IC_upar_phase2" => 0.0, + "z_bc" => "wall", + "z_discretization" => "chebyshev_pseudospectral", + "z_nelement" => 2, + "z_ngrid" => 3, + ) + +test_input_list = [ + test_input_full_f , + ] diff --git a/debug_test/fokker_planck_collisions_tests.jl b/debug_test/fokker_planck_collisions_tests.jl new file mode 100644 index 000000000..502e987f8 --- /dev/null +++ b/debug_test/fokker_planck_collisions_tests.jl @@ -0,0 +1,23 @@ +module FokkerPlanckCollisionsDebug + +# Debug test using wall boundary conditions. + +include("setup.jl") + +# Create a temporary directory for test output +test_output_directory = get_MPI_tempdir() +mkpath(test_output_directory) + + +# Input parameters for the test +include("fokker_planck_collisions_inputs.jl") + +# Defines the test functions, using variables defined in the *_inputs.jl file +include("runtest_template.jl") + +end # FokkerPlanckCollisionsDebug + + +using .FokkerPlanckCollisionsDebug + +FokkerPlanckCollisionsDebug.runtests() diff --git a/debug_test/runtests.jl b/debug_test/runtests.jl index 85b7bb380..55550595e 100644 --- a/debug_test/runtests.jl +++ b/debug_test/runtests.jl @@ -5,6 +5,7 @@ include("setup.jl") function runtests() @testset "moment_kinetics tests" begin include(joinpath(@__DIR__, "sound_wave_tests.jl")) + include(joinpath(@__DIR__, "fokker_planck_collisions_tests.jl")) include(joinpath(@__DIR__, "wall_bc_tests.jl")) include(joinpath(@__DIR__, "harrisonthompson.jl")) include(joinpath(@__DIR__, "mms_tests.jl")) From bb587f850c06cb4508759fe94f139d7da2373d9a Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 16:06:27 +0000 Subject: [PATCH 273/331] Only print warning about vperp boundary conditions on rank-0 process --- src/moment_kinetics_input.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index 0b0c56e4c..d1ce6590c 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -188,7 +188,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) collisions.nuii = get(scan_input, "nuii", 0.0) collisions.weakform_fokker_planck = get(scan_input, "weakform_fokker_planck", true) - if !collisions.weakform_fokker_planck + if !collisions.weakform_fokker_planck && global_rank[] == 0 println("WARNING: you have used weakform_fokker_planck = false") println("WARNING: you have selected a depreciated version of the ion-ion self collision operator") end @@ -1109,7 +1109,7 @@ function check_coordinate_input(coord, coord_name, io) coord.nelement_global, " elements across the $coord_name domain [", 0.0, ",", coord.L, "].") - if coord.bc != "zero" && coord.n_global > 1 + if coord.bc != "zero" && coord.n_global > 1 && global_rank[] == 0 println("WARNING: regularity condition (df/dvperp=0 at vperp=0) not being " * "imposed. Collisions or vperp-diffusion will be unstable.") end From 4bb811e6a53d5b469f5653a7196008842903d09f Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 16:32:26 +0000 Subject: [PATCH 274/331] Comment out print of MM2D_sparse Only useful for debugging. --- src/fokker_planck_calculus.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 3f8884d1c..4f71c2478 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1758,14 +1758,14 @@ function assemble_matrix_operators_dirichlet_bc_sparse(vpa,vperp,vpa_spectral,vp if global_rank[] == 0 && print_to_screen println("finished elliptic operator constructor assignment ", Dates.format(now(), dateformat"H:MM:SS")) end - if nc_global < 60 - println("MM2D_sparse \n",MM2D_sparse) - print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) + #if nc_global < 60 + # println("MM2D_sparse \n",MM2D_sparse) + # print_matrix(Array(MM2D_sparse),"MM2D_sparse",nc_global,nc_global) # print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) # print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) # print_matrix(LP2D,"LP",nc_global,nc_global) # print_matrix(LV2D,"LV",nc_global,nc_global) - end + #end end return MM2D_sparse, KKpar2D_sparse, KKperp2D_sparse, KKpar2D_with_BC_terms_sparse, KKperp2D_with_BC_terms_sparse, From cc3e88076ebd7cfa6d19c245c6987bff98a4283b Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 19:38:29 +0000 Subject: [PATCH 275/331] Fix debug_setup_loop_ranges_split_one_combination() This debug function is meant to emulate `get_best_ranges()`, but only actually splitting one region type. Previously, it treated all the other region types as serial regions, but this means that only the rank-0 process in each shared-memory block actually enters any `@loop_*` macro. However, in `get_best_ranges()` it is only the parallelised dimensions that get an empty range (of `1:0`) on processes that have no work to do - for the other (non-parallelised) dimensions every process should loop over all the points. Treating other region types as serial regions meant that some code that was correct in non-debug mode failed in debug mode. This commit fixes this problem, by making every process loop over all points in the non-parallelised dimensions. --- src/looping.jl | 12 +++++- test/loop_setup_tests.jl | 82 ++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/looping.jl b/src/looping.jl index 08018cdd8..d3f2a0451 100644 --- a/src/looping.jl +++ b/src/looping.jl @@ -366,9 +366,17 @@ eval(quote loop_ranges_store[dims] = LoopRanges(; parallel_dims=dims, rank0 = rank0, ranges...) else - # Use the same ranges as serial loops + # Loop over all indices for non-parallelised dimensions (dimensions not in + # `dims`), but only loop over parallel dimensions (dimensions in `dims`) on + # rank0. + this_ranges = Dict(d=>1:n for (d,n) in dim_sizes) + if !rank0 + for d ∈ dims + this_ranges[d] = 1:0 + end + end loop_ranges_store[dims] = LoopRanges(; - parallel_dims=dims, rank0 = rank0, serial_ranges...) + parallel_dims=dims, rank0 = rank0, this_ranges...) end end diff --git a/test/loop_setup_tests.jl b/test/loop_setup_tests.jl index c34ea677b..725714d96 100644 --- a/test/loop_setup_tests.jl +++ b/test/loop_setup_tests.jl @@ -197,26 +197,26 @@ function runtests() @test loop_ranges_store[()].z == 1:0 @test loop_ranges_store[(:s,)].s == 1:0 - @test loop_ranges_store[(:s,)].r == 1:0 - @test loop_ranges_store[(:s,)].z == 1:0 + @test loop_ranges_store[(:s,)].r == 1:3 + @test loop_ranges_store[(:s,)].z == 1:4 - @test loop_ranges_store[(:r,)].s == 1:0 + @test loop_ranges_store[(:r,)].s == 1:2 @test loop_ranges_store[(:r,)].r == 1:0 - @test loop_ranges_store[(:r,)].z == 1:0 + @test loop_ranges_store[(:r,)].z == 1:4 - @test loop_ranges_store[(:z,)].s == 1:0 - @test loop_ranges_store[(:z,)].r == 1:0 + @test loop_ranges_store[(:z,)].s == 1:2 + @test loop_ranges_store[(:z,)].r == 1:3 @test loop_ranges_store[(:z,)].z == 1:0 @test loop_ranges_store[(:s,:r)].s == 1:0 @test loop_ranges_store[(:s,:r)].r == 1:0 - @test loop_ranges_store[(:s,:r)].z == 1:0 + @test loop_ranges_store[(:s,:r)].z == 1:4 @test loop_ranges_store[(:s,:z)].s == 2:2 @test loop_ranges_store[(:s,:z)].r == 1:3 @test loop_ranges_store[(:s,:z)].z == 1:4 - @test loop_ranges_store[(:r,:z)].s == 1:0 + @test loop_ranges_store[(:r,:z)].s == 1:2 @test loop_ranges_store[(:r,:z)].r == 1:0 @test loop_ranges_store[(:r,:z)].z == 1:0 @@ -269,23 +269,23 @@ function runtests() @test loop_ranges_store[()].z == 1:0 @test loop_ranges_store[(:s,)].s == 1:0 - @test loop_ranges_store[(:s,)].r == 1:0 - @test loop_ranges_store[(:s,)].z == 1:0 + @test loop_ranges_store[(:s,)].r == 1:3 + @test loop_ranges_store[(:s,)].z == 1:4 - @test loop_ranges_store[(:r,)].s == 1:0 + @test loop_ranges_store[(:r,)].s == 1:2 @test loop_ranges_store[(:r,)].r == 1:0 - @test loop_ranges_store[(:r,)].z == 1:0 + @test loop_ranges_store[(:r,)].z == 1:4 - @test loop_ranges_store[(:z,)].s == 1:0 - @test loop_ranges_store[(:z,)].r == 1:0 + @test loop_ranges_store[(:z,)].s == 1:2 + @test loop_ranges_store[(:z,)].r == 1:3 @test loop_ranges_store[(:z,)].z == 1:0 @test loop_ranges_store[(:s,:r)].s == 1:0 @test loop_ranges_store[(:s,:r)].r == 1:0 - @test loop_ranges_store[(:s,:r)].z == 1:0 + @test loop_ranges_store[(:s,:r)].z == 1:4 @test loop_ranges_store[(:s,:z)].s == 1:0 - @test loop_ranges_store[(:s,:z)].r == 1:0 + @test loop_ranges_store[(:s,:z)].r == 1:3 @test loop_ranges_store[(:s,:z)].z == 1:0 @test loop_ranges_store[(:r,:z)].s == 1:2 @@ -305,23 +305,23 @@ function runtests() @test loop_ranges_store[()].z == 1:0 @test loop_ranges_store[(:s,)].s == 1:0 - @test loop_ranges_store[(:s,)].r == 1:0 - @test loop_ranges_store[(:s,)].z == 1:0 + @test loop_ranges_store[(:s,)].r == 1:3 + @test loop_ranges_store[(:s,)].z == 1:4 - @test loop_ranges_store[(:r,)].s == 1:0 + @test loop_ranges_store[(:r,)].s == 1:2 @test loop_ranges_store[(:r,)].r == 1:0 - @test loop_ranges_store[(:r,)].z == 1:0 + @test loop_ranges_store[(:r,)].z == 1:4 - @test loop_ranges_store[(:z,)].s == 1:0 - @test loop_ranges_store[(:z,)].r == 1:0 + @test loop_ranges_store[(:z,)].s == 1:2 + @test loop_ranges_store[(:z,)].r == 1:3 @test loop_ranges_store[(:z,)].z == 1:0 @test loop_ranges_store[(:s,:r)].s == 1:0 @test loop_ranges_store[(:s,:r)].r == 1:0 - @test loop_ranges_store[(:s,:r)].z == 1:0 + @test loop_ranges_store[(:s,:r)].z == 1:4 @test loop_ranges_store[(:s,:z)].s == 1:0 - @test loop_ranges_store[(:s,:z)].r == 1:0 + @test loop_ranges_store[(:s,:z)].r == 1:3 @test loop_ranges_store[(:s,:z)].z == 1:0 @test loop_ranges_store[(:r,:z)].s == 1:2 @@ -341,23 +341,23 @@ function runtests() @test loop_ranges_store[()].z == 1:0 @test loop_ranges_store[(:s,)].s == 1:0 - @test loop_ranges_store[(:s,)].r == 1:0 - @test loop_ranges_store[(:s,)].z == 1:0 + @test loop_ranges_store[(:s,)].r == 1:3 + @test loop_ranges_store[(:s,)].z == 1:4 - @test loop_ranges_store[(:r,)].s == 1:0 + @test loop_ranges_store[(:r,)].s == 1:2 @test loop_ranges_store[(:r,)].r == 1:0 - @test loop_ranges_store[(:r,)].z == 1:0 + @test loop_ranges_store[(:r,)].z == 1:4 - @test loop_ranges_store[(:z,)].s == 1:0 - @test loop_ranges_store[(:z,)].r == 1:0 + @test loop_ranges_store[(:z,)].s == 1:2 + @test loop_ranges_store[(:z,)].r == 1:3 @test loop_ranges_store[(:z,)].z == 1:0 @test loop_ranges_store[(:s,:r)].s == 1:0 @test loop_ranges_store[(:s,:r)].r == 1:0 - @test loop_ranges_store[(:s,:r)].z == 1:0 + @test loop_ranges_store[(:s,:r)].z == 1:4 @test loop_ranges_store[(:s,:z)].s == 1:0 - @test loop_ranges_store[(:s,:z)].r == 1:0 + @test loop_ranges_store[(:s,:z)].r == 1:3 @test loop_ranges_store[(:s,:z)].z == 1:0 @test loop_ranges_store[(:r,:z)].s == 1:2 @@ -377,26 +377,26 @@ function runtests() @test loop_ranges_store[()].z == 1:0 @test loop_ranges_store[(:s,)].s == 1:0 - @test loop_ranges_store[(:s,)].r == 1:0 - @test loop_ranges_store[(:s,)].z == 1:0 + @test loop_ranges_store[(:s,)].r == 1:3 + @test loop_ranges_store[(:s,)].z == 1:4 - @test loop_ranges_store[(:r,)].s == 1:0 + @test loop_ranges_store[(:r,)].s == 1:2 @test loop_ranges_store[(:r,)].r == 1:0 - @test loop_ranges_store[(:r,)].z == 1:0 + @test loop_ranges_store[(:r,)].z == 1:4 - @test loop_ranges_store[(:z,)].s == 1:0 - @test loop_ranges_store[(:z,)].r == 1:0 + @test loop_ranges_store[(:z,)].s == 1:2 + @test loop_ranges_store[(:z,)].r == 1:3 @test loop_ranges_store[(:z,)].z == 1:0 @test loop_ranges_store[(:s,:r)].s == 1:0 @test loop_ranges_store[(:s,:r)].r == 1:0 - @test loop_ranges_store[(:s,:r)].z == 1:0 + @test loop_ranges_store[(:s,:r)].z == 1:4 @test loop_ranges_store[(:s,:z)].s == 1:0 - @test loop_ranges_store[(:s,:z)].r == 1:0 + @test loop_ranges_store[(:s,:z)].r == 1:3 @test loop_ranges_store[(:s,:z)].z == 1:0 - @test loop_ranges_store[(:r,:z)].s == 1:0 + @test loop_ranges_store[(:r,:z)].s == 1:2 @test loop_ranges_store[(:r,:z)].r == 1:0 @test loop_ranges_store[(:r,:z)].z == 1:0 From e16e04ca8c513308ce99bcc203dd113830418b9f Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 20:26:15 +0000 Subject: [PATCH 276/331] Zero-initialise the dSdt array --- src/initial_conditions.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 97bc46b3b..88dbc1d70 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -216,6 +216,13 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, end end + # Zero-initialise the dSdt diagnostic to avoid writing uninitialised values, as the + # collision operator will not be calculated before the initial values are written to + # file. + @serial_region begin + moments.charged.dSdt .= 0.0 + end + init_boundary_distributions!(boundary_distributions, pdf, vz, vr, vzeta, vpa, vperp, z, r, composition) From 85cfee604bbf89b3dd13c6a18461704fd9ffb51d Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 25 Nov 2023 21:34:41 +0000 Subject: [PATCH 277/331] Ensure loop type returns to vperp_vpa at outer level ...in explicit_fokker_planck_collisions_weak_form!(). Apparently it is necessary to have looping.loop_ranges in the correct state when wrapping around the end of the @loop_s_r_z. --- src/fokker_planck.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 51dcfcafb..b75ecac5a 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -250,6 +250,7 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos @serial_region begin dSdt[iz,ir,is] = -get_density(lnfC,vpa,vperp) end + begin_vperp_vpa_region() end end return nothing From 4a59d86c4b455138ddebc36e16fe29ea8eae6da1 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 26 Nov 2023 10:56:10 +0000 Subject: [PATCH 278/331] Reduce number of spatial grid points in FP collision operator debug test The Fokker-Planck collisions do not do any spatially-dependent operations, so having just nelement=1, ngrid=2 in the r- and z-diretions should be enough to check for shared-memory errors. Reducing the number of spatial grid points from 5*5=25 to 2*2=4 massively speeds up the debug test, making it more practical to run (especially on the CI servers). --- debug_test/fokker_planck_collisions_inputs.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debug_test/fokker_planck_collisions_inputs.jl b/debug_test/fokker_planck_collisions_inputs.jl index 9f673ebf1..819525b7a 100644 --- a/debug_test/fokker_planck_collisions_inputs.jl +++ b/debug_test/fokker_planck_collisions_inputs.jl @@ -29,8 +29,8 @@ test_input_full_f = Dict( "nuii" => 1.0, "r_bc" => "periodic", "r_discretization" => "chebyshev_pseudospectral", - "r_nelement" => 2, - "r_ngrid" => 3, + "r_nelement" => 1, + "r_ngrid" => 2, "rhostar" => 1.0, "split_operators" => false, "vpa_L" => 6.0, @@ -58,8 +58,8 @@ test_input_full_f = Dict( "z_IC_upar_phase2" => 0.0, "z_bc" => "wall", "z_discretization" => "chebyshev_pseudospectral", - "z_nelement" => 2, - "z_ngrid" => 3, + "z_nelement" => 1, + "z_ngrid" => 2, ) test_input_list = [ From 86df79b83edfa05ad5a7057088eab486445de55f Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 26 Nov 2023 11:03:02 +0000 Subject: [PATCH 279/331] Skip neutral dimensions in debug checks if n_neutral_species==0 If no neutral speies is present, then the debug checks for combinations of neutral dimensions (anything including `:sn`) would be no different to running in serial, so they can be safely skipped to save time. --- debug_test/runtest_template.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debug_test/runtest_template.jl b/debug_test/runtest_template.jl index c5245eaa7..7d7928e3a 100644 --- a/debug_test/runtest_template.jl +++ b/debug_test/runtest_template.jl @@ -58,6 +58,12 @@ function runtests(; restart=false) n_factors = length(factor(Vector, global_size[])) for input ∈ test_input_list, debug_loop_type ∈ dimension_combinations_to_test + if :sn ∈ debug_loop_type && "n_neutral_species" ∈ keys(input) && + input["n_neutral_species"] <= 0 + # Skip neutral dimension parallelisation options if the number of neutral + # species is zero, as these would just be equivalent to running in serial + continue + end ndims = length(debug_loop_type) for i ∈ 1:(ndims+n_factors-1)÷n_factors debug_loop_parallel_dims = From c1e6c9ab01dc1e56581c0d2b66f4affcb11e95e9 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 26 Nov 2023 11:06:25 +0000 Subject: [PATCH 280/331] Increase timeout for debug checks in CI Github.com's Ubuntu CI servers seem to be slower than they used to be, and are making the debug checks job time out. --- .github/workflows/debug_checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/debug_checks.yml b/.github/workflows/debug_checks.yml index 9f21bc82f..cbbc28ed5 100644 --- a/.github/workflows/debug_checks.yml +++ b/.github/workflows/debug_checks.yml @@ -11,7 +11,7 @@ jobs: # Only run on linux to save CI server cpu-hours os: [ubuntu-latest] fail-fast: false - timeout-minutes: 240 + timeout-minutes: 360 steps: - uses: actions/checkout@v4 From fb3be3abe12cb871bb97dcc0230b59c620c7aa27 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 26 Nov 2023 14:10:43 +0000 Subject: [PATCH 281/331] Don't allocate arrays for collision operator when not using collisions --- src/time_advance.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time_advance.jl b/src/time_advance.jl index b5ee90ec1..ea4b20cd2 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -223,7 +223,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, elseif advance.explicit_weakform_fp_collisions fp_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true) else - fp_arrays = init_fokker_planck_collisions(vpa,vperp; precompute_weights=false) + fp_arrays = nothing end # create the "fields" structure that contains arrays # for the electrostatic potential phi and eventually the electromagnetic fields From b1d73e5ae5279771392c86691614fb96eba3ef20 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 26 Nov 2023 17:02:06 +0000 Subject: [PATCH 282/331] Increase r_ngrid and z_ngrid to 3 in debug check for collisions ngrid=2 results in an out-of-bounds index error in Chebyshev derivatives. --- debug_test/fokker_planck_collisions_inputs.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_test/fokker_planck_collisions_inputs.jl b/debug_test/fokker_planck_collisions_inputs.jl index 819525b7a..5f6a8d559 100644 --- a/debug_test/fokker_planck_collisions_inputs.jl +++ b/debug_test/fokker_planck_collisions_inputs.jl @@ -30,7 +30,7 @@ test_input_full_f = Dict( "r_bc" => "periodic", "r_discretization" => "chebyshev_pseudospectral", "r_nelement" => 1, - "r_ngrid" => 2, + "r_ngrid" => 3, "rhostar" => 1.0, "split_operators" => false, "vpa_L" => 6.0, @@ -59,7 +59,7 @@ test_input_full_f = Dict( "z_bc" => "wall", "z_discretization" => "chebyshev_pseudospectral", "z_nelement" => 1, - "z_ngrid" => 2, + "z_ngrid" => 3, ) test_input_list = [ From 59c2a8fa27cabde9f51006bcdd9a9817a75024db Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 27 Nov 2023 15:48:48 +0000 Subject: [PATCH 283/331] Fix MMS test for Krook collisions The final collision frequency used must be `nuii_krook`, not `nu_krook` (which does not have the factors for spatially-varying density and temperature included). There was also a merge error which meant that the MMS source term associated with the Krook collisions was calculated and added twice. --- src/manufactured_solns.jl | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index 71eec2af5..a3050a4b3 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -526,7 +526,7 @@ using IfElse pvth = 1 end FMaxwellian = (densi/vthi^pvth)*exp( -( ( vpa-upari)^2 + vperp^2 )/vthi^2) - Si += - nu_krook*(FMaxwellian - dfni) + 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 @@ -541,23 +541,6 @@ using IfElse if num_diss_params.z_dissipation_coefficient > 0.0 && include_num_diss_in_MMS Si += - num_diss_params.z_dissipation_coefficient*Dz(Dz(dfni)) end - nu_krook = collisions.krook_collision_frequency_prefactor - if nu_krook > 0.0 - Ti_over_Tref = vthi^2 - if collisions.krook_collisions_option == "manual" - nuii_krook = nu_krook - else # default option - nuii_krook = nu_krook * densi * Ti_over_Tref^(-1.5) - end - if vperp_coord.n > 1 - pvth = 3 - else - pvth = 1 - end - FMaxwellian = (densi/vthi^pvth)*exp( -( ( vpa-upari)^2 + vperp^2 )/vthi^2) - Si += -nuii_krook*(FMaxwellian - dfni) - end - Source_i = expand_derivatives(Si) From 4eb57e224be29e4849965d875d062ba9792dfa1a Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 27 Nov 2023 18:03:48 +0000 Subject: [PATCH 284/331] Added function for computing the Rosenbluth potentials by direct integration with the Green's functions, and a test in the test/fokker_planck_tests.jl module. This feature ensures that the direct integration method for the Rosenbluth potentials is preserved outside of the 'strong form' implementation of the collision operator, which can now be removed. --- src/fokker_planck.jl | 87 +++++++++++++------------- src/fokker_planck_calculus.jl | 85 +++++++++++++++++++++++++- test/fokker_planck_tests.jl | 111 ++++++++++++++++++++++++++++++++-- 3 files changed, 233 insertions(+), 50 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index b75ecac5a..8903a6a1e 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -70,48 +70,6 @@ using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian # begin functions associated with the weak-form operator ######################################################## -""" -allocate the required ancilliary arrays -""" - -function allocate_fokkerplanck_arrays(vperp,vpa) - nvpa = vpa.n - nvperp = vperp.n - - G0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - #Rosenbluth_G = allocate_float(nvpa,nvperp) - d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) - d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) - d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) - dGdvperp = allocate_shared_float(nvpa,nvperp) - #Rosenbluth_H = allocate_float(nvpa,nvperp) - dHdvpa = allocate_shared_float(nvpa,nvperp) - dHdvperp = allocate_shared_float(nvpa,nvperp) - #Cflux_vpa = allocate_shared_float(nvpa,nvperp) - #Cflux_vperp = allocate_shared_float(nvpa,nvperp) - buffer_vpavperp_1 = allocate_float(nvpa,nvperp) - buffer_vpavperp_2 = allocate_float(nvpa,nvperp) - Cssp_result_vpavperp = allocate_shared_float(nvpa,nvperp) - dfdvpa = allocate_shared_float(nvpa,nvperp) - d2fdvpa2 = allocate_shared_float(nvpa,nvperp) - d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) - dfdvperp = allocate_shared_float(nvpa,nvperp) - d2fdvperp2 = allocate_shared_float(nvpa,nvperp) - - return fokkerplanck_arrays_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, - dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, - Cssp_result_vpavperp, dfdvpa, d2fdvpa2, - d2fdvperpdvpa, dfdvperp, d2fdvperp2) -end - - - """ function that initialises the arrays needed for Fokker Planck collisions using numerical integration to compute the Rosenbluth potentials only @@ -462,15 +420,56 @@ end # begin functions associated with the strong-form operator ########################################################## + +""" +allocate the required ancilliary arrays +""" + +function allocate_fokkerplanck_arrays(vperp,vpa) + nvpa = vpa.n + nvperp = vperp.n + + G0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H0_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) + #Rosenbluth_G = allocate_float(nvpa,nvperp) + d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) + dGdvperp = allocate_shared_float(nvpa,nvperp) + #Rosenbluth_H = allocate_float(nvpa,nvperp) + dHdvpa = allocate_shared_float(nvpa,nvperp) + dHdvperp = allocate_shared_float(nvpa,nvperp) + #Cflux_vpa = allocate_shared_float(nvpa,nvperp) + #Cflux_vperp = allocate_shared_float(nvpa,nvperp) + buffer_vpavperp_1 = allocate_float(nvpa,nvperp) + buffer_vpavperp_2 = allocate_float(nvpa,nvperp) + Cssp_result_vpavperp = allocate_shared_float(nvpa,nvperp) + dfdvpa = allocate_shared_float(nvpa,nvperp) + d2fdvpa2 = allocate_shared_float(nvpa,nvperp) + d2fdvperpdvpa = allocate_shared_float(nvpa,nvperp) + dfdvperp = allocate_shared_float(nvpa,nvperp) + d2fdvperp2 = allocate_shared_float(nvpa,nvperp) + + return fokkerplanck_arrays_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, + dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, + Cssp_result_vpavperp, dfdvpa, d2fdvpa2, + d2fdvperpdvpa, dfdvperp, d2fdvperp2) +end + """ function that initialises the arrays needed for Fokker Planck collisions """ -function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false) +function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false, print_to_screen=false) fka = allocate_fokkerplanck_arrays(vperp,vpa) if vperp.n > 1 && precompute_weights @views init_Rosenbluth_potential_integration_weights!(fka.G0_weights, fka.G1_weights, fka.H0_weights, fka.H1_weights, - fka.H2_weights, fka.H3_weights, vperp, vpa) + fka.H2_weights, fka.H3_weights, vperp, vpa, print_to_screen=print_to_screen) end return fka end diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 4f71c2478..ed429d810 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -247,7 +247,7 @@ function that precomputes the required integration weights """ function init_Rosenbluth_potential_integration_weights!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights,vperp,vpa;print_to_screen=true) - x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp) + x_vpa, w_vpa, x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre = setup_basic_quadratures(vpa,vperp,print_to_screen=print_to_screen) @serial_region begin if global_rank[] == 0 && print_to_screen @@ -2079,7 +2079,7 @@ end function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, - vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays; + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays::fokkerplanck_weakform_arrays_struct; algebraic_solve_for_d2Gdvperp2=false,calculate_GG=false,calculate_dGdvperp=false) # extract the necessary precalculated and buffer arrays from fokkerplanck_arrays @@ -2168,6 +2168,87 @@ function calculate_rosenbluth_potentials_via_elliptic_solve!(GG,HH,dHdvpa,dHdvpe return nothing end +""" +function to calculate Rosenbluth potentials by direct integration +""" + +function calculate_rosenbluth_potentials_via_direct_integration!(GG,HH,dHdvpa,dHdvperp, + d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays::fokkerplanck_arrays_struct) + dfdvpa = fkpl_arrays.dfdvpa + dfdvperp = fkpl_arrays.dfdvperp + d2fdvperpdvpa = fkpl_arrays.d2fdvperpdvpa + G0_weights = fkpl_arrays.G0_weights + G1_weights = fkpl_arrays.G1_weights + H0_weights = fkpl_arrays.H0_weights + H1_weights = fkpl_arrays.H1_weights + H2_weights = fkpl_arrays.H2_weights + H3_weights = fkpl_arrays.H3_weights + # first compute the derivatives of fs' (the integration weights assume d fs' dvpa and d fs' dvperp are known) + begin_vperp_region() + @loop_vperp ivperp begin + @views derivative!(vpa.scratch, ffsp_in[:,ivperp], vpa, vpa_spectral) + @. dfdvpa[:,ivperp] = vpa.scratch + end + begin_vpa_region() + @loop_vpa ivpa begin + @views derivative!(vperp.scratch, ffsp_in[ivpa,:], vperp, vperp_spectral) + @. dfdvperp[ivpa,:] = vperp.scratch + @views derivative!(vperp.scratch, dfdvpa[ivpa,:], vperp, vperp_spectral) + @. d2fdvperpdvpa[ivpa,:] = vperp.scratch + end + # with the integrands calculated, compute the integrals + calculate_rosenbluth_integrals!(GG,d2Gdvpa2,dGdvperp,d2Gdvperpdvpa, + d2Gdvperp2,HH,dHdvpa,dHdvperp, + ffsp_in,dfdvpa,dfdvperp,d2fdvperpdvpa, + G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + vpa.n,vperp.n) + return nothing +end + + +""" +Function to carry out the integration of the revelant +distribution functions to form the required coefficients +for the full-F operator. We assume that the weights are +precalculated. The function takes as arguments the arrays +of coefficients (which we fill), the required distributions, +the precomputed weights, the indicies of the `field' velocities, +and the sizes of the primed vpa and vperp coordinates arrays. +""" +function calculate_rosenbluth_integrals!(GG,d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, + d2Gspdvperp2,HH,dHspdvpa,dHspdvperp, + fsp,dfspdvpa,dfspdvperp,d2fspdvperpdvpa, + G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + nvpa,nvperp) + begin_vperp_vpa_region() + @loop_vperp_vpa ivperp ivpa begin + GG[ivpa,ivperp] = 0.0 + d2Gspdvpa2[ivpa,ivperp] = 0.0 + dGspdvperp[ivpa,ivperp] = 0.0 + d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 + d2Gspdvperp2[ivpa,ivperp] = 0.0 + HH[ivpa,ivperp] = 0.0 + dHspdvpa[ivpa,ivperp] = 0.0 + dHspdvperp[ivpa,ivperp] = 0.0 + for ivperpp in 1:nvperp + for ivpap in 1:nvpa + GG[ivpa,ivperp] += G0_weights[ivpap,ivperpp,ivpa,ivperp]*fsp[ivpap,ivperpp] + #d2Gspdvpa2[ivpa,ivperp] += G0_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvpa2[ivpap,ivperpp] + d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] + dGspdvperp[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperpdvpa[ivpap,ivperpp] + #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + HH[ivpa,ivperp] += H0_weights[ivpap,ivperpp,ivpa,ivperp]*fsp[ivpap,ivperpp] + dHspdvpa[ivpa,ivperp] += H0_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] + dHspdvperp[ivpa,ivperp] += H1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] + end + end + end + return nothing +end + """ function to enforce boundary conditions on the collision operator result to be consistent with the boundary conditions imposed on the the pdf diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index 5a55b7dcd..90acf5606 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -15,14 +15,14 @@ using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form, fokker_planck_collision_operator_weak_form! -using moment_kinetics.fokker_planck: conserving_corrections! +using moment_kinetics.fokker_planck: conserving_corrections!, init_fokker_planck_collisions using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian using moment_kinetics.fokker_planck_test: dHdvperp_Maxwellian, dHdvpa_Maxwellian, Cssp_Maxwellian_inputs using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_elliptic_solve!, calculate_rosenbluth_potential_boundary_data_exact! using moment_kinetics.fokker_planck_calculus: test_rosenbluth_potential_boundary_data, allocate_rosenbluth_potential_boundary_data -using moment_kinetics.fokker_planck_calculus: enforce_vpavperp_BCs! +using moment_kinetics.fokker_planck_calculus: enforce_vpavperp_BCs!, calculate_rosenbluth_potentials_via_direct_integration! function create_grids(ngrid,nelement_vpa,nelement_vperp; Lvpa=12.0,Lvperp=6.0) @@ -31,7 +31,7 @@ function create_grids(ngrid,nelement_vpa,nelement_vperp; nelement_global_vpa = nelement_local_vpa # total number of elements nelement_local_vperp = nelement_vperp # number of elements per rank nelement_global_vperp = nelement_local_vperp # total number of elements - bc = "" #not required to take a particular value, not used + bc = "zero" # used only in derivative! functions # fd_option and adv_input not actually used so given values unimportant #discretization = "chebyshev_pseudospectral" discretization = "gausslegendre_pseudospectral" @@ -145,7 +145,7 @@ function runtests() finalize_comms!() end - @testset " - test weak-form Rosenbluth potential calculation" begin + @testset " - test weak-form Rosenbluth potential calculation: elliptic solve" begin ngrid = 9 nelement_vpa = 8 nelement_vperp = 4 @@ -435,6 +435,109 @@ function runtests() finalize_comms!() end + @testset " - test weak-form Rosenbluth potential calculation: direct integration" begin + ngrid = 5 # chosen for a quick test -- direct integration is slow! + nelement_vpa = 8 + nelement_vperp = 4 + vpa, vpa_spectral, vperp, vperp_spectral = create_grids(ngrid,nelement_vpa,nelement_vperp, + Lvpa=12.0,Lvperp=6.0) + begin_serial_region() + fkpl_arrays = init_fokker_planck_collisions(vperp,vpa,precompute_weights=true,print_to_screen=print_to_screen) + dummy_array = allocate_float(vpa.n,vperp.n) + F_M = allocate_float(vpa.n,vperp.n) + H_M_exact = allocate_float(vpa.n,vperp.n) + H_M_num = allocate_shared_float(vpa.n,vperp.n) + H_M_err = allocate_float(vpa.n,vperp.n) + G_M_exact = allocate_float(vpa.n,vperp.n) + G_M_num = allocate_shared_float(vpa.n,vperp.n) + G_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvpa2_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvpa2_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvpa2_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvperp2_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvperp2_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvperp2_M_err = allocate_float(vpa.n,vperp.n) + dGdvperp_M_exact = allocate_float(vpa.n,vperp.n) + dGdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) + dGdvperp_M_err = allocate_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_exact = allocate_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) + d2Gdvperpdvpa_M_err = allocate_float(vpa.n,vperp.n) + dHdvpa_M_exact = allocate_float(vpa.n,vperp.n) + dHdvpa_M_num = allocate_shared_float(vpa.n,vperp.n) + dHdvpa_M_err = allocate_float(vpa.n,vperp.n) + dHdvperp_M_exact = allocate_float(vpa.n,vperp.n) + dHdvperp_M_num = allocate_shared_float(vpa.n,vperp.n) + dHdvperp_M_err = allocate_float(vpa.n,vperp.n) + + dens, upar, vth = 1.0, 1.0, 1.0 + begin_serial_region() + for ivperp in 1:vperp.n + for ivpa in 1:vpa.n + F_M[ivpa,ivperp] = F_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + H_M_exact[ivpa,ivperp] = H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + G_M_exact[ivpa,ivperp] = G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_M_exact[ivpa,ivperp] = d2Gdvpa2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_M_exact[ivpa,ivperp] = d2Gdvperp2_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dGdvperp_M_exact[ivpa,ivperp] = dGdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_M_exact[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvpa_M_exact[ivpa,ivperp] = dHdvpa_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + dHdvperp_M_exact[ivpa,ivperp] = dHdvperp_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) + end + end + # calculate the potentials numerically + calculate_rosenbluth_potentials_via_direct_integration!(G_M_num,H_M_num,dHdvpa_M_num,dHdvperp_M_num, + d2Gdvpa2_M_num,dGdvperp_M_num,d2Gdvperpdvpa_M_num,d2Gdvperp2_M_num,F_M, + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays) + begin_serial_region() + @serial_region begin + # test the integration + # to recalculate absolute tolerances atol, set print_to_screen = true + H_M_max, H_M_L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dHdvpa_M_max, dHdvpa_M_L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dHdvperp_M_max, dHdvperp_M_L2 = print_test_data(dHdvperp_M_exact,dHdvperp_M_num,dHdvperp_M_err,"dHdvperp_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + G_M_max, G_M_L2 = print_test_data(G_M_exact,G_M_num,G_M_err,"G_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvpa2_M_max, d2Gdvpa2_M_L2 = print_test_data(d2Gdvpa2_M_exact,d2Gdvpa2_M_num,d2Gdvpa2_M_err,"d2Gdvpa2_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + dGdvperp_M_max, dGdvperp_M_L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvperpdvpa_M_max, d2Gdvperpdvpa_M_L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + d2Gdvperp2_M_max, d2Gdvperp2_M_L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) + atol_max = 2.1e-4 + atol_L2 = 6.5e-6 + @test H_M_max < atol_max + @test H_M_L2 < atol_L2 + atol_max = 1.5e-3 + atol_L2 = 6.5e-5 + @test dHdvpa_M_max < atol_max + @test dHdvpa_M_L2 < atol_L2 + atol_max = 8.0e-4 + atol_L2 = 4.0e-5 + @test dHdvperp_M_max < atol_max + @test dHdvperp_M_L2 < atol_L2 + atol_max = 1.1e-4 + atol_L2 = 4.0e-5 + @test G_M_max < atol_max + @test G_M_L2 < atol_L2 + atol_max = 2.5e-4 + atol_L2 = 1.2e-5 + @test d2Gdvpa2_M_max < atol_max + @test d2Gdvpa2_M_L2 < atol_L2 + atol_max = 9.0e-5 + atol_L2 = 6.0e-5 + @test dGdvperp_M_max < atol_max + @test dGdvperp_M_L2 < atol_L2 + atol_max = 1.1e-4 + atol_L2 = 9.0e-6 + @test d2Gdvperpdvpa_M_max < atol_max + @test d2Gdvperpdvpa_M_L2 < atol_L2 + atol_max = 2.0e-4 + atol_L2 = 1.1e-5 + @test d2Gdvperp2_M_max < atol_max + @test d2Gdvperp2_M_L2 < atol_L2 + end + finalize_comms!() + end + + end end From 828361df94c8685f2b542600297b376b5bb174d6 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 27 Nov 2023 20:35:38 +0000 Subject: [PATCH 285/331] Move comment regarding the definition of the normalised collision frequency. --- src/fokker_planck.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 8903a6a1e..6241c6036 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -216,7 +216,11 @@ end """ Function for evaluating Css' = Css'[Fs,Fs'] +The result is stored in the array fkpl_arrays.CC """ +# the normalised collision frequency is defined by +# nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 +# with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp, fkpl_arrays::fokkerplanck_weakform_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral; @@ -540,11 +544,7 @@ For a single species, ir, and iz, this routine leaves in place the fokkerplanck_arrays struct with testable distributions function derivatives, Rosenbluth potentials, and collision operator in place. -""" -#returns distribution function advanced by the (normalised) -# C[F_s,F_s'] = C[F_s,F_s'](vpa,vperp) given inputs -#collision frequency nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 -#with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 +""" function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, From f50d52f4f037d996d95934b9afeb6d083196ef43 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 27 Nov 2023 22:02:43 +0000 Subject: [PATCH 286/331] Add H and G to the fokker_planck_arrays_struct for direct integration test. --- src/fokker_planck.jl | 8 ++++---- src/fokker_planck_calculus.jl | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 6241c6036..0a7b37741 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -439,12 +439,12 @@ function allocate_fokkerplanck_arrays(vperp,vpa) H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - #Rosenbluth_G = allocate_float(nvpa,nvperp) + GG = allocate_shared_float(nvpa,nvperp) d2Gdvpa2 = allocate_shared_float(nvpa,nvperp) d2Gdvperpdvpa = allocate_shared_float(nvpa,nvperp) d2Gdvperp2 = allocate_shared_float(nvpa,nvperp) dGdvperp = allocate_shared_float(nvpa,nvperp) - #Rosenbluth_H = allocate_float(nvpa,nvperp) + HH = allocate_shared_float(nvpa,nvperp) dHdvpa = allocate_shared_float(nvpa,nvperp) dHdvperp = allocate_shared_float(nvpa,nvperp) #Cflux_vpa = allocate_shared_float(nvpa,nvperp) @@ -459,8 +459,8 @@ function allocate_fokkerplanck_arrays(vperp,vpa) d2fdvperp2 = allocate_shared_float(nvpa,nvperp) return fokkerplanck_arrays_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, - dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, + GG,d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, + HH,dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, Cssp_result_vpavperp, dfdvpa, d2fdvpa2, d2fdvperpdvpa, dfdvperp, d2fdvperp2) end diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index ed429d810..ac36d3748 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -75,12 +75,12 @@ struct fokkerplanck_arrays_struct H1_weights::MPISharedArray{mk_float,4} H2_weights::MPISharedArray{mk_float,4} H3_weights::MPISharedArray{mk_float,4} - #Rosenbluth_G::Array{mk_float,2} + GG::MPISharedArray{mk_float,2} d2Gdvpa2::MPISharedArray{mk_float,2} d2Gdvperpdvpa::MPISharedArray{mk_float,2} d2Gdvperp2::MPISharedArray{mk_float,2} dGdvperp::MPISharedArray{mk_float,2} - #Rosenbluth_H::Array{mk_float,2} + HH::MPISharedArray{mk_float,2} dHdvpa::MPISharedArray{mk_float,2} dHdvperp::MPISharedArray{mk_float,2} #Cflux_vpa::MPISharedArray{mk_float,2} From be1235dcc9d176a8403b27be37ba7203a51333ba Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 27 Nov 2023 22:13:39 +0000 Subject: [PATCH 287/331] Add a test plotting script fkpl_direct_integration_test.jl to mirror the functionality of fkpl_functional_test.jl but only testing the Rosenbluth potential integration function. This script is used for generating plots of the performance of the numerical integration. Similar functionality exists in test/fokker_planck_tests.jl but without the ability to plot the results. --- fkpl_direct_integration_test.jl | 438 ++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 fkpl_direct_integration_test.jl diff --git a/fkpl_direct_integration_test.jl b/fkpl_direct_integration_test.jl new file mode 100644 index 000000000..c3b7cde0b --- /dev/null +++ b/fkpl_direct_integration_test.jl @@ -0,0 +1,438 @@ +export test_strong_form_collision_operator + +using Printf +using Plots +using LaTeXStrings +using Measures +using MPI +using Dates + +import moment_kinetics +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_direct_integration! +using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, dGdvperp_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian +using moment_kinetics.fokker_planck_test: dHdvpa_Maxwellian, dHdvperp_Maxwellian, H_Maxwellian, G_Maxwellian +using moment_kinetics.fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian +using moment_kinetics.fokker_planck_test: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian +using moment_kinetics.type_definitions: mk_float, mk_int +using moment_kinetics.calculus: derivative! +using moment_kinetics.velocity_moments: integrate_over_vspace, get_pressure +using moment_kinetics.communication +using moment_kinetics.looping +using moment_kinetics.array_allocation: allocate_shared_float, allocate_float + +function get_vth(pres,dens,mass) + return sqrt(2.0*pres/(dens*mass)) +end + +function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) + end +end + +function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) + for iscan in 1:nscan + expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) + end +end +""" +L2norm assuming the input is the +absolution error ff_err = ff - ff_exact +We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) +where the volume of velocity space is finite +""" +function L2norm_vspace(ff_err,vpa,vperp) + ff_ones = copy(ff_err) + @. ff_ones = 1.0 + gg = copy(ff_err) + @. gg = (ff_err)^2 + num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) + L2norm = sqrt(num/denom) + return L2norm +end + + +function init_grids(nelement,ngrid) + discretization = "gausslegendre_pseudospectral" + #discretization = "chebyshev_pseudospectral" + #discretization = "finite_difference" + + # define inputs needed for the test + vpa_ngrid = ngrid #number of points per element + vpa_nelement_local = nelement # number of elements per rank + vpa_nelement_global = vpa_nelement_local # total number of elements + vpa_L = 12.0 #physical box size in reference units + bc = "zero" + vperp_ngrid = ngrid #number of points per element + vperp_nelement_local = nelement # number of elements per rank + vperp_nelement_global = vperp_nelement_local # total number of elements + vperp_L = 6.0 #physical box size in reference units + bc = "zero" + + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + element_spacing_option = "uniform" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0 + comm = MPI.COMM_NULL + # create the 'input' struct containing input info needed to create a + # coordinate + vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, + nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, + nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + + # create the coordinate structs + #println("made inputs") + vpa, vpa_spectral = define_coordinate(vpa_input) + vperp, vperp_spectral = define_coordinate(vperp_input) + return vpa, vperp, vpa_spectral, vperp_spectral +end + +test_Lagrange_integral = false #true +test_Lagrange_integral_scan = true + +function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) + # set up grids for input Maxwellian + vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) + # set up necessary inputs for collision operator functions + nvperp = vperp.n + nvpa = vpa.n + # Set up MPI + if standalone + initialize_comms!() + end + setup_distributed_memory_MPI(1,1,1,1) + looping.setup_loop_ranges!(block_rank[], block_size[]; + s=1, sn=1, + r=1, z=1, vperp=vperp.n, vpa=vpa.n, + vzeta=1, vr=1, vz=1) + @serial_region begin + println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) + end + + fs_in = Array{mk_float,2}(undef,nvpa,nvperp) + + dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) + dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) + d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) + + GG_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + GG_err = allocate_shared_float(nvpa,nvperp) + d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) + dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dGdvperp_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) + d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) + + HH_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + HH_err = allocate_shared_float(nvpa,nvperp) + dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvpa_err = allocate_shared_float(nvpa,nvperp) + dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) + dHdvperp_err = allocate_shared_float(nvpa,nvperp) + + @serial_region begin + println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) + end + + # set up test Maxwellian + denss = 1.0 #3.0/4.0 + upars = 0.0 #2.0/3.0 + ppars = 1.0 #2.0/3.0 + pperps = 1.0 #2.0/3.0 + press = get_pressure(ppars,pperps) + ms = 1.0 + vths = get_vth(press,denss,ms) + + for ivperp in 1:nvperp + for ivpa in 1:nvpa + fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) + dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + + GG_Maxwell[ivpa,ivperp] = G_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + HH_Maxwell[ivpa,ivperp] = H_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) + end + end + + # initialise the weights + fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + fka = fokkerplanck_arrays + # calculate the potentials by direct integration + calculate_rosenbluth_potentials_via_direct_integration!(fka.GG,fka.HH,fka.dHdvpa,fka.dHdvperp, + fka.d2Gdvpa2,fka.dGdvperp,fka.d2Gdvperpdvpa,fka.d2Gdvperp2,fs_in, + vpa,vperp,vpa_spectral,vperp_spectral,fka) + + # error analysis of distribution function + begin_serial_region() + @serial_region begin + println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) + @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) + max_dfsdvpa_err = maximum(dfsdvpa_err) + println("max_dfsdvpa_err: ",max_dfsdvpa_err) + @. dfsdvperp_err = abs(fka.dfdvperp - dfsdvperp_Maxwell) + max_dfsdvperp_err = maximum(dfsdvperp_err) + println("max_dfsdvperp_err: ",max_dfsdvperp_err) + @. d2fsdvperpdvpa_err = abs(fka.d2fdvperpdvpa - d2fsdvperpdvpa_Maxwell) + max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) + println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) + + end + + plot_dHdvpa = false #true + plot_dHdvperp = false #true + plot_d2Gdvperp2 = false #true + plot_d2Gdvperpdvpa = false #true + plot_dGdvperp = false #true + plot_d2Gdvpa2 = false #true + + @serial_region begin + @. GG_err = abs(fka.GG - GG_Maxwell) + max_GG_err, max_GG_index = findmax(GG_err) + println("max_GG_err: ",max_GG_err," ",max_GG_index) + println("spot check GG_err: ",GG_err[end,end], " GG: ",fka.GG[end,end]) + + @. HH_err = abs(fka.HH - HH_Maxwell) + max_HH_err, max_HH_index = findmax(HH_err) + println("max_HH_err: ",max_HH_err," ",max_HH_index) + println("spot check HH_err: ",HH_err[end,end], " HH: ",fka.HH[end,end]) + @. dHdvperp_err = abs(fka.dHdvperp - dHdvperp_Maxwell) + max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) + println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) + println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",fka.dHdvperp[end,end]) + @. dHdvpa_err = abs(fka.dHdvpa - dHdvpa_Maxwell) + max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) + println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) + println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",fka.dHdvpa[end,end]) + + if plot_dHdvpa + @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvpa_err.pdf") + savefig(outfile) + end + if plot_dHdvperp + @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dHdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvperp2_err = abs(fka.d2Gdvperp2 - d2Gdvperp2_Maxwell) + max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) + println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) + println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",fka.d2Gdvperp2[end,end]) + if plot_d2Gdvperp2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperp2_err.pdf") + savefig(outfile) + end + @. d2Gdvperpdvpa_err = abs(fka.d2Gdvperpdvpa - d2Gdvperpdvpa_Maxwell) + max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) + println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) + println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",fka.d2Gdvperpdvpa[end,end]) + if plot_d2Gdvperpdvpa + @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") + savefig(outfile) + end + @. dGdvperp_err = abs(fka.dGdvperp - dGdvperp_Maxwell) + max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) + println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) + println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",fka.dGdvperp[end,end]) + if plot_dGdvperp + @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_dGdvperp_err.pdf") + savefig(outfile) + end + @. d2Gdvpa2_err = abs(fka.d2Gdvpa2 - d2Gdvpa2_Maxwell) + max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) + println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) + println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",fka.d2Gdvpa2[end,end]) + if plot_d2Gdvpa2 + @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") + savefig(outfile) + @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, + windowsize = (360,240), margin = 15pt) + outfile = string("fkpl_d2Gdvpa2_err.pdf") + savefig(outfile) + end + + end + _block_synchronize() + if standalone + finalize_comms!() + end + #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) + results = (maximum(GG_err), maximum(HH_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), + maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvperpdvpa_err)) + return results +end + +function test_rosenbluth_potentials_direct_integration(;ngrid=5,nelement_list=[2],plot_scan=true) + if size(nelement_list,1) == 1 + nelement = nelement_list[1] + test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) + else + initialize_comms!() + nscan = size(nelement_list,1) + max_G_err = Array{mk_float,1}(undef,nscan) + max_H_err = Array{mk_float,1}(undef,nscan) + max_dHdvpa_err = Array{mk_float,1}(undef,nscan) + max_dHdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) + max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + max_dGdvperp_err = Array{mk_float,1}(undef,nscan) + max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) + max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) + max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) + + expected = Array{mk_float,1}(undef,nscan) + expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) + expected_integral = Array{mk_float,1}(undef,nscan) + expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) + + expected_label = L"(1/N_{el})^{n_g - 1}" + expected_integral_label = L"(1/N_{el})^{n_g +1}" + + for iscan in 1:nscan + local nelement = nelement_list[iscan] + ((max_G_err[iscan], max_H_err[iscan], max_dHdvpa_err[iscan], + max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], + max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], + max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], + max_dfsdvperp_err[iscan], max_d2fsdvperpdvpa_err[iscan]) + = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) + end + if global_rank[]==0 && plot_scan + fontsize = 8 + ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) + xlabel = L"N_{element}" + dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" + dHdvperplabel = L"\epsilon(dH/d v_{\perp})" + d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" + d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" + d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" + dGdvperplabel = L"\epsilon(dG/d v_{\perp})" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], + xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + HHlabel = L"\epsilon(H)" + GGlabel = L"\epsilon(G)" + #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) + plot(nelement_list, [max_H_err, max_G_err, expected, expected_integral], + xlabel=xlabel, label=[HHlabel GGlabel expected_label expected_integral_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + + dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" + dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" + d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" + plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvperpdvpa_err,expected], + xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvperpdvpa_label expected_label], ylabel="", + shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, + xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, + foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) + #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" + outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" + savefig(outfile) + println(outfile) + end + finalize_comms!() + end +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + ngrid = 5 + nelement_list = [2,4,8,16,32] + plot_scan = true + test_rosenbluth_potentials_direct_integration(ngrid=ngrid,nelement_list=nelement_list,plot_scan=plot_scan) +end From 62318f0622e8995148e1a0ea61223a172f3694f4 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 28 Nov 2023 09:57:13 +0000 Subject: [PATCH 288/331] Commit removing the 'strong form' collision operator option. This was the original implementation of the collision operator, which did not have good conservation properties because it used a pseudo-spectral method to compute the derivatives in the collision operator. The form of the collision operator used was the fully expanded form (not a divergence form), and the Rosenbluth potentials were computed by direct integration. We choose not to support this option further because 1) the conserving terms in this operator required information from the boundary condition structs, which are no longer available in the euler_time_advance function. 2) The formulation of this option used a memory-intensive implementation. 3) Supporting two different self-collision operators in the source code is likely to be confusing. 4) The only benefit of keeping the operator would be to benefit from the speed of the pseudo-spectral method of computing the derivatives and the "strong form" construction (which is only valid if if the functions converge pointwise in a strong sense). However, it would be relatively easy to reconstruct this operator from the Rosenbluth potentials and derivative functions, if required, and so overall it seems preferable to clean up this previous implementation and associated test scripts in a single commit. --- 2D_FEM_assembly_test.jl | 1 - fkpl_direct_integration_test.jl | 6 +- fkpl_functional_test.jl | 502 ------- fkpl_single_field_point_test.jl | 1265 ------------------ fkpl_test.jl | 2163 ------------------------------- src/fokker_planck.jl | 436 +------ src/fokker_planck_calculus.jl | 8 +- src/input_structs.jl | 10 +- src/moment_kinetics_input.jl | 16 +- src/time_advance.jl | 60 +- test/fokker_planck_tests.jl | 4 +- 11 files changed, 41 insertions(+), 4430 deletions(-) delete mode 100644 fkpl_functional_test.jl delete mode 100644 fkpl_single_field_point_test.jl delete mode 100644 fkpl_test.jl diff --git a/2D_FEM_assembly_test.jl b/2D_FEM_assembly_test.jl index 856744d53..4798cd647 100644 --- a/2D_FEM_assembly_test.jl +++ b/2D_FEM_assembly_test.jl @@ -12,7 +12,6 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, get_QQ_local! using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.fokker_planck: init_fokker_planck_collisions using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form using moment_kinetics.fokker_planck: fokker_planck_collision_operator_weak_form! using moment_kinetics.fokker_planck: conserving_corrections! diff --git a/fkpl_direct_integration_test.jl b/fkpl_direct_integration_test.jl index c3b7cde0b..cf4fdaeb2 100644 --- a/fkpl_direct_integration_test.jl +++ b/fkpl_direct_integration_test.jl @@ -1,4 +1,4 @@ -export test_strong_form_collision_operator +export test_rosenbluth_potentials_direct_integration using Printf using Plots @@ -10,7 +10,7 @@ using Dates import moment_kinetics using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.fokker_planck: init_fokker_planck_collisions +using moment_kinetics.fokker_planck: init_fokker_planck_collisions_direct_integration using moment_kinetics.fokker_planck_calculus: calculate_rosenbluth_potentials_via_direct_integration! using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, dGdvperp_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian using moment_kinetics.fokker_planck_test: dHdvpa_Maxwellian, dHdvperp_Maxwellian, H_Maxwellian, G_Maxwellian @@ -180,7 +180,7 @@ function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) end # initialise the weights - fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) + fokkerplanck_arrays = init_fokker_planck_collisions_direct_integration(vperp,vpa; precompute_weights=true) fka = fokkerplanck_arrays # calculate the potentials by direct integration calculate_rosenbluth_potentials_via_direct_integration!(fka.GG,fka.HH,fka.dHdvpa,fka.dHdvperp, diff --git a/fkpl_functional_test.jl b/fkpl_functional_test.jl deleted file mode 100644 index 134facb45..000000000 --- a/fkpl_functional_test.jl +++ /dev/null @@ -1,502 +0,0 @@ -export test_strong_form_collision_operator - -using Printf -using Plots -using LaTeXStrings -using Measures -using MPI -using Dates - -import moment_kinetics -using moment_kinetics.input_structs: grid_input, advection_input, species_composition, collisions_input, boltzmann_electron_response -using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.gauss_legendre: gausslegendre_mass_matrix_solve! -using moment_kinetics.fokker_planck: init_fokker_planck_collisions, explicit_fokker_planck_collisions! -using moment_kinetics.fokker_planck_test: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, dGdvperp_Maxwellian, d2Gdvperpdvpa_Maxwellian, d2Gdvperp2_Maxwellian -using moment_kinetics.fokker_planck_test: dHdvpa_Maxwellian, dHdvperp_Maxwellian, Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian -using moment_kinetics.fokker_planck_test: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian -using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.calculus: derivative!, second_derivative! -using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure -using moment_kinetics.velocity_moments: integrate_over_vspace -using moment_kinetics.communication -using moment_kinetics.looping -using moment_kinetics.array_allocation: allocate_shared_float, allocate_float -using moment_kinetics.time_advance: setup_dummy_and_buffer_arrays -using moment_kinetics.advection: setup_advection -using moment_kinetics.initial_conditions: create_boundary_distributions, create_pdf -using moment_kinetics.input_structs: advance_info -using moment_kinetics.time_advance: setup_runge_kutta_coefficients - -function get_vth(pres,dens,mass) - return sqrt(pres/(dens*mass)) -end - -function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) - end -end - -function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) - end -end -""" -L2norm assuming the input is the -absolution error ff_err = ff - ff_exact -We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) -where the volume of velocity space is finite -""" -function L2norm_vspace(ff_err,vpa,vperp) - ff_ones = copy(ff_err) - @. ff_ones = 1.0 - gg = copy(ff_err) - @. gg = (ff_err)^2 - num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - L2norm = sqrt(num/denom) - return L2norm -end - - -function init_grids(nelement,ngrid) - discretization = "gausslegendre_pseudospectral" - #discretization = "chebyshev_pseudospectral" - #discretization = "finite_difference" - - # define inputs needed for the test - vpa_ngrid = ngrid #number of points per element - vpa_nelement_local = nelement # number of elements per rank - vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 12.0 #physical box size in reference units - bc = "zero" - vperp_ngrid = ngrid #number of points per element - vperp_nelement_local = nelement # number of elements per rank - vperp_nelement_global = vperp_nelement_local # total number of elements - vperp_L = 6.0 #physical box size in reference units - bc = "zero" - - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - element_spacing_option = "uniform" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) - vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) - - # create the coordinate structs - #println("made inputs") - vpa, vpa_spectral = define_coordinate(vpa_input) - vperp, vperp_spectral = define_coordinate(vperp_input) - return vpa, vperp, vpa_spectral, vperp_spectral -end - -test_Lagrange_integral = false #true -test_Lagrange_integral_scan = true - -function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) - # set up grids for input Maxwellian - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - nz = 1 - nr = 1 - n_ion_species = 1 - n_neutral_species = 1 - nvzeta = 1 - nvr = 1 - nvz = 1 - dt = 1.0 - adv_input = advection_input("default", 1.0, 0.0, 0.0) - vr_input = grid_input("vr", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") - vz_input = grid_input("vz", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") - vzeta_input = grid_input("vzeta", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") - r_input = grid_input("r", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") - z_input = grid_input("z", 1, 1, 1, - 1, 0, 1.0, "", "", "", "", adv_input,MPI.COMM_NULL,"uniform") - vr, vr_spectral = define_coordinate(vr_input) - vz, vz_spectral = define_coordinate(vz_input) - vzeta, vzeta_spectral = define_coordinate(vzeta_input) - r, r_spectral = define_coordinate(r_input) - z, z_spectral = define_coordinate(z_input) - composition = species_composition(n_ion_species, n_ion_species, n_neutral_species, - boltzmann_electron_response, false, 1.0, 1.0, - 1.0, 0.0, 1.0, 0.0, allocate_float(n_ion_species)) - nuii = 1.0 - collisions = collisions_input(0.0, 0.0, false, 0.0, "", false, nuii, 0.0, "none") - rk_coefs = setup_runge_kutta_coefficients(4) - advance = advance_info(false, false, false, false, false, false, - false, false, false, false, false, false, - true, false, false, false, false, false, - true, false, false, false, false, false, - rk_coefs, - false, false, true, false) - - # Set up MPI - if standalone - initialize_comms!() - end - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=n_ion_species, sn=n_neutral_species, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - scratch_dummy = setup_dummy_and_buffer_arrays(r.n,z.n,vpa.n,vperp.n,1,1,1, - composition.n_ion_species,1) - - @serial_region begin - println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - fs_in = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) - fs_out = Array{mk_float,5}(undef,nvpa,nvperp,nz,nr,n_ion_species) - - dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) - dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dGdvperp_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) - - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = allocate_shared_float(nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = allocate_shared_float(nvpa,nvperp) - - Cssp_err = allocate_shared_float(nvpa,nvperp) - Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - - @serial_region begin - println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # set up test Maxwellian - denss = 1.0 #3.0/4.0 - upars = 0.0 #2.0/3.0 - ppars = 1.0 #2.0/3.0 - pperps = 1.0 #2.0/3.0 - press = get_pressure(ppars,pperps) - ms = 1.0 - vths = get_vth(press,denss,ms) - - nussp = nuii - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - denss,upars,vths,ms, - nussp,vpa,vperp,ivpa,ivperp) - end - end - vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) - # initialise the vpa advection speed - begin_s_r_z_vperp_region() - begin_serial_region() - z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) - r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) - pdf_unused = create_pdf(composition, r, z, vperp, vpa, vzeta, vr, vz) - boundary_distributions = create_boundary_distributions(vz, vr, vzeta, vpa, vperp, z, - composition) - dSdt = 0.0 - - # initialise the weights - fokkerplanck_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) - # evaluate the collision operator - explicit_fokker_planck_collisions!(fs_out,fs_in,dSdt,composition,collisions,dt,fokkerplanck_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, - boundary_distributions, advance, - vpa_advect, z_advect, r_advect, - diagnose_entropy_production = false) - - fka = fokkerplanck_arrays - # error analysis of distribution function - begin_serial_region() - @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. dfsdvpa_err = abs(fka.dfdvpa - dfsdvpa_Maxwell) - max_dfsdvpa_err = maximum(dfsdvpa_err) - println("max_dfsdvpa_err: ",max_dfsdvpa_err) - @. d2fsdvpa2_err = abs(fka.d2fdvpa2 - d2fsdvpa2_Maxwell) - max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) - println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) - @. dfsdvperp_err = abs(fka.dfdvperp - dfsdvperp_Maxwell) - max_dfsdvperp_err = maximum(dfsdvperp_err) - println("max_dfsdvperp_err: ",max_dfsdvperp_err) - @. d2fsdvperpdvpa_err = abs(fka.d2fdvperpdvpa - d2fsdvperpdvpa_Maxwell) - max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) - println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) - @. d2fsdvperp2_err = abs(fka.d2fdvperp2 - d2fsdvperp2_Maxwell) - max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) - println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) - - end - - plot_dHdvpa = false #true - plot_dHdvperp = false #true - plot_d2Gdvperp2 = false #true - plot_d2Gdvperpdvpa = false #true - plot_dGdvperp = false #true - plot_d2Gdvpa2 = false #true - - @serial_region begin - @. Cssp_err = abs(fka.Cssp_result_vpavperp - Cssp_Maxwell) - max_C_err, max_C_index = findmax(Cssp_err) - println("max_C_err: ",max_C_err," ",max_C_index) - println("spot check C_err: ",Cssp_err[end,end], " Cssp: ",fka.Cssp_result_vpavperp[end,end]) - @. dHdvperp_err = abs(fka.dHdvperp - dHdvperp_Maxwell) - max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) - println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) - println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",fka.dHdvperp[end,end]) - @. dHdvpa_err = abs(fka.dHdvpa - dHdvpa_Maxwell) - max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) - println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) - println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",fka.dHdvpa[end,end]) - - if plot_dHdvpa - @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_err.pdf") - savefig(outfile) - end - if plot_dHdvperp - @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvperp2_err = abs(fka.d2Gdvperp2 - d2Gdvperp2_Maxwell) - max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) - println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) - println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",fka.d2Gdvperp2[end,end]) - if plot_d2Gdvperp2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_err.pdf") - savefig(outfile) - end - @. d2Gdvperpdvpa_err = abs(fka.d2Gdvperpdvpa - d2Gdvperpdvpa_Maxwell) - max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) - println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) - println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",fka.d2Gdvperpdvpa[end,end]) - if plot_d2Gdvperpdvpa - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") - savefig(outfile) - end - @. dGdvperp_err = abs(fka.dGdvperp - dGdvperp_Maxwell) - max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) - println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) - println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",fka.dGdvperp[end,end]) - if plot_dGdvperp - @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvpa2_err = abs(fka.d2Gdvpa2 - d2Gdvpa2_Maxwell) - max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) - println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) - println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",fka.d2Gdvpa2[end,end]) - if plot_d2Gdvpa2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_err.pdf") - savefig(outfile) - end - - end - _block_synchronize() - if standalone - finalize_comms!() - end - #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - (results = (maximum(Cssp_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), - maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err))) - return results -end - -function test_strong_form_collision_operator(;ngrid=5,nelement_list=[2],plot_scan=true) - if size(nelement_list,1) == 1 - nelement = nelement_list[1] - test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) - else - initialize_comms!() - nscan = size(nelement_list,1) - max_C_err = Array{mk_float,1}(undef,nscan) - max_dHdvpa_err = Array{mk_float,1}(undef,nscan) - max_dHdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_dGdvperp_err = Array{mk_float,1}(undef,nscan) - max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) - max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) - - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - expected_integral = Array{mk_float,1}(undef,nscan) - expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - - expected_label = L"(1/N_{el})^{n_g - 1}" - expected_integral_label = L"(1/N_{el})^{n_g +1}" - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - ((max_C_err[iscan],max_dHdvpa_err[iscan], - max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], - max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], - max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], - max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan]) - = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) - end - if global_rank[]==0 && plot_scan - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - Clabel = L"\epsilon(C)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabel = L"\epsilon(dG/d v_{\perp})" - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) - plot(nelement_list, [max_C_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - - dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" - dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" - d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" - d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" - d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" - plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], - xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - end - finalize_comms!() - end -end - -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - test_strong_form_collision_operator() -end diff --git a/fkpl_single_field_point_test.jl b/fkpl_single_field_point_test.jl deleted file mode 100644 index 7f6700a23..000000000 --- a/fkpl_single_field_point_test.jl +++ /dev/null @@ -1,1265 +0,0 @@ -using Printf -using Plots -using LaTeXStrings -using Measures -using MPI -using SpecialFunctions: erf, ellipe, ellipk -using FastGaussQuadrature -using Dates -using LinearAlgebra: mul! - -import moment_kinetics -using moment_kinetics.input_structs: grid_input, advection_input -using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral -using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! -using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! -using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! -#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: init_fokker_planck_collisions -using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients -using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian -using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian -using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.calculus: derivative!, second_derivative! -using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure -using moment_kinetics.velocity_moments: integrate_over_vspace -using moment_kinetics.communication -using moment_kinetics.looping -using moment_kinetics.array_allocation: allocate_shared_float - -function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth - return eta -end - -function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - G = 2.0/sqrt(pi) - else - # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) - G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) - end - return G*dens*vth -end -function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 - H = 2.0/sqrt(pi) - else - # H_M = erf(eta)/eta - H = erf(eta)/eta - end - return H*dens/vth -end - -function pressure(ppar,pperp) - pres = (1.0/3.0)*(ppar + 2.0*pperp) - return pres -end - -function get_vth(pres,dens,mass) - return sqrt(pres/(dens*mass)) -end - -function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) - end -end - -function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) - end -end -""" -L2norm assuming the input is the -absolution error ff_err = ff - ff_exact -We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) -where the volume of velocity space is finite -""" -function L2norm_vspace(ff_err,vpa,vperp) - ff_ones = copy(ff_err) - @. ff_ones = 1.0 - gg = copy(ff_err) - @. gg = (ff_err)^2 - num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - L2norm = sqrt(num/denom) - return L2norm -end - -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - - function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2Gdvpa2[:,ivperp] = vpa.scratch2 - end - end - - function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) - @. buffer_vpavperp[ivpa,:] = vperp.scratch - end - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) - @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch - end - end - - function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2Gdvperp2[ivpa,:] = vperp.scratch2 - end - end - - function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) - @. dHdvpa[:,ivperp] = vpa.scratch - end - end - - function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) - @. dHdvperp[ivpa,:] = vperp.scratch - end - end - - function init_grids(nelement,ngrid) - discretization = "gausslegendre_pseudospectral" - #discretization = "chebyshev_pseudospectral" - #discretization = "finite_difference" - - # define inputs needed for the test - vpa_ngrid = ngrid #number of points per element - vpa_nelement_local = nelement # number of elements per rank - vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 12.0 #physical box size in reference units - bc = "zero" - vperp_ngrid = ngrid #number of points per element - vperp_nelement_local = nelement # number of elements per rank - vperp_nelement_global = vperp_nelement_local # total number of elements - vperp_L = 6.0 #physical box size in reference units - bc = "zero" - - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - - # create the coordinate structs - #println("made inputs") - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - #println(vperp.grid) - #println(vperp.wgts) - if discretization == "chebyshev_pseudospectral" - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - #println("using chebyshev_pseudospectral") - elseif discretization == "gausslegendre_pseudospectral" - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - #println("using gausslegendre_pseudospectral") - end - return vpa, vperp, vpa_spectral, vperp_spectral - end - - test_Lagrange_integral = false #true - test_Lagrange_integral_scan = true - - function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) - # set up grids for input Maxwellian - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - #ivpa_field = floor(mk_int,nvpa/2 + nvpa/8 - 1) - #ivperp_field = floor(mk_int,nvperp/10) - ivpa_field = floor(mk_int,nvpa/2 + nvpa/16) - ivperp_field = floor(mk_int,nvperp/8 -1) - #ivpa_field = floor(mk_int,nvpa/2) - #ivperp_field = floor(mk_int,1) - ivpa_field = floor(mk_int,nvpa/2) + 5# + 4 - ivperp_field = 9 - println("Investigating vpa = ",vpa.grid[ivpa_field], " vperp = ",vperp.grid[ivperp_field]) - - # Set up MPI - if standalone - initialize_comms!() - end - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=1, sn=1, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - - @serial_region begin - println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - fs_in = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) - G_weights = allocate_shared_float(1,1,nvpa,nvperp) - G1_weights = allocate_shared_float(1,1,nvpa,nvperp) - G2_weights = allocate_shared_float(1,1,nvpa,nvperp) - G3_weights = allocate_shared_float(1,1,nvpa,nvperp) - Gsp = allocate_shared_float(1,1) - d2Gspdvpa2 = allocate_shared_float(1,1) - dGspdvperp = allocate_shared_float(1,1) - d2Gspdvperpdvpa = allocate_shared_float(1,1) - d2Gspdvperp2 = allocate_shared_float(1,1) - #Gsp = Array{mk_float,2}(undef,1,1) - G_Maxwell = Array{mk_float,2}(undef,1,1) - G_err = allocate_shared_float(1,1) - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,1,1) - d2Gdvpa2_err = allocate_shared_float(1,1) - dGdvperp_Maxwell = Array{mk_float,2}(undef,1,1) - dGdvperp_err = allocate_shared_float(1,1) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,1,1) - d2Gdvperpdvpa_err = allocate_shared_float(1,1) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,1,1) - d2Gdvperp2_err = allocate_shared_float(1,1) - - n_weights = allocate_shared_float(1,1,nvpa,nvperp) - nsp = allocate_shared_float(1,1) - n_err = allocate_shared_float(1,1) - - H_weights = allocate_shared_float(1,1,nvpa,nvperp) - H1_weights = allocate_shared_float(1,1,nvpa,nvperp) - H2_weights = allocate_shared_float(1,1,nvpa,nvperp) - H3_weights = allocate_shared_float(1,1,nvpa,nvperp) - Hsp_from_Gsp = allocate_shared_float(1,1) - Hsp = allocate_shared_float(1,1) - dHspdvpa = allocate_shared_float(1,1) - dHspdvperp = allocate_shared_float(1,1) - #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) - H_Maxwell = Array{mk_float,2}(undef,1,1) - H_err = allocate_shared_float(1,1) - dHdvpa_Maxwell = Array{mk_float,2}(undef,1,1) - dHdvpa_err = allocate_shared_float(1,1) - dHdvperp_Maxwell = Array{mk_float,2}(undef,1,1) - dHdvperp_err = allocate_shared_float(1,1) - - - @serial_region begin - println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # set up test Maxwellian - # species s - denss = 1.0 #3.0/4.0 - upars = 0.0 #2.0/3.0 - ppars = 1.0 #2.0/3.0 - pperps = 1.0 #2.0/3.0 - press = get_pressure(ppars,pperps) - ms = 1.0 - vths = get_vth(press,denss,ms) - # species sp - denssp = 1.0 #3.0/4.0 - uparsp = 0.0 #2.0/3.0 - pparsp = 1.0 #2.0/3.0 - pperpsp = 1.0 #2.0/3.0 - pressp = get_pressure(pparsp,pperpsp) - msp = 1.0 - vthsp = get_vth(pressp,denssp,msp) - - nussp = 1.0 - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfspdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - end - end - - ivpa = ivpa_field - ivperp = ivperp_field - G_Maxwell[1,1] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - H_Maxwell[1,1] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_Maxwell[1,1] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[1,1] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[1,1] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[1,1] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[1,1] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[1,1] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - for ivperp in 1:nvperp - # s - @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) - @. dfsdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2fsdvpa2[:,ivperp] = vpa.scratch2 - # sp - @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) - @. dfspdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2fspdvpa2[:,ivperp] = vpa.scratch2 - end - if vpa.discretization == "gausslegendre_pseudospectral" - @serial_region begin - println("use weak-form second derivative for vpa") - end - for ivperp in 1:nvperp - @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) - @. d2fsdvpa2[:,ivperp] = vpa.scratch2 - @views second_derivative!(vpa.scratch2, fsp_in[:,ivperp], vpa, vpa_spectral) - @. d2fspdvpa2[:,ivperp] = vpa.scratch2 - end - end - for ivpa in 1:vpa.n - # s - @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) - @. dfsdvperp[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fsdvperp2[ivpa,:] = vperp.scratch2 - # sp - @views derivative!(vperp.scratch, fsp_in[ivpa,:], vperp, vperp_spectral) - @. dfspdvperp[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fspdvperp2[ivpa,:] = vperp.scratch2 - end - for ivperp in 1:nvperp - # s - @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) - @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch - # sp - @views derivative!(vpa.scratch, dfspdvperp[:,ivperp], vpa, vpa_spectral) - @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch - end - - # error analysis of distribution function - @serial_region begin - @. dfsdvpa_err = abs(dfsdvpa - dfsdvpa_Maxwell) - max_dfsdvpa_err = maximum(dfsdvpa_err) - println("max_dfsdvpa_err: ",max_dfsdvpa_err) - @. d2fsdvpa2_err = abs(d2fsdvpa2 - d2fsdvpa2_Maxwell) - max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) - println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) - @. dfsdvperp_err = abs(dfsdvperp - dfsdvperp_Maxwell) - max_dfsdvperp_err = maximum(dfsdvperp_err) - println("max_dfsdvperp_err: ",max_dfsdvperp_err) - @. d2fsdvperpdvpa_err = abs(d2fsdvperpdvpa - d2fsdvperpdvpa_Maxwell) - max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) - println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) - @. d2fsdvperp2_err = abs(d2fsdvperp2 - d2fsdvperp2_Maxwell) - max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) - println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) - - @. dfspdvpa_err = abs(dfspdvpa - dfspdvpa_Maxwell) - max_dfspdvpa_err = maximum(dfspdvpa_err) - @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) - max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) - println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) - @. dfspdvperp_err = abs(dfspdvperp - dfspdvperp_Maxwell) - max_dfspdvperp_err = maximum(dfspdvperp_err) - println("max_dfspdvperp_err: ",max_dfspdvperp_err) - @. d2fspdvperpdvpa_err = abs(d2fspdvperpdvpa - d2fspdvperpdvpa_Maxwell) - max_d2fspdvperpdvpa_err = maximum(d2fspdvperpdvpa_err) - println("max_d2fspdvperpdvpa_err: ",max_d2fspdvperpdvpa_err) - @. d2fspdvperp2_err = abs(d2fspdvperp2 - d2fspdvperp2_Maxwell) - max_d2fspdvperp2_err = maximum(d2fspdvperp2_err) - println("max_d2fspdvperp2_err: ",max_d2fspdvperp2_err) - end - function get_imin_imax(coord,iel) - j = iel - if j > 1 - k = 1 - else - k = 0 - end - imin = coord.imin[j] - k - imax = coord.imax[j] - return imin, imax - end - - function get_nodes(coord,iel) - # get imin and imax of this element on full grid - (imin, imax) = get_imin_imax(coord,iel) - nodes = coord.grid[imin:imax] - return nodes - end - """ - Lagrange polynomial - args: - j - index of l_j from list of nodes - x_nodes - array of x node values - x - point where interpolated value is returned - """ - function lagrange_poly(j,x_nodes,x) - # get number of nodes - n = size(x_nodes,1) - # location where l(x0) = 1 - x0 = x_nodes[j] - # evaluate polynomial - poly = 1.0 - for i in 1:j-1 - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - for i in j+1:n - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - return poly - end - - function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) - println("nodes ",nodes) - zero = 1.0e-10 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - nnodes = size(nodes,1) - nquad_legendre = size(x_legendre,1) - nquad_laguerre = size(x_laguerre,1) - # assume x_scaled, w_scaled are arrays of length 2*nquad - # use only nquad points for most elements, but use 2*nquad for - # elements with interior divergences - println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - if abs(coord_val - node_max) < zero # divergence at upper endpoint - node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 - - n = nquad_laguerre + nquad_legendre - shift = 0.5*(node_min + node_cut) - scale = 0.5*(node_cut - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - - @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) - @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre - - nquad_coord = n - println("upper divergence") - elseif abs(coord_val - node_min) < zero # divergence at lower endpoint - n = nquad_laguerre + nquad_legendre - nquad = size(x_laguerre,1) - node_cut = (nodes[1] + nodes[2])/2.0 - for j in 1:nquad_laguerre - x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) - w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] - end - shift = 0.5*(node_max + node_cut) - scale = 0.5*(node_max - node_cut) - @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift - @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre - - nquad_coord = n - println("lower divergence") - else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - println("igrid_coord ", igrid_coord, " ", nodes[igrid_coord]," ", coord_val) - if nnodes > 3 - delta_nodes = nodes[2:end] - nodes[1:nnodes-1] - min_spacing = minimum(delta_nodes) - else # use that for ngrid = 2, 3 nodes are equally spaced - min_spacing = nodes[2] - nodes[1] - end - println("min spacing",min_spacing) - n = 2*nquad_laguerre - #node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 - node_cut_high = 0.5*min_spacing + nodes[igrid_coord] - if igrid_coord == 1 - # exception for vperp coordinate near orgin - k = 0 - node_cut_low = node_min - nquad_coord = nquad_legendre + 2*nquad_laguerre - else - # fill in lower Gauss-Legendre points - #node_cut_low = (nodes[igrid_coord-1] + nodes[igrid_coord])/2.0 - node_cut_low = nodes[igrid_coord] - 0.5*min_spacing - shift = 0.5*(node_cut_low + node_min) - scale = 0.5*(node_cut_low - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - k = nquad_legendre - nquad_coord = 2*(nquad_laguerre + nquad_legendre) - end - # lower half of domain - for j in 1:nquad_laguerre - x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] - end - # upper half of domain - for j in 1:nquad_laguerre - x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] - end - # fill in upper Gauss-Legendre points - shift = 0.5*(node_cut_high + node_max) - scale = 0.5*(node_max - node_cut_high) - @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift - @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre - - println("intermediate divergence") - #else # no divergences - # nquad = size(x_legendre,1) - # shift = 0.5*(node_min + node_max) - # scale = 0.5*(node_max - node_min) - # @. x_scaled[1:nquad] = scale*x_legendre + shift - # @. w_scaled[1:nquad] = scale*w_legendre - # #println("no divergence") - # nquad_coord = nquad - end - println("x_scaled",x_scaled) - println("w_scaled",w_scaled) - println("nquad_coord ",nquad_coord) - return nquad_coord - end - - function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) - zero = 1.0e-6 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - nquad = size(x_legendre,1) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled[1:nquad] = scale*x_legendre + shift - @. w_scaled[1:nquad] = scale*w_legendre - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) - return nquad - end - - # function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid - function ng_low(igrid,ngrid) - return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) - end - # function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid - function ng_hi(igrid,ngrid) - return floor(mk_int, igrid/ngrid) - end - # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 - function nel_low(ielement,nelement) - return floor(mk_int, (ielement - 2 + nelement)/nelement) - end - # function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement - function nel_hi(ielement,nelement) - return 1- floor(mk_int, ielement/nelement) - end - - function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids - nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids - for igrid_vperp in 1:vperp.ngrid - for igrid_vpa in 1:vpa.ngrid - # get grid index for point on full grid - ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad_vperp - for kvpa in 1:nquad_vpa - x_kvpa = x_vpa[kvpa] - x_kvperp = x_vperp[kvperp] - w_kvperp = w_vperp[kvperp] - w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - #mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) - #if mm_test > 1.0 - # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - #end - G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - - (G_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G1_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G2_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G3_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H1_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H2_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*(vpa_val - x_kvpa)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (n_weights[1,1,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - end - end - end - end - return nothing - end - - function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - vperp,ielement_vperpp, - #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in 1:ielement_vpa_low-1 - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - for ielement_vpap in ielement_vpa_low:ielement_vpa_hi - # use general grid function that checks divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - - end - return nothing - end - - function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - for ielement_vpap in 1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - - end - return nothing - end - - function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - for ielement_vperpp in 1:ielement_vperp_low-1 - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - - #vperp_nodes = get_nodes(vperp,ielement_vperpp) - #vperp_max = vperp_nodes[end] - #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperpp, - #nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - return nothing - end - - function loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - for ielement_vperpp in 1:vperp.nelement_local - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - return nothing - end - - @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # get Gauss-Legendre points and weights on (-1,1) - nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) - #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries - nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - - #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - - - @serial_region begin - println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid - nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid - # precalculated weights, integrating over Lagrange polynomials - #begin_vperp_vpa_region() - #@loop_vperp_vpa ivperp ivpa begin - ivpa = ivpa_field - ivperp = ivperp_field - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] - ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) - ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) - igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] - ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) - ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - @. G_weights[1,1,:,:] = 0.0 - @. G1_weights[1,1,:,:] = 0.0 - @. G2_weights[1,1,:,:] = 0.0 - @. G3_weights[1,1,:,:] = 0.0 - @. H_weights[1,1,:,:] = 0.0 - @. H1_weights[1,1,:,:] = 0.0 - @. H2_weights[1,1,:,:] = 0.0 - @. H3_weights[1,1,:,:] = 0.0 - @. n_weights[1,1,:,:] = 0.0 - # loop over elements and grid points within elements on primed coordinate - loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - #loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - #end - - #_block_synchronize() - begin_serial_region() - @serial_region begin - println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) - end - - begin_vperp_vpa_region() - - # use precalculated weights to calculate Gsp using nodal values of fs - #@loop_vperp_vpa ivperp ivpa begin - #for ivperp in 1:nvperp - #for ivpa in 1:nvpa - d2Gspdvpa2[1,1] = 0.0 - dGspdvperp[1,1] = 0.0 - d2Gspdvperpdvpa[1,1] = 0.0 - d2Gspdvperp2[1,1] = 0.0 - Gsp[1,1] = 0.0 - Hsp[1,1] = 0.0 - dHspdvpa[1,1] = 0.0 - dHspdvperp[1,1] = 0.0 - nsp[1,1] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - #d2Gspdvpa2[1,1] += G_weights[1,1,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] - d2Gspdvpa2[1,1] += H3_weights[1,1,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dGspdvperp[1,1] += G1_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperpdvpa[1,1] += G1_weights[1,1,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - #d2Gspdvperp2[1,1] += G2_weights[1,1,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperp2[1,1] += H2_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - Gsp[1,1] += G_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - Hsp[1,1] += H_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - dHspdvpa[1,1] += H_weights[1,1,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - dHspdvperp[1,1] += H1_weights[1,1,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - nsp[1,1] += n_weights[1,1,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - end - end - #end - - (Hsp_from_Gsp[1,1] = 0.5*( d2Gspdvpa2[1,1] + - d2Gspdvperp2[1,1] + - (1.0/vperp.grid[ivperp])*dGspdvperp[1,1])) - #end - - plot_H = false #true - plot_dHdvpa = false #true - plot_dHdvperp = false #true - plot_d2Gdvperp2 = false #true - plot_d2Gdvperpdvpa = false #true - plot_dGdvperp = false #true - plot_d2Gdvpa2 = false #true - plot_G = false #true - plot_n = false #true - - begin_serial_region() - @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. n_err = abs(nsp - denssp) - max_n_err = maximum(n_err) - #println("max_n_err: ",max_n_err) - println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) - @. H_err = abs(Hsp_from_Gsp - H_Maxwell) - max_H_err = maximum(H_err) - #println("max_H_from_G_err: ",max_H_err) - @. H_err = abs(Hsp - H_Maxwell) - max_H_err = maximum(H_err) - #println("max_H_err: ",max_H_err) - println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) - @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) - max_dHdvperp_err = maximum(dHdvperp_err) - #println("max_dHdvperp_err: ",max_dHdvperp_err) - println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) - @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) - max_dHdvpa_err = maximum(dHdvpa_err) - #println("max_dHdvpa_err: ",max_dHdvpa_err) - println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) - - if plot_n - @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_n_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, n_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_n_err.pdf") - savefig(outfile) - end - if plot_H - @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Hsp_from_Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_from_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_err.pdf") - savefig(outfile) - end - if plot_dHdvpa - @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_err.pdf") - savefig(outfile) - end - if plot_dHdvperp - @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) - max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) - #println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err) - println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) - if plot_d2Gdvperp2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_err.pdf") - savefig(outfile) - end - @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) - max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) - #println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err) - println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) - if plot_d2Gdvperpdvpa - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") - savefig(outfile) - end - @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) - max_dGdvperp_err = maximum(dGdvperp_err) - #println("max_dGdvperp_err: ",max_dGdvperp_err) - println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) - if plot_dGdvperp - @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) - max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) - #println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err) - println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) - if plot_d2Gdvpa2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_err.pdf") - savefig(outfile) - end - @. G_err = abs(Gsp - G_Maxwell) - max_G_err = maximum(G_err) - #println("max_G_err: ",max_G_err) - println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) - if plot_G - @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_err.pdf") - savefig(outfile) - end - end - _block_synchronize() - if standalone - finalize_comms!() - end - #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - (results = (maximum(G_err), maximum(H_err), - maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), - maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), - maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), - maximum(n_err))) - return results - end - - if test_Lagrange_integral - ngrid = 9 - nelement = 4 - test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) - end - if test_Lagrange_integral_scan - initialize_comms!() - ngrid = 5 - nscan = 1 - plot_scan = false - #nelement_list = Int[2, 4, 8, 16, 32, 64, 128] - #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[2, 4, 8] - #nelement_list = Int[100] - nelement_list = Int[32] - max_G_err = Array{mk_float,1}(undef,nscan) - max_H_err = Array{mk_float,1}(undef,nscan) - max_dHdvpa_err = Array{mk_float,1}(undef,nscan) - max_dHdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_dGdvperp_err = Array{mk_float,1}(undef,nscan) - max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) - max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) - max_dfspdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) - max_n_err = Array{mk_float,1}(undef,nscan) - - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - expected_integral = Array{mk_float,1}(undef,nscan) - expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - - expected_label = L"(1/N_{el})^{n_g - 1}" - expected_integral_label = L"(1/N_{el})^{n_g +1}" - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - ((max_G_err[iscan], max_H_err[iscan], - max_dHdvpa_err[iscan], - max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], - max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], - max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], - max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], - max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], - max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], - max_n_err[iscan]) - = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) - end - if global_rank[]==0 && plot_scan - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - nlabel = L"\epsilon(n)" - Glabel = L"\epsilon(G)" - Hlabel = L"\epsilon(H)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabel = L"\epsilon(dG/d v_{\perp})" - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral) - plot(nelement_list, [max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - plot(nelement_list, [max_G_err,max_H_err,max_n_err,expected,expected_integral], - xlabel=xlabel, label=[Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - - dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" - dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" - d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" - d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" - d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" - plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], - xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - - dfspdvperp_label = L"\epsilon(d F_{s^\prime} /d v_{\perp})" - d2fspdvpa2_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\|\|}^2)" - d2fspdvperpdvpa_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\perp}d v_{\|\|})" - d2fspdvperp2_label = L"\epsilon(d^2 F_{s^\prime}/ d v_{\perp}^2)" - plot(nelement_list, [max_dfspdvperp_err,max_d2fspdvpa2_err,max_d2fspdvperpdvpa_err,max_d2fspdvperp2_err,expected], - xlabel=xlabel, label=[dfspdvperp_label d2fspdvpa2_label d2fspdvperpdvpa_label d2fspdvperp2_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_fsp_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - end - finalize_comms!() - end - -end diff --git a/fkpl_test.jl b/fkpl_test.jl deleted file mode 100644 index aa4795f0d..000000000 --- a/fkpl_test.jl +++ /dev/null @@ -1,2163 +0,0 @@ -using Printf -using Plots -using LaTeXStrings -using Measures -using MPI -using SpecialFunctions: erf, ellipe, ellipk -using FastGaussQuadrature -using Dates -using LinearAlgebra: mul! - -import moment_kinetics -using moment_kinetics.input_structs: grid_input, advection_input -using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral -using moment_kinetics.gauss_legendre: setup_gausslegendre_pseudospectral, gausslegendre_mass_matrix_solve! -using moment_kinetics.fokker_planck: evaluate_RMJ_collision_operator! -using moment_kinetics.fokker_planck: calculate_Rosenbluth_potentials! -#using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: init_fokker_planck_collisions -using moment_kinetics.fokker_planck: calculate_collisional_fluxes, calculate_Maxwellian_Rosenbluth_coefficients -using moment_kinetics.fokker_planck: Cflux_vpa_Maxwellian_inputs, Cflux_vperp_Maxwellian_inputs -using moment_kinetics.fokker_planck: calculate_Rosenbluth_H_from_G! -using moment_kinetics.fokker_planck: d2Gdvpa2, dGdvperp, d2Gdvperpdvpa, d2Gdvperp2 -using moment_kinetics.fokker_planck: dHdvpa, dHdvperp, Cssp_Maxwellian_inputs -using moment_kinetics.fokker_planck: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian -using moment_kinetics.fokker_planck: d2Fdvpa2_Maxwellian, d2Fdvperpdvpa_Maxwellian, d2Fdvperp2_Maxwellian -using moment_kinetics.fokker_planck: Cssp_fully_expanded_form, get_local_Cssp_coefficients!, init_fokker_planck_collisions -using moment_kinetics.type_definitions: mk_float, mk_int -using moment_kinetics.calculus: derivative!, second_derivative! -using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure -using moment_kinetics.velocity_moments: integrate_over_vspace -using moment_kinetics.communication -using moment_kinetics.looping -using moment_kinetics.array_allocation: allocate_shared_float - -function eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - eta = sqrt((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vth - return eta -end - -function G_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - G = 2.0/sqrt(pi) - else - # G_M = (1/2 eta)*( eta erf'(eta) + (1 + 2 eta^2) erf(eta)) - G = (1.0/sqrt(pi))*exp(-eta^2) + ((0.5/eta) + eta)*erf(eta) - end - return G*dens*vth -end -function H_Maxwellian(dens,upar,vth,vpa,vperp,ivpa,ivperp) - # speed variable - eta = eta_speed(upar,vth,vpa,vperp,ivpa,ivperp) - zero = 1.0e-10 - if eta < zero - # erf(eta)/eta ~ 2/sqrt(pi) + O(eta^2) for eta << 1 - H = 2.0/sqrt(pi) - else - # H_M = erf(eta)/eta - H = erf(eta)/eta - end - return H*dens/vth -end - -function pressure(ppar,pperp) - pres = (1.0/3.0)*(ppar + 2.0*pperp) - return pres -end - -function get_vth(pres,dens,mass) - return sqrt(pres/(dens*mass)) -end - -function expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid - 1) - end -end - -function expected_nelement_integral_scaling!(expected,nelement_list,ngrid,nscan) - for iscan in 1:nscan - expected[iscan] = (1.0/nelement_list[iscan])^(ngrid+1) - end -end -""" -L2norm assuming the input is the -absolution error ff_err = ff - ff_exact -We compute sqrt( int (ff_err)^2 d^3 v / int d^3 v) -where the volume of velocity space is finite -""" -function L2norm_vspace(ff_err,vpa,vperp) - ff_ones = copy(ff_err) - @. ff_ones = 1.0 - gg = copy(ff_err) - @. gg = (ff_err)^2 - num = integrate_over_vspace(@view(gg[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - denom = integrate_over_vspace(@view(ff_ones[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - L2norm = sqrt(num/denom) - return L2norm -end - - #function Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) - # #Gamma = 0.0 - # #return Gamma - #end - #function Gamma_vpa_GMaxwellian(Bmag,vpa,mu,ivpa,imu) - # ## speed variable - # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) - # # - # #d2Gdeta2 = (erf(eta)/(eta^3)) - (2.0/sqrt(pi))*(exp(-eta^2)/(eta^2)) - # #zero = 1.0e-10 - # #if eta > zero - # # #Gamma = -2.0*vpa.grid[ivpa]*exp(-eta^2)*d2Gdeta2 - # #else - # # #Gamma = 0.0 - # #end - # #return Gamma - #end - #function Gamma_vpa_HMaxwellian(Bmag,vpa,mu,ivpa,imu) - # ## speed variable - # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) - # # - # #dHdeta = (2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2)) - # #zero = 1.0e-10 - # #if eta > zero - # # #Gamma = -2.0*vpa.grid[ivpa]*exp(-eta^2)*(1.0/eta)*dHdeta - # #else - # # #Gamma = 0.0 - # #end - # #return Gamma - #end - - #function Gamma_mu_Maxwellian(Bmag,vpa,mu,ivpa,imu) - # #Gamma = 0.0 - # #return Gamma - #end - #function Gamma_mu_GMaxwellian(Bmag,vpa,mu,ivpa,imu) - # ## speed variable - # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) - # # - # #d2Gdeta2 = (erf(eta)/(eta^3)) - (2.0/sqrt(pi))*(exp(-eta^2)/(eta^2)) - # #zero = 1.0e-10 - # #if eta > zero - # # #Gamma = -4.0*mu.grid[imu]*exp(-eta^2)*d2Gdeta2 - # #else - # # #Gamma = 0.0 - # #end - # #return Gamma - #end - #function Gamma_mu_HMaxwellian(Bmag,vpa,mu,ivpa,imu) - # ## speed variable - # #eta = sqrt(vpa.grid[ivpa]^2 + 2.0*Bmag*mu.grid[imu]) - # # - # #dHdeta = (2.0/sqrt(pi))*(exp(-eta^2)/eta) - (erf(eta)/(eta^2)) - # #zero = 1.0e-10 - # #if eta > zero - # # #Gamma = -4.0*mu.grid[imu]*exp(-eta^2)*(1.0/eta)*dHdeta - # #else - # # #Gamma = 0.0 - # #end - # #return Gamma - #end - -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - - function calculate_d2Gdvpa2!(d2Gdvpa2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, G[:,ivperp], vpa, vpa_spectral) - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2Gdvpa2[:,ivperp] = vpa.scratch2 - end - end - - function calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa,G,vpa,vpa_spectral,vperp,vperp_spectral, buffer_vpavperp) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) - @. buffer_vpavperp[ivpa,:] = vperp.scratch - end - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, buffer_vpavperp[:,ivperp], vpa, vpa_spectral) - @. d2Gdvperpdvpa[:,ivperp] = vpa.scratch - end - end - - function calculate_d2Gdvperp2!(d2Gdvperp2,G,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, G[ivpa,:], vperp, vperp_spectral) - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2Gdvperp2[ivpa,:] = vperp.scratch2 - end - end - - function calculate_dHdvpa!(dHdvpa,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivperp in 1:vperp.n - @views derivative!(vpa.scratch, H[:,ivperp], vpa, vpa_spectral) - @. dHdvpa[:,ivperp] = vpa.scratch - end - end - - function calculate_dHdvperp!(dHdvperp,H,vpa,vpa_spectral,vperp,vperp_spectral) - for ivpa in 1:vpa.n - @views derivative!(vperp.scratch, H[ivpa,:], vperp, vperp_spectral) - @. dHdvperp[ivpa,:] = vperp.scratch - end - end - - function init_grids(nelement,ngrid) - discretization = "gausslegendre_pseudospectral" - #discretization = "chebyshev_pseudospectral" - #discretization = "finite_difference" - - # define inputs needed for the test - vpa_ngrid = ngrid #number of points per element - vpa_nelement_local = nelement # number of elements per rank - vpa_nelement_global = vpa_nelement_local # total number of elements - vpa_L = 12.0 #physical box size in reference units - bc = "zero" - vperp_ngrid = ngrid #number of points per element - vperp_nelement_local = nelement # number of elements per rank - vperp_nelement_global = vperp_nelement_local # total number of elements - vperp_L = 6.0 #physical box size in reference units - bc = "zero" - - # fd_option and adv_input not actually used so given values unimportant - fd_option = "fourth_order_centered" - cheb_option = "matrix" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - nrank = 1 - irank = 0 - comm = MPI.COMM_NULL - # create the 'input' struct containing input info needed to create a - # coordinate - vpa_input = grid_input("vpa", vpa_ngrid, vpa_nelement_global, vpa_nelement_local, - nrank, irank, vpa_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - vperp_input = grid_input("vperp", vperp_ngrid, vperp_nelement_global, vperp_nelement_local, - nrank, irank, vperp_L, discretization, fd_option, cheb_option, bc, adv_input,comm) - - # create the coordinate structs - #println("made inputs") - vpa = define_coordinate(vpa_input) - vperp = define_coordinate(vperp_input) - #println(vperp.grid) - #println(vperp.wgts) - if discretization == "chebyshev_pseudospectral" - vpa_spectral = setup_chebyshev_pseudospectral(vpa) - vperp_spectral = setup_chebyshev_pseudospectral(vperp) - #println("using chebyshev_pseudospectral") - elseif discretization == "gausslegendre_pseudospectral" - vpa_spectral = setup_gausslegendre_pseudospectral(vpa) - vperp_spectral = setup_gausslegendre_pseudospectral(vperp) - #println("using gausslegendre_pseudospectral") - end - return vpa, vperp, vpa_spectral, vperp_spectral - end - - test_Rosenbluth_integrals = false#true - test_collision_operator_fluxes = false#true - test_Lagrange_integral = false #true - test_Lagrange_integral_scan = true - #ngrid = 9 - #nelement = 8 - - function test_Rosenbluth_potentials(nelement,ngrid;numerical_G = false) - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - - fs_in = Array{mk_float,2}(undef,nvpa,nvperp) - G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_check = Array{mk_float,2}(undef,nvpa,nvperp) - G_err = Array{mk_float,2}(undef,nvpa,nvperp) - H_err = Array{mk_float,2}(undef,nvpa,nvperp) - H_check_err = Array{mk_float,2}(undef,nvpa,nvperp) - - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_check = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_check = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - - - # set up test Maxwellian - dens = 3.0/4.0 - upar = 2.0/3.0 - ppar = 2.0/3.0 - pperp = 2.0/3.0 - pres = get_pressure(ppar,pperp) - mi = 1.0 - vths = get_vth(pres,dens,mi) - - n_ion_species = 1 - dens_in = Array{mk_float,1}(undef,n_ion_species) - upar_in = Array{mk_float,1}(undef,n_ion_species) - vths_in = Array{mk_float,1}(undef,n_ion_species) - dens_in[1] = dens - upar_in[1] = upar - vths_in[1] = vths - fkarrays = init_fokker_planck_collisions(vperp, vpa, init_integral_factors = true) - - - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - G_Maxwell[ivpa,ivperp] = G_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(dens,upar,vths,vpa,vperp,ivpa,ivperp) - end - end - - fsp_in = fs_in - - # evaluate the Rosenbluth potentials - @views calculate_Rosenbluth_potentials!(fkarrays.Rosenbluth_G, fkarrays.Rosenbluth_H, - fsp_in, fkarrays.elliptic_integral_E_factor,fkarrays.elliptic_integral_K_factor, - fkarrays.buffer_vpavperp_1,vperp,vpa) - - #@views heatmap(vperp.grid, vpa.grid, fkarrays.Rosenbluth_G[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # windowsize = (360,240), margin = 15pt) - # outfile = string("fkpl_RosenG.pdf") - # savefig(outfile) - #@views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # windowsize = (360,240), margin = 15pt) - # outfile = string("fkpl_G_Maxwell.pdf") - # savefig(outfile) - - G_in = G_Maxwell # use analytical G to calculate a H for the check - @views calculate_Rosenbluth_H_from_G!(H_check,G_in, - vpa,vpa_spectral,vperp,vperp_spectral, - fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) - - if numerical_G - G_in = fkarrays.Rosenbluth_G - H_in = fkarrays.Rosenbluth_H - @views calculate_Rosenbluth_H_from_G!(H_in,G_in, - vpa,vpa_spectral,vperp,vperp_spectral, - fkarrays.buffer_vpavperp_1,fkarrays.buffer_vpavperp_2) - # using numerical G, errors are much worse than using smooth analytical G - else - G_in = G_Maxwell # use analytical G for coeffs - H_in = H_Maxwell # use analytical H for coeffs - end - @views calculate_d2Gdvpa2!(d2Gdvpa2_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral) - @views calculate_d2Gdvperpdvpa!(d2Gdvperpdvpa_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral, fkarrays.buffer_vpavperp_1) - @views calculate_d2Gdvperp2!(d2Gdvperp2_check,G_in,vpa,vpa_spectral,vperp,vperp_spectral) - @views calculate_dHdvperp!(dHdvperp_check,H_in,vpa,vpa_spectral,vperp,vperp_spectral) - @views calculate_dHdvpa!(dHdvpa_check,H_in,vpa,vpa_spectral,vperp,vperp_spectral) - - #@views heatmap(vperp.grid, vpa.grid, H_check[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # windowsize = (360,240), margin = 15pt) - # outfile = string("fkpl_RosenH.pdf") - # savefig(outfile) - #@views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # windowsize = (360,240), margin = 15pt) - # outfile = string("fkpl_H_Maxwell.pdf") - # savefig(outfile) - - - @. G_err = abs(fkarrays.Rosenbluth_G - G_Maxwell) - @. H_err = abs(fkarrays.Rosenbluth_H - H_Maxwell) - @. H_check_err = abs(H_check - H_Maxwell) - max_G_err = maximum(G_err) - max_H_err = maximum(H_err) - max_H_check_err = maximum(H_check_err) - println("max(G_err)",max_G_err) - println("max(H_err)",max_H_err) - println("max(H_check_err)",max_H_check_err) - - for ivperp in 1:nvperp - for ivpa in 1:nvpa - ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian - ((d2Gdvpa2_Maxwell[ivpa,ivperp], d2Gdvperpdvpa_Maxwell[ivpa,ivperp], - d2Gdvperp2_Maxwell[ivpa,ivperp],dHdvpa_Maxwell[ivpa,ivperp], - dHdvperp_Maxwell[ivpa,ivperp]) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[:], - upar_in[:],vths_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) - end - end - - @. d2Gdvperpdvpa_err = abs(d2Gdvperpdvpa_check - d2Gdvperpdvpa_Maxwell) - @. d2Gdvpa2_err = abs(d2Gdvpa2_check - d2Gdvpa2_Maxwell) - @. d2Gdvperp2_err = abs(d2Gdvperp2_check - d2Gdvperp2_Maxwell) - @. dHdvperp_err = abs(dHdvperp_check - dHdvperp_Maxwell) - @. dHdvpa_err = abs(dHdvpa_check - dHdvpa_Maxwell) - max_dHdvpa_err = maximum(dHdvpa_err) - max_dHdvperp_err = maximum(dHdvperp_err) - max_d2Gdvperp2_err = maximum(d2Gdvperp2_err) - max_d2Gdvpa2_err = maximum(d2Gdvpa2_err) - max_d2Gdvperpdvpa_err = maximum(d2Gdvperpdvpa_err) - println("max(dHdvpa_err)",max_dHdvpa_err) - println("max(dHdvperp_err)",max_dHdvperp_err) - println("max(d2Gdvperp2_err)",max_d2Gdvperp2_err) - println("max(d2Gdvpa2_err)",max_d2Gdvpa2_err) - println("max(d2Gdvperpdvpa_err)",max_d2Gdvperpdvpa_err) - # - # zero = 1.0e-3 - # #println(G_Maxwell[41,:]) - # #println(G_Maxwell[:,1]) - # for ivperp in 1:nvperp - # #for ivpa in 1:nvpa - # # if (maximum(G_err[ivpa,ivperp]) > zero) - # # ##println("ivpa: ",ivpa," ivperp: ",ivperp," G_err: ",G_err[ivpa,ivperp]) - # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]," G_Maxwell: ",G_Maxwell[ivpa,ivperp]," G_num: ",fkarrays.Rosenbluth_G[ivpa,ivperp]) - # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," G_err: ",G_err[ivpa,ivperp]) - # # end - # # #H_err[ivpa,ivperp] - # #end - # end - # #println(H_Maxwell[:,1]) - # #println(fkarrays.Rosenbluth_H[:,1]) - # #zero = 0.1 - # for ivperp in 1:nvperp - # #for ivpa in 1:nvpa - # # if (maximum(H_err[ivpa,ivperp]) > zero) - # # ###println("ivpa: ",ivpa," ivperp: ",ivperp," H_err: ",H_err[ivpa,ivperp]) - # # ##println("ivpa: ",ivpa," vpa: ",vpa.grid[ivpa]," ivperp: ",ivperp," vperp: ",vperp.grid[ivperp]," H_err: ",H_err[ivpa,ivperp]," H_Maxwell: ",H_Maxwell[ivpa,ivperp]," H_num: ",fkarrays.Rosenbluth_H[ivpa,ivperp]) - # # end - # # #H_err[ivpa,ivperp] - # #end - # end - return max_G_err, max_H_err, max_H_check_err, max_dHdvpa_err, max_dHdvperp_err, max_d2Gdvperp2_err, max_d2Gdvpa2_err, max_d2Gdvperpdvpa_err - end - - function test_Lagrange_Rosenbluth_potentials(ngrid,nelement; standalone=true) - # set up grids for input Maxwellian - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - - # Set up MPI - if standalone - initialize_comms!() - end - setup_distributed_memory_MPI(1,1,1,1) - looping.setup_loop_ranges!(block_rank[], block_size[]; - s=1, sn=1, - r=1, z=1, vperp=vperp.n, vpa=vpa.n, - vzeta=1, vr=1, vz=1) - - @serial_region begin - println("beginning allocation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - fs_in = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfsdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fsdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - fsp_in = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvpa2_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - dfspdvperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperpdvpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - d2fspdvperp2_err = Array{mk_float,2}(undef,nvpa,nvperp) - - #G_weights = Array{mk_float,4}(undef,nvpa,nvperp,nvpa,nvperp) - G_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - G3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Gsp = allocate_shared_float(nvpa,nvperp) - d2Gspdvpa2 = allocate_shared_float(nvpa,nvperp) - dGspdvperp = allocate_shared_float(nvpa,nvperp) - d2Gspdvperpdvpa = allocate_shared_float(nvpa,nvperp) - d2Gspdvperp2 = allocate_shared_float(nvpa,nvperp) - #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) - G_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - G_err = allocate_shared_float(nvpa,nvperp) - d2Gdvpa2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvpa2_err = allocate_shared_float(nvpa,nvperp) - dGdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dGdvperp_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperpdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa_err = allocate_shared_float(nvpa,nvperp) - d2Gdvperp2_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2_err = allocate_shared_float(nvpa,nvperp) - - n_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - nsp = allocate_shared_float(nvpa,nvperp) - n_err = allocate_shared_float(nvpa,nvperp) - - H_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H1_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H2_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - H3_weights = allocate_shared_float(nvpa,nvperp,nvpa,nvperp) - Hsp = allocate_shared_float(nvpa,nvperp) - Hsp_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvpa_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvperp_from_Gsp = allocate_shared_float(nvpa,nvperp) - dHspdvpa = allocate_shared_float(nvpa,nvperp) - dHspdvperp = allocate_shared_float(nvpa,nvperp) - #Gsp = Array{mk_float,2}(undef,nvpa,nvperp) - H_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - H_err = allocate_shared_float(nvpa,nvperp) - dHdvpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa_err = allocate_shared_float(nvpa,nvperp) - dHdvperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp_err = allocate_shared_float(nvpa,nvperp) - - Cssp_numerical = allocate_shared_float(nvpa,nvperp) - Cssp_err = allocate_shared_float(nvpa,nvperp) - Cssp_div_numerical = allocate_shared_float(nvpa,nvperp) - Cssp_div_err = allocate_shared_float(nvpa,nvperp) - Cssp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Cflux_vpa = allocate_shared_float(nvpa,nvperp) - Cflux_vpa_err = allocate_shared_float(nvpa,nvperp) - Cflux_vperp = allocate_shared_float(nvpa,nvperp) - Cflux_vperp_err = allocate_shared_float(nvpa,nvperp) - Cflux_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Cflux_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - - @serial_region begin - println("setting up input arrays ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # set up test Maxwellian - # species s - denss = 1.0 #3.0/4.0 - upars = 0.0 #2.0/3.0 - ppars = 1.0 #2.0/3.0 - pperps = 1.0 #2.0/3.0 - press = get_pressure(ppars,pperps) - ms = 1.0 - vths = get_vth(press,denss,ms) - # species sp - denssp = 1.0 #3.0/4.0 - uparsp = 0.0 #2.0/3.0 - pparsp = 1.0 #2.0/3.0 - pperpsp = 1.0 #2.0/3.0 - pressp = get_pressure(pparsp,pperpsp) - msp = 1.0 - vthsp = get_vth(pressp,denssp,msp) - - nussp = 1.0 - for ivperp in 1:nvperp - for ivpa in 1:nvpa - fs_in[ivpa,ivperp] = F_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfsdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - dfsdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - d2fsdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denss,upars,vths,vpa,vperp,ivpa,ivperp) - - fsp_in[ivpa,ivperp] = F_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) #(denss/vths^3)*exp( - ((vpa.grid[ivpa]-upar)^2 + vperp.grid[ivperp]^2)/vths^2 ) - dfspdvpa_Maxwell[ivpa,ivperp] = dFdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvpa2_Maxwell[ivpa,ivperp] = d2Fdvpa2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dfspdvperp_Maxwell[ivpa,ivperp] = dFdvperp_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvperpdvpa_Maxwell[ivpa,ivperp] = d2Fdvperpdvpa_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2fspdvperp2_Maxwell[ivpa,ivperp] = d2Fdvperp2_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - G_Maxwell[ivpa,ivperp] = G_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - H_Maxwell[ivpa,ivperp] = H_Maxwellian(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvpa2_Maxwell[ivpa,ivperp] = d2Gdvpa2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dGdvperp_Maxwell[ivpa,ivperp] = dGdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperpdvpa_Maxwell[ivpa,ivperp] = d2Gdvperpdvpa(denssp,upars,vthsp,vpa,vperp,ivpa,ivperp) - d2Gdvperp2_Maxwell[ivpa,ivperp] = d2Gdvperp2(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvperp_Maxwell[ivpa,ivperp] = dHdvperp(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - dHdvpa_Maxwell[ivpa,ivperp] = dHdvpa(denssp,uparsp,vthsp,vpa,vperp,ivpa,ivperp) - - Cssp_Maxwell[ivpa,ivperp] = Cssp_Maxwellian_inputs(denss,upars,vths,ms, - denssp,uparsp,vthsp,msp, - nussp,vpa,vperp,ivpa,ivperp) - Cflux_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(ms,denss,upars,vths, - msp,denssp,uparsp,vthsp, - vpa,vperp,ivpa,ivperp) - Cflux_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(ms,denss,upars,vths, - msp,denssp,uparsp,vthsp, - vpa,vperp,ivpa,ivperp) - end - end - for ivperp in 1:nvperp - # s - @views derivative!(vpa.scratch, fs_in[:,ivperp], vpa, vpa_spectral) - @. dfsdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2fsdvpa2[:,ivperp] = vpa.scratch2 - # sp - @views derivative!(vpa.scratch, fsp_in[:,ivperp], vpa, vpa_spectral) - @. dfspdvpa[:,ivperp] = vpa.scratch - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2fspdvpa2[:,ivperp] = vpa.scratch2 - end - if vpa.discretization == "gausslegendre_pseudospectral" - @serial_region begin - println("use weak-form second derivative for vpa") - end - for ivperp in 1:nvperp - @views second_derivative!(vpa.scratch2, fs_in[:,ivperp], vpa, vpa_spectral) - @. d2fsdvpa2[:,ivperp] = vpa.scratch2 - @views second_derivative!(vpa.scratch2, fsp_in[:,ivperp], vpa, vpa_spectral) - @. d2fspdvpa2[:,ivperp] = vpa.scratch2 - end - end - for ivpa in 1:vpa.n - # s - @views derivative!(vperp.scratch, fs_in[ivpa,:], vperp, vperp_spectral) - @. dfsdvperp[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fsdvperp2[ivpa,:] = vperp.scratch2 - # sp - @views derivative!(vperp.scratch, fsp_in[ivpa,:], vperp, vperp_spectral) - @. dfspdvperp[ivpa,:] = vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fspdvperp2[ivpa,:] = vperp.scratch2 - end - for ivperp in 1:nvperp - # s - @views derivative!(vpa.scratch, dfsdvperp[:,ivperp], vpa, vpa_spectral) - @. d2fsdvperpdvpa[:,ivperp] = vpa.scratch - # sp - @views derivative!(vpa.scratch, dfspdvperp[:,ivperp], vpa, vpa_spectral) - @. d2fspdvperpdvpa[:,ivperp] = vpa.scratch - end - - # error analysis of distribution function - @serial_region begin - @. dfsdvpa_err = abs(dfsdvpa - dfsdvpa_Maxwell) - max_dfsdvpa_err = maximum(dfsdvpa_err) - println("max_dfsdvpa_err: ",max_dfsdvpa_err) - @. d2fsdvpa2_err = abs(d2fsdvpa2 - d2fsdvpa2_Maxwell) - max_d2fsdvpa2_err = maximum(d2fsdvpa2_err) - println("max_d2fsdvpa2_err: ",max_d2fsdvpa2_err) - @. dfsdvperp_err = abs(dfsdvperp - dfsdvperp_Maxwell) - max_dfsdvperp_err = maximum(dfsdvperp_err) - println("max_dfsdvperp_err: ",max_dfsdvperp_err) - @. d2fsdvperpdvpa_err = abs(d2fsdvperpdvpa - d2fsdvperpdvpa_Maxwell) - max_d2fsdvperpdvpa_err = maximum(d2fsdvperpdvpa_err) - println("max_d2fsdvperpdvpa_err: ",max_d2fsdvperpdvpa_err) - @. d2fsdvperp2_err = abs(d2fsdvperp2 - d2fsdvperp2_Maxwell) - max_d2fsdvperp2_err = maximum(d2fsdvperp2_err) - println("max_d2fsdvperp2_err: ",max_d2fsdvperp2_err) - - @. dfspdvpa_err = abs(dfspdvpa - dfspdvpa_Maxwell) - max_dfspdvpa_err = maximum(dfspdvpa_err) - @. d2fspdvpa2_err = abs(d2fspdvpa2 - d2fspdvpa2_Maxwell) - max_d2fspdvpa2_err = maximum(d2fspdvpa2_err) - println("max_d2fspdvpa2_err: ",max_d2fspdvpa2_err) - @. dfspdvperp_err = abs(dfspdvperp - dfspdvperp_Maxwell) - max_dfspdvperp_err = maximum(dfspdvperp_err) - println("max_dfspdvperp_err: ",max_dfspdvperp_err) - @. d2fspdvperpdvpa_err = abs(d2fspdvperpdvpa - d2fspdvperpdvpa_Maxwell) - max_d2fspdvperpdvpa_err = maximum(d2fspdvperpdvpa_err) - println("max_d2fspdvperpdvpa_err: ",max_d2fspdvperpdvpa_err) - @. d2fspdvperp2_err = abs(d2fspdvperp2 - d2fspdvperp2_Maxwell) - max_d2fspdvperp2_err = maximum(d2fspdvperp2_err) - println("max_d2fspdvperp2_err: ",max_d2fspdvperp2_err) - end - function get_imin_imax(coord,iel) - j = iel - if j > 1 - k = 1 - else - k = 0 - end - imin = coord.imin[j] - k - imax = coord.imax[j] - return imin, imax - end - - function get_nodes(coord,iel) - # get imin and imax of this element on full grid - (imin, imax) = get_imin_imax(coord,iel) - nodes = coord.grid[imin:imax] - return nodes - end - """ - Lagrange polynomial - args: - j - index of l_j from list of nodes - x_nodes - array of x node values - x - point where interpolated value is returned - """ - function lagrange_poly(j,x_nodes,x) - # get number of nodes - n = size(x_nodes,1) - # location where l(x0) = 1 - x0 = x_nodes[j] - # evaluate polynomial - poly = 1.0 - for i in 1:j-1 - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - for i in j+1:n - poly *= (x - x_nodes[i])/(x0 - x_nodes[i]) - end - return poly - end - - function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) - #println("nodes ",nodes) - zero = 1.0e-10 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - nnodes = size(nodes,1) - nquad_legendre = size(x_legendre,1) - nquad_laguerre = size(x_laguerre,1) - # assume x_scaled, w_scaled are arrays of length 2*nquad - # use only nquad points for most elements, but use 2*nquad for - # elements with interior divergences - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - if abs(coord_val - node_max) < zero # divergence at upper endpoint - node_cut = (nodes[nnodes-1] + nodes[nnodes])/2.0 - - n = nquad_laguerre + nquad_legendre - shift = 0.5*(node_min + node_cut) - scale = 0.5*(node_cut - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - - @. x_scaled[1+nquad_legendre:n] = node_max + (node_cut - node_max)*exp(-x_laguerre) - @. w_scaled[1+nquad_legendre:n] = (node_max - node_cut)*w_laguerre - - nquad_coord = n - #println("upper divergence") - elseif abs(coord_val - node_min) < zero # divergence at lower endpoint - n = nquad_laguerre + nquad_legendre - nquad = size(x_laguerre,1) - node_cut = (nodes[1] + nodes[2])/2.0 - for j in 1:nquad_laguerre - x_scaled[nquad_laguerre+1-j] = node_min + (node_cut - node_min)*exp(-x_laguerre[j]) - w_scaled[nquad_laguerre+1-j] = (node_cut - node_min)*w_laguerre[j] - end - shift = 0.5*(node_max + node_cut) - scale = 0.5*(node_max - node_cut) - @. x_scaled[1+nquad_laguerre:n] = scale*x_legendre + shift - @. w_scaled[1+nquad_laguerre:n] = scale*w_legendre - - nquad_coord = n - #println("lower divergence") - else #if (coord_val - node_min)*(coord_val - node_max) < - zero # interior divergence - #println(nodes[igrid_coord]," ", coord_val) - n = 2*nquad_laguerre - node_cut_high = (nodes[igrid_coord+1] + nodes[igrid_coord])/2.0 - if igrid_coord == 1 - # exception for vperp coordinate near orgin - k = 0 - node_cut_low = node_min - nquad_coord = nquad_legendre + 2*nquad_laguerre - else - # fill in lower Gauss-Legendre points - node_cut_low = (nodes[igrid_coord-1]+nodes[igrid_coord])/2.0 - shift = 0.5*(node_cut_low + node_min) - scale = 0.5*(node_cut_low - node_min) - @. x_scaled[1:nquad_legendre] = scale*x_legendre + shift - @. w_scaled[1:nquad_legendre] = scale*w_legendre - k = nquad_legendre - nquad_coord = 2*(nquad_laguerre + nquad_legendre) - end - # lower half of domain - for j in 1:nquad_laguerre - x_scaled[k+j] = coord_val + (node_cut_low - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+j] = (coord_val - node_cut_low)*w_laguerre[j] - end - # upper half of domain - for j in 1:nquad_laguerre - x_scaled[k+n+1-j] = coord_val + (node_cut_high - coord_val)*exp(-x_laguerre[j]) - w_scaled[k+n+1-j] = (node_cut_high - coord_val)*w_laguerre[j] - end - # fill in upper Gauss-Legendre points - shift = 0.5*(node_cut_high + node_max) - scale = 0.5*(node_max - node_cut_high) - @. x_scaled[k+n+1:nquad_coord] = scale*x_legendre + shift - @. w_scaled[k+n+1:nquad_coord] = scale*w_legendre - - #println("intermediate divergence") - #else # no divergences - # nquad = size(x_legendre,1) - # shift = 0.5*(node_min + node_max) - # scale = 0.5*(node_max - node_min) - # @. x_scaled[1:nquad] = scale*x_legendre + shift - # @. w_scaled[1:nquad] = scale*w_legendre - # #println("no divergence") - # nquad_coord = nquad - end - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) - return nquad_coord - end - - function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) - zero = 1.0e-6 - @. x_scaled = 0.0 - @. w_scaled = 0.0 - #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) - nquad = size(x_legendre,1) - shift = 0.5*(node_min + node_max) - scale = 0.5*(node_max - node_min) - @. x_scaled[1:nquad] = scale*x_legendre + shift - @. w_scaled[1:nquad] = scale*w_legendre - #println("x_scaled",x_scaled) - #println("w_scaled",w_scaled) - return nquad - end - - # function returns 1 if igrid = 1 or 0 if 1 < igrid <= ngrid - function ng_low(igrid,ngrid) - return floor(mk_int, (ngrid - igrid)/(ngrid - 1)) - end - # function returns 1 if igrid = ngrid or 0 if 1 =< igrid < ngrid - function ng_hi(igrid,ngrid) - return floor(mk_int, igrid/ngrid) - end - # function returns 1 for nelement >= ielement > 1, 0 for ielement =1 - function nel_low(ielement,nelement) - return floor(mk_int, (ielement - 2 + nelement)/nelement) - end - # function returns 1 for nelement > ielement >= 1, 0 for ielement =nelement - function nel_hi(ielement,nelement) - return 1- floor(mk_int, ielement/nelement) - end - - function local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids - nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) # values and indices for unprimed (field) grids - for igrid_vperp in 1:vperp.ngrid - for igrid_vpa in 1:vpa.ngrid - # get grid index for point on full grid - ivpap = vpa.igrid_full[igrid_vpa,ielement_vpa] - ivperpp = vperp.igrid_full[igrid_vperp,ielement_vperp] - # carry out integration over Lagrange polynomial at this node, on this element - for kvperp in 1:nquad_vperp - for kvpa in 1:nquad_vpa - x_kvpa = x_vpa[kvpa] - x_kvperp = x_vperp[kvperp] - w_kvperp = w_vperp[kvperp] - w_kvpa = w_vpa[kvpa] - denom = (vpa_val - x_kvpa)^2 + (vperp_val + x_kvperp)^2 - mm = min(4.0*vperp_val*x_kvperp/denom,1.0 - 1.0e-15) - #mm = 4.0*vperp_val*x_kvperp/denom/(1.0 + 10^-15) - #mm = 4.0*vperp_val*x_kvperp/denom - prefac = sqrt(denom) - ellipe_mm = ellipe(mm) - ellipk_mm = ellipk(mm) - #if mm_test > 1.0 - # println("mm: ",mm_test," ellipe: ",ellipe_mm," ellipk: ",ellipk_mm) - #end - G_elliptic_integral_factor = 2.0*ellipe_mm*prefac/pi - G1_elliptic_integral_factor = -(2.0*prefac/pi)*( (2.0 - mm)*ellipe_mm - 2.0*(1.0 - mm)*ellipk_mm )/(3.0*mm) - G2_elliptic_integral_factor = (2.0*prefac/pi)*( (7.0*mm^2 + 8.0*mm - 8.0)*ellipe_mm + 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - G3_elliptic_integral_factor = (2.0*prefac/pi)*( 8.0*(mm^2 - mm + 1.0)*ellipe_mm - 4.0*(2.0 - mm)*(1.0 - mm)*ellipk_mm )/(15.0*mm^2) - H_elliptic_integral_factor = 2.0*ellipk_mm/(pi*prefac) - H1_elliptic_integral_factor = -(2.0/(pi*prefac))*( (mm-2.0)*(ellipk_mm/mm) + (2.0*ellipe_mm/mm) ) - H2_elliptic_integral_factor = (2.0/(pi*prefac))*( (3.0*mm^2 - 8.0*mm + 8.0)*(ellipk_mm/(3.0*mm^2)) + (4.0*mm - 8.0)*ellipe_mm/(3.0*mm^2) ) - lagrange_poly_vpa = lagrange_poly(igrid_vpa,vpa_nodes,x_kvpa) - lagrange_poly_vperp = lagrange_poly(igrid_vperp,vperp_nodes,x_kvperp) - - (G_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G1_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G2_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G2_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (G3_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - G3_elliptic_integral_factor*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H1_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H1_elliptic_integral_factor*x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (H2_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - (H1_elliptic_integral_factor*vperp_val - H2_elliptic_integral_factor*x_kvperp)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - (H3_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - H_elliptic_integral_factor*(vpa_val - x_kvpa)* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - - (n_weights[ivpa,ivperp,ivpap,ivperpp] += - lagrange_poly_vpa*lagrange_poly_vperp* - x_kvperp*w_kvperp*w_kvpa*2.0/sqrt(pi)) - end - end - end - end - return nothing - end - - function loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - vperp,ielement_vperpp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in 1:ielement_vpa_low-1 - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - for ielement_vpap in ielement_vpa_low:ielement_vpa_hi - #for ielement_vpap in 1:vpa.nelement_local - # use general grid function that checks divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - end - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - for ielement_vpap in ielement_vpa_hi+1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - - end - return nothing - end - - function loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vperp grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - for ielement_vpap in 1:vpa.nelement_local - # do integration over part of the domain with no divergences - vpa_nodes = get_nodes(vpa,ielement_vpap) - vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] - nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - local_element_integration!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - nquad_vpa,ielement_vpap,vpa_nodes,vpa, - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, - x_vpa, w_vpa, x_vperp, w_vperp, - vpa_val, vperp_val, ivpa, ivperp) - - end - return nothing - end - - function loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - for ielement_vperpp in 1:ielement_vperp_low-1 - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_low:ielement_vperp_hi - - #vperp_nodes = get_nodes(vperp,ielement_vperpp) - #vperp_max = vperp_nodes[end] - #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) - loop_over_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperpp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - end - for ielement_vperpp in ielement_vperp_hi+1:vperp.nelement_local - - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - return nothing - end - - function loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - for ielement_vperpp in 1:vperp.nelement_local - vperp_nodes = get_nodes(vperp,ielement_vperpp) - vperp_max = vperp_nodes[end] - vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,nelement_vperp) - nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - loop_over_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - nquad_vperp,ielement_vperpp,vperp_nodes,vperp, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - vpa_val, vperp_val, ivpa, ivperp) - end - return nothing - end - - @serial_region begin - println("setting up GL quadrature ", Dates.format(now(), dateformat"H:MM:SS")) - end - - # get Gauss-Legendre points and weights on (-1,1) - nquad = 2*ngrid - x_legendre, w_legendre = gausslegendre(nquad) - #nlaguerre = min(9,nquad) # to prevent points to close to the boundaries - nlaguerre = nquad - x_laguerre, w_laguerre = gausslaguerre(nlaguerre) - - #x_hlaguerre, w_hlaguerre = gausslaguerre(halfnquad) - x_vpa, w_vpa = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - x_vperp, w_vperp = Array{mk_float,1}(undef,4*nquad), Array{mk_float,1}(undef,4*nquad) - - - @serial_region begin - println("beginning weights calculation ", Dates.format(now(), dateformat"H:MM:SS")) - end - - nelement_vpa, ngrid_vpa = vpa.nelement_local, vpa.ngrid - nelement_vperp, ngrid_vperp = vperp.nelement_local, vperp.ngrid - # precalculated weights, integrating over Lagrange polynomials - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - #limits where checks required to determine which divergence-safe grid is needed - igrid_vpa, ielement_vpa = vpa.igrid[ivpa], vpa.ielement[ivpa] - ielement_vpa_low = ielement_vpa - ng_low(igrid_vpa,ngrid_vpa)*nel_low(ielement_vpa,nelement_vpa) - ielement_vpa_hi = ielement_vpa + ng_hi(igrid_vpa,ngrid_vpa)*nel_hi(ielement_vpa,nelement_vpa) - #println("igrid_vpa: ielement_vpa: ielement_vpa_low: ielement_vpa_hi:", igrid_vpa," ",ielement_vpa," ",ielement_vpa_low," ",ielement_vpa_hi) - igrid_vperp, ielement_vperp = vperp.igrid[ivperp], vperp.ielement[ivperp] - ielement_vperp_low = ielement_vperp - ng_low(igrid_vperp,ngrid_vperp)*nel_low(ielement_vperp,nelement_vperp) - ielement_vperp_hi = ielement_vperp + ng_hi(igrid_vperp,ngrid_vperp)*nel_hi(ielement_vperp,nelement_vperp) - #println("igrid_vperp: ielement_vperp: ielement_vperp_low: ielement_vperp_hi:", igrid_vperp," ",ielement_vperp," ",ielement_vperp_low," ",ielement_vperp_hi) - - vperp_val = vperp.grid[ivperp] - vpa_val = vpa.grid[ivpa] - @. G_weights[ivpa,ivperp,:,:] = 0.0 - @. G1_weights[ivpa,ivperp,:,:] = 0.0 - @. G2_weights[ivpa,ivperp,:,:] = 0.0 - @. G3_weights[ivpa,ivperp,:,:] = 0.0 - @. H_weights[ivpa,ivperp,:,:] = 0.0 - @. H1_weights[ivpa,ivperp,:,:] = 0.0 - @. H2_weights[ivpa,ivperp,:,:] = 0.0 - @. H3_weights[ivpa,ivperp,:,:] = 0.0 - @. n_weights[ivpa,ivperp,:,:] = 0.0 - # loop over elements and grid points within elements on primed coordinate - loop_over_vperp_vpa_elements!(G_weights,G1_weights,G2_weights,G3_weights, - #loop_over_vperp_vpa_elements_no_divergences!(G_weights,G1_weights,G2_weights,G3_weights, - H_weights,H1_weights,H2_weights,H3_weights,n_weights, - vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids - vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids - x_vpa, w_vpa, x_vperp, w_vperp, # arrays to store points and weights for primed (source) grids - igrid_vpa, igrid_vperp, vpa_val, vperp_val, ivpa, ivperp) - end - - fkarrays = init_fokker_planck_collisions(vperp,vpa,precompute_weights=true) - G1_weights = fkarrays.G1_weights - H_weights = fkarrays.H0_weights - H1_weights = fkarrays.H1_weights - H2_weights = fkarrays.H2_weights - H3_weights = fkarrays.H3_weights - - #_block_synchronize() - begin_serial_region() - @serial_region begin - println("beginning integration ", Dates.format(now(), dateformat"H:MM:SS")) - end - - begin_vperp_vpa_region() - - # use precalculated weights to calculate Gsp using nodal values of fs - @loop_vperp_vpa ivperp ivpa begin - #for ivperp in 1:nvperp - #for ivpa in 1:nvpa - #d2Gspdvpa2[ivpa,ivperp] = 0.0 - #dGspdvperp[ivpa,ivperp] = 0.0 - #d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 - #d2Gspdvperp2[ivpa,ivperp] = 0.0 - Gsp[ivpa,ivperp] = 0.0 - Hsp[ivpa,ivperp] = 0.0 - #dHspdvpa[ivpa,ivperp] = 0.0 - #dHspdvperp[ivpa,ivperp] = 0.0 - nsp[ivpa,ivperp] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - ##d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvpa2[ivpap,ivperpp] - #d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - #dGspdvperp[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - #d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperpdvpa[ivpap,ivperpp] - ##d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpa,ivperp,ivpap,ivperpp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - Gsp[ivpa,ivperp] += G_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - Hsp[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - #dHspdvpa[ivpa,ivperp] += H_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvpa[ivpap,ivperpp] - #dHspdvperp[ivpa,ivperp] += H1_weights[ivpa,ivperp,ivpap,ivperpp]*dfspdvperp[ivpap,ivperpp] - nsp[ivpa,ivperp] += n_weights[ivpa,ivperp,ivpap,ivperpp]*fsp_in[ivpap,ivperpp] - end - end - get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp, - dfspdvpa,dfspdvperp,d2fspdvperpdvpa, - G1_weights,H_weights,H1_weights,H2_weights,H3_weights, - ivpa,ivperp,nvpa,nvperp) - #end - - (Hsp_from_Gsp[ivpa,ivperp] = 0.5*( d2Gspdvpa2[ivpa,ivperp] + - d2Gspdvperp2[ivpa,ivperp] + - (1.0/vperp.grid[ivperp])*dGspdvperp[ivpa,ivperp])) - end - - begin_vperp_region() - @loop_vperp ivperp begin - @views derivative!(vpa.scratch, Hsp_from_Gsp[:,ivperp], vpa, vpa_spectral) - @. dHspdvpa_from_Gsp[:,ivperp] = vpa.scratch - end - begin_vpa_region() - @loop_vpa ivpa begin - @views derivative!(vperp.scratch, Hsp_from_Gsp[ivpa,:], vperp, vperp_spectral) - @. dHspdvperp_from_Gsp[ivpa,:] = vperp.scratch - end - - # evaluate collsion operator - begin_vperp_vpa_region() - @loop_vperp_vpa ivperp ivpa begin - # fully expanded form - #(Cssp_numerical[ivpa,ivperp] = nussp*( d2fsdvpa2[ivpa,ivperp]*d2Gspdvpa2[ivpa,ivperp] + - # d2fsdvperp2[ivpa,ivperp]*d2Gspdvperp2[ivpa,ivperp] + - # 2.0*d2fsdvperpdvpa[ivpa,ivperp]*d2Gspdvperpdvpa[ivpa,ivperp] + - # (1.0/(vperp.grid[ivperp]^2))*dfsdvperp[ivpa,ivperp]*dGspdvperp[ivpa,ivperp] + - # 2.0*(1.0 - (ms/msp))*(dfsdvpa[ivpa,ivperp]*dHspdvpa[ivpa,ivperp] + dfsdvperp[ivpa,ivperp]*dHspdvperp[ivpa,ivperp]) + - # (8.0/sqrt(pi))*(ms/msp)*fs_in[ivpa,ivperp]*fsp_in[ivpa,ivperp]) ) - (Cssp_numerical[ivpa,ivperp] = Cssp_fully_expanded_form(nussp,ms,msp, - d2fsdvpa2[ivpa,ivperp],d2fsdvperp2[ivpa,ivperp],d2fsdvperpdvpa[ivpa,ivperp],dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp],fs_in[ivpa,ivperp], - d2Gspdvpa2[ivpa,ivperp],d2Gspdvperp2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp],dGspdvperp[ivpa,ivperp], - dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp],fsp_in[ivpa,ivperp],vperp.grid[ivperp]) ) - # collisional fluxes - ((Cflux_vpa[ivpa,ivperp],Cflux_vperp[ivpa,ivperp]) = - calculate_collisional_fluxes(fs_in[ivpa,ivperp], - dfsdvpa[ivpa,ivperp],dfsdvperp[ivpa,ivperp], - d2Gspdvpa2[ivpa,ivperp],d2Gspdvperpdvpa[ivpa,ivperp], - d2Gspdvperp2[ivpa,ivperp],dHspdvpa[ivpa,ivperp],dHspdvperp[ivpa,ivperp], - ms,msp) ) - end - if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" - @. Cssp_div_numerical = 0.0 - begin_vperp_region() - @loop_vperp ivperp begin - @views mul!(vpa.scratch,vpa_spectral.S_matrix,Cflux_vpa[:,ivperp]) - gausslegendre_mass_matrix_solve!(vpa.scratch2,vpa.scratch,vpa_spectral) - Cssp_div_numerical[:,ivperp] += vpa.scratch2 - end - begin_vpa_region() - @loop_vpa ivpa begin - @views mul!(vperp.scratch,vperp_spectral.S_matrix,Cflux_vperp[ivpa,:]) - gausslegendre_mass_matrix_solve!(vperp.scratch2,vperp.scratch,vperp_spectral) - Cssp_div_numerical[ivpa,:] += vperp.scratch2 - end - @. Cssp_div_numerical *= nussp - end - - plot_H = false #true - plot_dHdvpa = false #true - plot_dHdvperp = false #true - plot_d2Gdvperp2 = false #true - plot_d2Gdvperpdvpa = false #true - plot_dGdvperp = false #true - plot_d2Gdvpa2 = false #true - plot_G = false #true - plot_C = false #true - plot_n = false #true - - begin_serial_region() - @serial_region begin - println("finished integration ", Dates.format(now(), dateformat"H:MM:SS")) - @. n_err = abs(nsp - denssp) - max_n_err, max_n_index = findmax(n_err) - println("max_n_err: ",max_n_err," ",max_n_index) - println("spot check n_err: ",n_err[end,end], " nsp: ",nsp[end,end]) - L2_n_err = L2norm_vspace(n_err,vpa,vperp) - println("L2_n_err: ",L2_n_err) - @. Cssp_err = abs(Cssp_numerical - Cssp_Maxwell) - max_C_err, max_C_index = findmax(Cssp_err) - max_C_Maxwell_val = maximum(Cssp_Maxwell) - max_C_numerical_val = maximum(Cssp_numerical) - println("max_C_err: ",max_C_err," ",max_C_index) - L2_C_err = L2norm_vspace(Cssp_err,vpa,vperp) - println("L2_C_err: ",L2_C_err) - println("max_C_Maxwell_val: ",max_C_Maxwell_val) - println("max_C_numerical_val: ",max_C_numerical_val) - if vpa.discretization == "gausslegendre_pseudospectral" && vperp.discretization == "gausslegendre_pseudospectral" - @. Cssp_div_err = abs(Cssp_div_numerical - Cssp_Maxwell) - max_C_div_err = maximum(Cssp_div_err) - println("max_C_div_err: ",max_C_div_err) - end - @. Cflux_vpa_err = abs(Cflux_vpa - Cflux_vpa_Maxwell) - max_Cflux_vpa_err = maximum(Cflux_vpa_err) - println("max_Cflux_vpa_err: ",max_Cflux_vpa_err) - @. Cflux_vperp_err = abs(Cflux_vperp - Cflux_vperp_Maxwell) - max_Cflux_vperp_err = maximum(Cflux_vperp_err) - println("max_Cflux_vperp_err: ",max_Cflux_vperp_err) - @. H_err = abs(Hsp_from_Gsp - H_Maxwell) - max_H_err = maximum(H_err) - println("max_H_from_G_err: ",max_H_err) - @. dHdvperp_err = abs(dHspdvperp_from_Gsp - dHdvperp_Maxwell) - max_dHdvperp_err = maximum(dHdvperp_err) - println("max_dHdvperp_err (from G): ",max_dHdvperp_err) - @. dHdvpa_err = abs(dHspdvpa_from_Gsp - dHdvpa_Maxwell) - max_dHdvpa_err = maximum(dHdvpa_err) - println("max_dHdvpa_err (from G): ",max_dHdvpa_err) - @. H_err = abs(Hsp - H_Maxwell) - max_H_err, max_H_index = findmax(H_err) - println("max_H_err: ",max_H_err," ",max_H_index) - println("spot check H_err: ",H_err[end,end], " H: ",Hsp[end,end]) - L2_H_err = L2norm_vspace(H_err,vpa,vperp) - println("L2_H_err: ",L2_H_err) - @. dHdvperp_err = abs(dHspdvperp - dHdvperp_Maxwell) - max_dHdvperp_err, max_dHdvperp_index = findmax(dHdvperp_err) - println("max_dHdvperp_err: ",max_dHdvperp_err," ",max_dHdvperp_index) - println("spot check dHdvperp_err: ",dHdvperp_err[end,end], " dHdvperp: ",dHspdvperp[end,end]) - L2_dHdvperp_err = L2norm_vspace(dHdvperp_err,vpa,vperp) - println("L2_dHdvperp_err: ",L2_dHdvperp_err) - @. dHdvpa_err = abs(dHspdvpa - dHdvpa_Maxwell) - max_dHdvpa_err, max_dHdvpa_index = findmax(dHdvpa_err) - println("max_dHdvpa_err: ",max_dHdvpa_err," ",max_dHdvpa_index) - println("spot check dHdvpa_err: ",dHdvpa_err[end,end], " dHdvpa: ",dHspdvpa[end,end]) - L2_dHdvpa_err = L2norm_vspace(dHdvpa_err,vpa,vperp) - println("L2_dHdvpa_err: ",L2_dHdvpa_err) - - if plot_n - @views heatmap(vperp.grid, vpa.grid, nsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_n_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, n_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_n_err.pdf") - savefig(outfile) - end - if plot_C - @views heatmap(vperp.grid, vpa.grid, Cssp_numerical[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Cssp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Cssp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_C_err.pdf") - savefig(outfile) - end - if plot_H - @views heatmap(vperp.grid, vpa.grid, Hsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Hsp_from_Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_from_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, H_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_H_err.pdf") - savefig(outfile) - end - if plot_dHdvpa - @views heatmap(vperp.grid, vpa.grid, dHspdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvpa_err.pdf") - savefig(outfile) - end - if plot_dHdvperp - @views heatmap(vperp.grid, vpa.grid, dHspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dHdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dHdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvperp2_err = abs(d2Gspdvperp2 - d2Gdvperp2_Maxwell) - max_d2Gdvperp2_err, max_d2Gdvperp2_index = findmax(d2Gdvperp2_err) - println("max_d2Gdvperp2_err: ",max_d2Gdvperp2_err," ",max_d2Gdvperp2_index) - println("spot check d2Gdvperp2_err: ",d2Gdvperp2_err[end,end], " d2Gdvperp2: ",d2Gspdvperp2[end,end]) - L2_d2Gdvperp2_err = L2norm_vspace(d2Gdvperp2_err,vpa,vperp) - println("L2_d2Gdvperp2_err: ",L2_d2Gdvperp2_err) - if plot_d2Gdvperp2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperp2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperp2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperp2_err.pdf") - savefig(outfile) - end - @. d2Gdvperpdvpa_err = abs(d2Gspdvperpdvpa - d2Gdvperpdvpa_Maxwell) - max_d2Gdvperpdvpa_err, max_d2Gdvperpdvpa_index = findmax(d2Gdvperpdvpa_err) - println("max_d2Gdvperpdvpa_err: ",max_d2Gdvperpdvpa_err," ",max_d2Gdvperpdvpa_index) - println("spot check d2Gdvperpdpva_err: ",d2Gdvperpdvpa_err[end,end], " d2Gdvperpdvpa: ",d2Gspdvperpdvpa[end,end]) - L2_d2Gdvperpdvpa_err = L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp) - println("L2_d2Gdvperpdvpa_err: ",L2_d2Gdvperpdvpa_err) - if plot_d2Gdvperpdvpa - @views heatmap(vperp.grid, vpa.grid, d2Gspdvperpdvpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvperpdvpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvperpdvpa_err.pdf") - savefig(outfile) - end - @. dGdvperp_err = abs(dGspdvperp - dGdvperp_Maxwell) - max_dGdvperp_err, max_dGdvperp_index = findmax(dGdvperp_err) - println("max_dGdvperp_err: ",max_dGdvperp_err," ",max_dGdvperp_index) - println("spot check dGdvperp_err: ",dGdvperp_err[end,end], " dGdvperp: ",dGspdvperp[end,end]) - L2_dGdvperp_err = L2norm_vspace(dGdvperp_err,vpa,vperp) - println("L2_dGdvperp_err: ",L2_dGdvperp_err) - if plot_dGdvperp - @views heatmap(vperp.grid, vpa.grid, dGspdvperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, dGdvperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_dGdvperp_err.pdf") - savefig(outfile) - end - @. d2Gdvpa2_err = abs(d2Gspdvpa2 - d2Gdvpa2_Maxwell) - max_d2Gdvpa2_err, max_d2Gdvpa2_index = findmax(d2Gdvpa2_err) - println("max_d2Gdvpa2_err: ",max_d2Gdvpa2_err," ",max_d2Gdvpa2_index) - println("spot check d2Gdvpa2_err: ",d2Gdvpa2_err[end,end], " d2Gdvpa2: ",d2Gspdvpa2[end,end]) - L2_d2Gdvpa2_err = L2norm_vspace(d2Gdvpa2_err,vpa,vperp) - println("L2_d2Gdvpa2_err: ",L2_d2Gdvpa2_err) - if plot_d2Gdvpa2 - @views heatmap(vperp.grid, vpa.grid, d2Gspdvpa2[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, d2Gdvpa2_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_d2Gdvpa2_err.pdf") - savefig(outfile) - end - @. G_err = abs(Gsp - G_Maxwell) - max_G_err, max_G_index = findmax(G_err) - println("max_G_err: ",max_G_err," ",max_G_index) - println("spot check G_err: ",G_err[end,end], " G: ",Gsp[end,end]) - L2_G_err = L2norm_vspace(G_err,vpa,vperp) - println("L2_G_err: ",L2_G_err) - if plot_G - @views heatmap(vperp.grid, vpa.grid, Gsp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_lagrange.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, G_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_G_err.pdf") - savefig(outfile) - end - end - _block_synchronize() - if standalone - finalize_comms!() - end - #println(maximum(G_err), maximum(H_err), maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err)) - (results = (maximum(Cssp_err), maximum(Cflux_vpa_err), maximum(Cflux_vperp_err), maximum(G_err), maximum(H_err), - maximum(dHdvpa_err), maximum(dHdvperp_err), maximum(d2Gdvperp2_err), maximum(d2Gdvpa2_err), maximum(d2Gdvperpdvpa_err), maximum(dGdvperp_err), - L2norm_vspace(Cssp_err,vpa,vperp), L2norm_vspace(G_err,vpa,vperp), L2norm_vspace(H_err,vpa,vperp), L2norm_vspace(dHdvpa_err,vpa,vperp), - L2norm_vspace(dHdvperp_err,vpa,vperp), L2norm_vspace(d2Gdvperp2_err,vpa,vperp), L2norm_vspace(d2Gdvpa2_err,vpa,vperp), - L2norm_vspace(d2Gdvperpdvpa_err,vpa,vperp), L2norm_vspace(dGdvperp_err,vpa,vperp), - maximum(dfsdvpa_err), maximum(dfsdvperp_err), maximum(d2fsdvpa2_err), maximum(d2fsdvperpdvpa_err), maximum(d2fsdvperp2_err), - maximum(dfspdvperp_err), maximum(d2fspdvpa2_err), maximum(d2fspdvperpdvpa_err), maximum(d2fspdvperp2_err), - maximum(n_err), L2norm_vspace(n_err,vpa,vperp) )) - return results - end - - function test_collision_operator(nelement,ngrid) - - vpa, vperp, vpa_spectral, vperp_spectral = init_grids(nelement,ngrid) - # set up necessary inputs for collision operator functions - nvperp = vperp.n - nvpa = vpa.n - - cfreqssp = 1.0 - ms = 1.0 - msp = 1.0 - - - Cssp = Array{mk_float,2}(undef,nvpa,nvperp) - Cssp_err = Array{mk_float,2}(undef,nvpa,nvperp) - - Gam_vperp_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vperp_err = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vperp_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_err = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_GMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_HMaxwell = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_Gerr = Array{mk_float,2}(undef,nvpa,nvperp) - #Gam_vpa_Herr = Array{mk_float,2}(undef,nvpa,nvperp) - - d2Gdvpa2 = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperpdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - d2Gdvperp2 = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvpa = Array{mk_float,2}(undef,nvpa,nvperp) - dHdvperp = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vpa_Maxwell = Array{mk_float,2}(undef,nvpa,nvperp) - Gam_vperp = Array{mk_float,2}(undef,nvpa,nvperp) - dfns = Array{mk_float,2}(undef,nvpa,nvperp) - dfnsp = Array{mk_float,2}(undef,nvpa,nvperp) - pdf_buffer_1 = Array{mk_float,2}(undef,nvpa,nvperp) - pdf_buffer_2 = Array{mk_float,2}(undef,nvpa,nvperp) - n_ion_species = 1 - dens_in = Array{mk_float,1}(undef,n_ion_species) - upar_in = Array{mk_float,1}(undef,n_ion_species) - vths_in = Array{mk_float,1}(undef,n_ion_species) - densp_in = Array{mk_float,1}(undef,n_ion_species) - uparp_in = Array{mk_float,1}(undef,n_ion_species) - vthsp_in = Array{mk_float,1}(undef,n_ion_species) - - # 2D isotropic Maxwellian test - # assign a known isotropic Maxwellian distribution in normalised units - # first argument (derivatives) - dens = 1.0#3.0/4.0 - upar = 2.0/3.0 - ppar = 2.0/3.0 - pperp = 2.0/3.0 - pres = get_pressure(ppar,pperp) - mi = 1.0 - vths = get_vth(pres,dens,mi) - # second argument (potentials) - densp = 1.0 #2.0#3.0/4.0 - uparp = 2.0/3.0#1.0/3.0#3.0/4.0 - pparp = 2.0/3.0#4.0/3.0 - pperpp = 2.0/3.0#4.0/3.0 - presp = get_pressure(pparp,pperpp) - mip = 1.0#1.5 - vthsp = get_vth(presp,densp,mip) - for ivperp in 1:vperp.n - for ivpa in 1:vpa.n - vpa_val = vpa.grid[ivpa] - vperp_val = vperp.grid[ivperp] - dfns[ivpa,ivperp] = (dens/vths^3)*exp( - ((vpa_val-upar)^2 + vperp_val^2)/vths^2 ) - dfnsp[ivpa,ivperp] = (densp/vthsp^3)*exp( - ((vpa_val-uparp)^2 + vperp_val^2)/vthsp^2 ) - end - end - pdf_in = dfns - dens_in[1] = get_density(dfns,vpa,vperp) - upar_in[1] = get_upar(dfns,vpa,vperp,dens_in[1]) - ppar_in = get_ppar(dfns,vpa,vperp,upar_in[1],mi) - pperp_in = get_pperp(dfns,vpa,vperp,mi) - pres_in = pressure(ppar_in,pperp_in) - vths_in[1] = get_vth(pres_in,dens_in[1],mi) - - densp_in[1] = get_density(dfnsp,vpa,vperp) - uparp_in[1] = get_upar(dfnsp,vpa,vperp,densp_in[1]) - pparp_in = get_ppar(dfnsp,vpa,vperp,uparp_in[1],mip) - pperpp_in = get_pperp(dfnsp,vpa,vperp,mip) - presp_in = pressure(pparp_in,pperpp_in) - vthsp_in[1] = get_vth(presp_in,densp_in[1],mip) - - println("Isotropic 2D Maxwellian: first argument (derivatives)") - println("dens_test: ", dens_in[1], " dens: ", dens, " error: ", abs(dens_in[1]-dens)) - println("upar_test: ", upar_in[1], " upar: ", upar, " error: ", abs(upar_in[1]-upar)) - println("ppar_test: ", ppar_in, " ppar: ", ppar, " error: ", abs(ppar_in-ppar)) - println("pperp_test: ", pperp_in, " pperp: ", pperp, " error: ", abs(pperp_in-pperp)) - println("vth_test: ", vths_in[1], " vth: ", vths, " error: ", abs(vths_in[1]-vths)) - println("Isotropic 2D Maxwellian: second argument (potentials)") - println("dens_test: ", densp_in[1], " dens: ", densp, " error: ", abs(densp_in[1]-densp)) - println("upar_test: ", uparp_in[1], " upar: ", uparp, " error: ", abs(uparp_in[1]-uparp)) - println("ppar_test: ", pparp_in, " ppar: ", pparp, " error: ", abs(pparp_in-pparp)) - println("pperp_test: ", pperpp_in, " pperp: ", pperpp, " error: ", abs(pperpp_in-pperpp)) - println("vth_test: ", vthsp_in[1], " vth: ", vthsp, " error: ", abs(vthsp_in[1]-vthsp)) - - - for ivperp in 1:nvperp - for ivpa in 1:nvpa - Gam_vpa_Maxwell[ivpa,ivperp] = Cflux_vpa_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], - mip,densp_in[1],uparp_in[1],vthsp_in[1], - vpa,vperp,ivpa,ivperp) - Gam_vperp_Maxwell[ivpa,ivperp] = Cflux_vperp_Maxwellian_inputs(mi,dens_in[1],upar_in[1],vths_in[1], - mip,densp_in[1],uparp_in[1],vthsp_in[1], - vpa,vperp,ivpa,ivperp) - end - end - - # d F / d vpa - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, pdf_in[:,ivperp], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp] = vpa.scratch - end - # d F / d vperp - for ivpa in 1:nvpa - @views derivative!(vperp.scratch, pdf_in[ivpa,:], vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:] = vperp.scratch - end - - for ivperp in 1:nvperp - for ivpa in 1:nvpa - ## evaluate the collision operator with analytically computed G & H from a shifted Maxwellian - ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, - Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(densp_in[:], - uparp_in[:],vthsp_in[:],vpa,vperp,ivpa,ivperp,n_ion_species) ) - - # now form the collisional fluxes at this s,z,r - ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp], - pdf_buffer_1[ivpa,ivperp],pdf_buffer_2[ivpa,ivperp], - Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - mi,mip) ) - - - d2Gdvpa2[ivpa,ivperp] = Rosenbluth_d2Gdvpa2 - d2Gdvperpdvpa[ivpa,ivperp] = Rosenbluth_d2Gdvperpdvpa - d2Gdvperp2[ivpa,ivperp] = Rosenbluth_d2Gdvperp2 - dHdvpa[ivpa,ivperp] = Rosenbluth_dHdvpa - dHdvperp[ivpa,ivperp] = Rosenbluth_dHdvperp - Gam_vpa[ivpa,ivperp] = Cflux_vpa - Gam_vperp[ivpa,ivperp] = Cflux_vperp - end - end - - @. Gam_vpa_err = abs(Gam_vpa - Gam_vpa_Maxwell) - @. Gam_vperp_err = abs(Gam_vperp - Gam_vperp_Maxwell) - max_Gam_vpa_err = maximum(Gam_vpa_err) - max_Gam_vperp_err = maximum(Gam_vperp_err) - println("max(Gam_vpa_err): ",max_Gam_vpa_err) - println("max(Gam_vperp_err): ",max_Gam_vperp_err) - - # d Gam_|| / d vpa - for ivperp in 1:nvperp - @views derivative!(vpa.scratch, Gam_vpa[:,ivperp], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp] = vpa.scratch - end - # (1/vperp) d vperp Gam_perp / d vperp - for ivpa in 1:nvpa - @views @. vperp.scratch2 = vperp.grid*Gam_vperp[ivpa,:] - @views derivative!(vperp.scratch, vperp.scratch2, vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:] = vperp.scratch/vperp.grid - end - - @. Cssp = pdf_buffer_1 + pdf_buffer_2 - @. Cssp_err = abs(Cssp) - max_Cssp_err = maximum(Cssp_err) - zero = 1.0e-6 - if ( abs(dens - densp) > zero || abs(upar - uparp) > zero || abs(vths - vthsp) > zero) - println("Cssp test not supported for F_Ms /= F_Ms', ignore result") - end - println("max(Cssp_err): ",max_Cssp_err) - - - if max_Gam_vpa_err > zero && false - @views heatmap(vperp.grid, vpa.grid, Cssp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Cssp.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vpa_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vpa_err.pdf") - savefig(outfile) - end - if max_Gam_vperp_err > zero && false - @views heatmap(vperp.grid, vpa.grid, Gam_vperp[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vperp_Maxwell[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp_Maxwell.pdf") - savefig(outfile) - @views heatmap(vperp.grid, vpa.grid, Gam_vperp_err[:,:], xlabel=L"v_{\perp}", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - windowsize = (360,240), margin = 15pt) - outfile = string("fkpl_Gam_vperp_err.pdf") - savefig(outfile) - end - return max_Gam_vpa_err, max_Gam_vperp_err, max_Cssp_err - end - - if test_Rosenbluth_integrals - ngrid = 8 - nscan = 5 - nelement_list = Int[2, 4, 8, 16, 32] - max_G_err = Array{mk_float,1}(undef,nscan) - max_H_err = Array{mk_float,1}(undef,nscan) - max_H_check_err = Array{mk_float,1}(undef,nscan) - max_dHdvpa_err = Array{mk_float,1}(undef,nscan) - max_dHdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - expected_label = L"(1/N_{el})^{n_g - 1}" - - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - ((max_G_err[iscan], max_H_err[iscan], - max_H_check_err[iscan], max_dHdvpa_err[iscan], - max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], - max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan]) - = test_Rosenbluth_potentials(nelement,ngrid)) - end - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - Glabel = L"\epsilon(G)" - Hlabel = L"\epsilon(H)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - plot(nelement_list, [max_G_err,max_H_check_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err, expected], - xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_analytical_test.pdf" - savefig(outfile) - println(outfile) - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - ((max_G_err[iscan], max_H_err[iscan], - max_H_check_err[iscan], max_dHdvpa_err[iscan], - max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], - max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan]) - = test_Rosenbluth_potentials(nelement,ngrid,numerical_G = true)) - end - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - Glabel = L"\epsilon(G)" - Hlabel = L"\epsilon(H)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - plot(nelement_list, [max_G_err,max_H_check_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err, expected], - xlabel=xlabel, label=[Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_coeffs_numerical_test.pdf" - savefig(outfile) - println(outfile) - end - - if test_collision_operator_fluxes - ngrid = 8 - nscan = 5 - nelement_list = Int[2, 4, 8, 16, 32] - max_Gam_vpa_err = Array{mk_float,1}(undef,nscan) - max_Gam_vperp_err = Array{mk_float,1}(undef,nscan) - max_Cssp_err = Array{mk_float,1}(undef,nscan) - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - - for iscan in 1:nscan - ((max_Gam_vpa_err[iscan], max_Gam_vperp_err[iscan], max_Cssp_err[iscan]) - = test_collision_operator(nelement_list[iscan],ngrid)) - end - fontsize = 10 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - Gam_vpa_label = L"\epsilon(\Gamma_{\|\|})" - Gam_vperp_label = L"\epsilon(\Gamma_{\perp})" - Cssp_err_label = L"\epsilon(C)" - expected_label = L"(1/N_{el})^{n_g - 1}" - plot(nelement_list, [max_Gam_vpa_err,max_Gam_vperp_err, expected], - xlabel=xlabel, label=[Gam_vpa_label Gam_vperp_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - outfile = "fkpl_fluxes_test.pdf" - savefig(outfile) - println(outfile) - end - - if test_Lagrange_integral - ngrid = 9 - nelement = 4 - test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=true) - end - if test_Lagrange_integral_scan - initialize_comms!() - ngrid = 9 - #nelement_list = Int[2, 4, 8, 16, 32] - #nelement_list = Int[2, 4, 8, 16] - #nelement_list = Int[50, 100, 200] - nelement_list = Int[4] - nscan = size(nelement_list,1) - max_C_err = Array{mk_float,1}(undef,nscan) - max_Gvpa_err = Array{mk_float,1}(undef,nscan) - max_Gvperp_err = Array{mk_float,1}(undef,nscan) - max_G_err = Array{mk_float,1}(undef,nscan) - max_H_err = Array{mk_float,1}(undef,nscan) - max_dHdvpa_err = Array{mk_float,1}(undef,nscan) - max_dHdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_dGdvperp_err = Array{mk_float,1}(undef,nscan) - max_dfsdvpa_err = Array{mk_float,1}(undef,nscan) - max_dfsdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_d2fsdvperp2_err = Array{mk_float,1}(undef,nscan) - max_dfspdvperp_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvpa2_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - max_d2fspdvperp2_err = Array{mk_float,1}(undef,nscan) - L2_C_err = Array{mk_float,1}(undef,nscan) - L2_G_err = Array{mk_float,1}(undef,nscan) - L2_H_err = Array{mk_float,1}(undef,nscan) - L2_dHdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dHdvperp_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperp2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvpa2_err = Array{mk_float,1}(undef,nscan) - L2_d2Gdvperpdvpa_err = Array{mk_float,1}(undef,nscan) - L2_dGdvperp_err = Array{mk_float,1}(undef,nscan) - max_n_err = Array{mk_float,1}(undef,nscan) - L2_n_err = Array{mk_float,1}(undef,nscan) - - expected = Array{mk_float,1}(undef,nscan) - expected_nelement_scaling!(expected,nelement_list,ngrid,nscan) - expected_integral = Array{mk_float,1}(undef,nscan) - expected_nelement_integral_scaling!(expected_integral,nelement_list,ngrid,nscan) - - expected_label = L"(1/N_{el})^{n_g - 1}" - expected_integral_label = L"(1/N_{el})^{n_g +1}" - - for iscan in 1:nscan - local nelement = nelement_list[iscan] - ((max_C_err[iscan], max_Gvpa_err[iscan], max_Gvperp_err[iscan], - max_G_err[iscan], max_H_err[iscan], - max_dHdvpa_err[iscan], - max_dHdvperp_err[iscan], max_d2Gdvperp2_err[iscan], - max_d2Gdvpa2_err[iscan], max_d2Gdvperpdvpa_err[iscan], - max_dGdvperp_err[iscan], L2_C_err[iscan], - L2_G_err[iscan], L2_H_err[iscan], L2_dHdvpa_err[iscan], - L2_dHdvperp_err[iscan], L2_d2Gdvperp2_err[iscan], L2_d2Gdvpa2_err[iscan], - L2_d2Gdvperpdvpa_err[iscan], L2_dGdvperp_err[iscan], max_dfsdvpa_err[iscan], - max_dfsdvperp_err[iscan], max_d2fsdvpa2_err[iscan], - max_d2fsdvperpdvpa_err[iscan], max_d2fsdvperp2_err[iscan], - max_dfspdvperp_err[iscan], max_d2fspdvpa2_err[iscan], - max_d2fspdvperpdvpa_err[iscan], max_d2fspdvperp2_err[iscan], - max_n_err[iscan], L2_n_err[iscan]) - = test_Lagrange_Rosenbluth_potentials(ngrid,nelement,standalone=false)) - end - if global_rank[]==0 - fontsize = 8 - ytick_sequence = Array([1.0e-13,1.0e-12,1.0e-11,1.0e-10,1.0e-9,1.0e-8,1.0e-7,1.0e-6,1.0e-5,1.0e-4,1.0e-3,1.0e-2,1.0e-1,1.0e-0,1.0e1]) - xlabel = L"N_{element}" - nlabel = L"\epsilon(n)" - Clabel = L"\epsilon(C)" - Gvpalabel = L"\epsilon(\Gamma_{\|\|})" - Gvperplabel = L"\epsilon(\Gamma_{\perp})" - Glabel = L"\epsilon(G)" - Hlabel = L"\epsilon(H)" - dHdvpalabel = L"\epsilon(dH/d v_{\|\|})" - dHdvperplabel = L"\epsilon(dH/d v_{\perp})" - d2Gdvperp2label = L"\epsilon(d^2G/d v_{\perp}^2)" - d2Gdvpa2label = L"\epsilon(d^2G/d v_{\|\|}^2)" - d2Gdvperpdvpalabel = L"\epsilon(d^2G/d v_{\perp} d v_{\|\|})" - dGdvperplabel = L"\epsilon(dG/d v_{\perp})" - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - plot(nelement_list, [L2_C_err,L2_G_err,L2_H_err,L2_dHdvpa_err,L2_dHdvperp_err,L2_d2Gdvperp2_err,L2_d2Gdvpa2_err,L2_d2Gdvperpdvpa_err,L2_dGdvperp_err,L2_n_err,expected,expected_integral], - xlabel=xlabel, label=[Clabel Glabel Hlabel dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel nlabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_coeffs_L2_error_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - plot(nelement_list, [max_C_err,max_Gvpa_err,max_Gvperp_err,max_G_err,max_H_err,max_n_err,expected,expected_integral], - xlabel=xlabel, label=[Clabel Gvpalabel Gvperplabel Glabel Hlabel nlabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_potentials_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - #println(max_G_err,max_H_err,max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected) - plot(nelement_list, [max_dHdvpa_err,max_dHdvperp_err,max_d2Gdvperp2_err,max_d2Gdvpa2_err,max_d2Gdvperpdvpa_err,max_dGdvperp_err, expected, expected_integral], - xlabel=xlabel, label=[dHdvpalabel dHdvperplabel d2Gdvperp2label d2Gdvpa2label d2Gdvperpdvpalabel dGdvperplabel expected_label expected_integral_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_essential_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - - dfsdvpa_label = L"\epsilon(d F_s / d v_{\|\|})" - dfsdvperp_label = L"\epsilon(d F_s /d v_{\perp})" - d2fsdvpa2_label = L"\epsilon(d^2 F_s /d v_{\|\|}^2)" - d2fsdvperpdvpa_label = L"\epsilon(d^2 F_s /d v_{\perp}d v_{\|\|})" - d2fsdvperp2_label = L"\epsilon(d^2 F_s/ d v_{\perp}^2)" - plot(nelement_list, [max_dfsdvpa_err,max_dfsdvperp_err,max_d2fsdvpa2_err,max_d2fsdvperpdvpa_err,max_d2fsdvperp2_err,expected], - xlabel=xlabel, label=[dfsdvpa_label dfsdvperp_label d2fsdvpa2_label d2fsdvperpdvpa_label d2fsdvperp2_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_fs_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - - dfspdvperp_label = L"\epsilon(d F_{s^\prime} /d v_{\perp})" - d2fspdvpa2_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\|\|}^2)" - d2fspdvperpdvpa_label = L"\epsilon(d^2 F_{s^\prime} /d v_{\perp}d v_{\|\|})" - d2fspdvperp2_label = L"\epsilon(d^2 F_{s^\prime}/ d v_{\perp}^2)" - plot(nelement_list, [max_dfspdvperp_err,max_d2fspdvpa2_err,max_d2fspdvperpdvpa_err,max_d2fspdvperp2_err,expected], - xlabel=xlabel, label=[dfspdvperp_label d2fspdvpa2_label d2fspdvperpdvpa_label d2fspdvperp2_label expected_label], ylabel="", - shape =:circle, xscale=:log10, yscale=:log10, xticks = (nelement_list, nelement_list), yticks = (ytick_sequence, ytick_sequence), markersize = 5, linewidth=2, - xtickfontsize = fontsize, xguidefontsize = fontsize, ytickfontsize = fontsize, yguidefontsize = fontsize, legendfontsize = fontsize, - foreground_color_legend = nothing, background_color_legend = nothing, legend=:bottomleft) - #outfile = "fkpl_coeffs_numerical_lagrange_integration_test_ngrid_"*string(ngrid)*".pdf" - outfile = "fkpl_fsp_numerical_test_ngrid_"*string(ngrid)*"_GLL.pdf" - savefig(outfile) - println(outfile) - end - finalize_comms!() - end - ## evaluate the collision operator with numerically computed G & H - #println("TEST: Css'[F_M,F_M] with numerical G[F_M] & H[F_M]") - #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, - # mu, vpa, mu_spectral, vpa_spectral, Bmag, fkarrays) - # - #zero = 1.0e1 - #Cssp_err = maximum(abs.(Cssp)) - #if Cssp_err > zero - # #println("ERROR: C_ss'[F_Ms,F_Ms] /= 0") - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if maximum(abs.(Cssp[ivpa,imu])) > zero - # # # # ###print("ivpa: ",ivpa," imu: ",imu," C: ") - # # # # ##println("ivpa: ",ivpa," imu: ",imu," C: ", Cssp[ivpa,imu]) - # # # # ##println(" imu: ",imu," C[:,imu]:") - # # # # ###@printf("%.1e", Cssp[ivpa,imu]) - # # # # ###println("") - # # # #end - # # #end - # #end - #end - #println("max(abs(C_ss'[F_Ms,F_Ms])): ", Cssp_err) - # - # - ## evaluate the collision operator with analytically computed G & H - #zero = 1.0e-6 - #println("TEST: Css'[F_M,F_M] with analytical G[F_M] & H[F_M]") - #@views @. fkarrays.Rosenbluth_G = G_Maxwell - #@views @. fkarrays.Rosenbluth_H = H_Maxwell - #@views evaluate_RMJ_collision_operator!(Cssp, fs_in, fsp_in, ms, msp, cfreqssp, - # mu, vpa, mu_spectral, vpa_spectral, Bmag, fkarrays) - # - #for imu in 1:mu.n - # #for ivpa in 1:vpa.n - # # #Gam_vpa_Maxwell[ivpa,imu] = Gamma_vpa_Maxwellian(Bmag,vpa,mu,ivpa,imu) - # # #Gam_mu_Maxwell[ivpa,imu] = Gamma_mu_Maxwellian(Bmag,vpa,mu,ivpa,imu) - # # # - # # #Gam_vpa_err[ivpa,imu] = abs(fkarrays.Gamma_vpa[ivpa,imu] - Gam_vpa_Maxwell[ivpa,imu]) - # # #Gam_mu_err[ivpa,imu] = abs(fkarrays.Gamma_mu[ivpa,imu] - Gam_mu_Maxwell[ivpa,imu]) - # #end - #end - #max_Gam_vpa_err = maximum(Gam_vpa_err) - #println("max(abs(Gamma_vpa[F_Ms,F_Ms])): ", max_Gam_vpa_err) - #if max_Gam_vpa_err > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_vpa_err[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_err: ",Gam_vpa_err[ivpa,imu]," Gam_vpa_num: ",fkarrays.Gamma_vpa[ivpa,imu]," Gam_vpa_Maxwell: ",Gam_vpa_Maxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_Maxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_Maxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_num.pdf") - # # # #savefig(outfile) - #end - #max_Gam_mu_err = maximum(Gam_mu_err) - #println("max(abs(Gamma_mu[F_Ms,F_Ms])): ", max_Gam_mu_err) - #if max_Gam_mu_err > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_mu_err[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_err: ",Gam_mu_err[ivpa,imu]," Gam_mu_num: ",fkarrays.Gamma_mu[ivpa,imu]," Gam_mu_Maxwell: ",Gam_mu_Maxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_mu_Maxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_Maxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_num.pdf") - # # # #savefig(outfile) - #end - # - #for imu in 1:mu.n - # #for ivpa in 1:vpa.n - # # #Gam_vpa_GMaxwell[ivpa,imu] = Gamma_vpa_GMaxwellian(Bmag,vpa,mu,ivpa,imu) - # # #Gam_mu_GMaxwell[ivpa,imu] = Gamma_mu_GMaxwellian(Bmag,vpa,mu,ivpa,imu) - # # # - # # #Gam_vpa_Gerr[ivpa,imu] = abs(fkarrays.Gamma_vpa_G[ivpa,imu] - Gam_vpa_GMaxwell[ivpa,imu]) - # # #Gam_mu_Gerr[ivpa,imu] = abs(fkarrays.Gamma_mu_G[ivpa,imu] - Gam_mu_GMaxwell[ivpa,imu]) - # #end - #end - #max_Gam_vpa_Gerr = maximum(Gam_vpa_Gerr) - #println("max(abs(Gamma_vpa_G[F_Ms,F_Ms])): ", max_Gam_vpa_Gerr) - #if max_Gam_vpa_Gerr > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_vpa_Gerr[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_Gerr: ",Gam_vpa_Gerr[ivpa,imu]," Gam_vpa_G_num: ",fkarrays.Gamma_vpa_G[ivpa,imu]," Gam_vpa_GMaxwell: ",Gam_vpa_GMaxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_GMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_GMaxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa_G[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_G_num.pdf") - # # # #savefig(outfile) - #end - #max_Gam_mu_Gerr = maximum(Gam_mu_Gerr) - #println("max(abs(Gamma_mu_G[F_Ms,F_Ms])): ", max_Gam_mu_Gerr) - #if max_Gam_mu_Gerr > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_mu_Gerr[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_Gerr: ",Gam_mu_Gerr[ivpa,imu]," Gam_mu_G_num: ",fkarrays.Gamma_mu_G[ivpa,imu]," Gam_mu_GMaxwell: ",Gam_mu_GMaxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_mu_GMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_GMaxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu_G[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_G_num.pdf") - # # # #savefig(outfile) - #end - # - #for imu in 1:mu.n - # #for ivpa in 1:vpa.n - # # #Gam_vpa_HMaxwell[ivpa,imu] = Gamma_vpa_HMaxwellian(Bmag,vpa,mu,ivpa,imu) - # # #Gam_mu_HMaxwell[ivpa,imu] = Gamma_mu_HMaxwellian(Bmag,vpa,mu,ivpa,imu) - # # # - # # #Gam_vpa_Herr[ivpa,imu] = abs(fkarrays.Gamma_vpa_H[ivpa,imu] - Gam_vpa_HMaxwell[ivpa,imu]) - # # #Gam_mu_Herr[ivpa,imu] = abs(fkarrays.Gamma_mu_H[ivpa,imu] - Gam_mu_HMaxwell[ivpa,imu]) - # #end - #end - #max_Gam_vpa_Herr = maximum(Gam_vpa_Herr) - #println("max(abs(Gamma_vpa_H[F_Ms,F_Ms])): ", max_Gam_vpa_Herr) - #if max_Gam_vpa_Herr > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_vpa_Herr[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_vpa_Herr: ",Gam_vpa_Herr[ivpa,imu]," Gam_vpa_H_num: ",fkarrays.Gamma_vpa_H[ivpa,imu]," Gam_vpa_HMaxwell: ",Gam_vpa_HMaxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_vpa_HMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_HMaxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_vpa_H[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_vpa_H_num.pdf") - # # # #savefig(outfile) - #end - #max_Gam_mu_Herr = maximum(Gam_mu_Herr) - #println("max(abs(Gamma_mu_H[F_Ms,F_Ms])): ", max_Gam_mu_Herr) - #if max_Gam_mu_Herr > zero - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if Gam_mu_Herr[ivpa,imu] > zero - # # # # ##println("ivpa: ",ivpa," imu: ",imu," Gam_mu_Herr: ",Gam_mu_Herr[ivpa,imu]," Gam_mu_H_num: ",fkarrays.Gamma_mu_H[ivpa,imu]," Gam_mu_HMaxwell: ",Gam_mu_HMaxwell[ivpa,imu]) - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Gam_mu_HMaxwell[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_HMaxwell.pdf") - # # # #savefig(outfile) - # #@views heatmap(mu.grid, vpa.grid, fkarrays.Gamma_mu_H[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Gam_mu_H_num.pdf") - # # # #savefig(outfile) - #end - # - #Cssp_err = maximum(abs.(Cssp)) - #if Cssp_err > zero - # #println("ERROR: C_ss'[F_Ms,F_Ms] /= 0") - # #for imu in 1:mu.n - # # #for ivpa in 1:vpa.n - # # # #if maximum(abs.(Cssp[ivpa,imu])) > zero - # # # # #print("ivpa: ",ivpa," imu: ",imu," C: ") - # # # # ##println("ivpa: ",ivpa," imu: ",imu," C: ", Cssp[ivpa,imu]) - # # # # ##println(" imu: ",imu," C[:,imu]:") - # # # # #@printf("%.1e", Cssp[ivpa,imu]) - # # # # #println("") - # # # #end - # # #end - # #end - # #@views heatmap(mu.grid, vpa.grid, Cssp[:,:], xlabel=L"\mu", ylabel=L"v_{||}", c = :deep, interpolation = :cubic, - # # # #windowsize = (360,240), margin = 15pt) - # # # #outfile = string("fkpl_Cssp_num.pdf") - # # # #savefig(outfile) - #end - #println("max(abs(C_ss'[F_Ms,F_Ms])): ", Cssp_err) - -end diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 0a7b37741..fccd5f493 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -13,14 +13,10 @@ Rosenbluth potentials are performed with Dirichlet boundary conditions. These routines provide the default collision operator used in the code. -The second set of functions implement the same operator using a "strong" form -where the collision operator is fully expanded out, and repeated interpolation -derivatives are used to calculate the terms in the operator arising from the -test particle distribution function. The Green's functions are used to obtain -the Rosenbluth potentials everywhere. This second implementation shows worse -numerical conservation properties than the first, and is significantly slower -due to the use of the Green's functions for the whole of velocity space. -For nowm, these functions are retained for development purposes. +The second set of functions are used to set up the necessary arrays to +compute the Rosenbluth potentials everywhere in vpa, vperp +by direct integration of the Green's functions. These functions are +supported for the purposes of testing and debugging. """ module fokker_planck @@ -52,7 +48,7 @@ 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 using ..fokker_planck_calculus: allocate_rosenbluth_potential_boundary_data -using ..fokker_planck_calculus: fokkerplanck_arrays_struct, fokkerplanck_weakform_arrays_struct +using ..fokker_planck_calculus: fokkerplanck_arrays_direct_integration_struct, fokkerplanck_weakform_arrays_struct using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc using ..fokker_planck_calculus: assemble_matrix_operators_dirichlet_bc_sparse using ..fokker_planck_calculus: assemble_explicit_collision_operator_rhs_serial! @@ -68,6 +64,7 @@ using ..fokker_planck_test: F_Maxwellian, dFdvpa_Maxwellian, dFdvperp_Maxwellian ######################################################## # begin functions associated with the weak-form operator +# where the potentials are computed by elliptic solve ######################################################## """ @@ -416,12 +413,14 @@ end ###################################################### # end functions associated with the weak-form operator +# where the potentials are computed by elliptic solve ###################################################### ########################################################## -# begin functions associated with the strong-form operator +# begin functions associated with the direct integration +# method for computing the Rosenbluth potentials ########################################################## @@ -429,7 +428,7 @@ end allocate the required ancilliary arrays """ -function allocate_fokkerplanck_arrays(vperp,vpa) +function allocate_fokkerplanck_arrays_direct_integration(vperp,vpa) nvpa = vpa.n nvperp = vperp.n @@ -458,7 +457,7 @@ function allocate_fokkerplanck_arrays(vperp,vpa) dfdvperp = allocate_shared_float(nvpa,nvperp) d2fdvperp2 = allocate_shared_float(nvpa,nvperp) - return fokkerplanck_arrays_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, + return fokkerplanck_arrays_direct_integration_struct(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, GG,d2Gdvpa2,d2Gdvperpdvpa,d2Gdvperp2,dGdvperp, HH,dHdvpa,dHdvperp,buffer_vpavperp_1,buffer_vpavperp_2, Cssp_result_vpavperp, dfdvpa, d2fdvpa2, @@ -466,11 +465,15 @@ function allocate_fokkerplanck_arrays(vperp,vpa) end """ -function that initialises the arrays needed for Fokker Planck collisions +function that initialises the arrays needed to calculate the Rosenbluth potentials +by direct integration. As this function is only supported to keep the testing +of the direct integration method, the struct 'fka' created here does not contain +all of the arrays necessary to compute the weak-form operator. This functionality +could be ported if necessary. """ -function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false, print_to_screen=false) - fka = allocate_fokkerplanck_arrays(vperp,vpa) +function init_fokker_planck_collisions_direct_integration(vperp,vpa; precompute_weights=false, print_to_screen=false) + fka = allocate_fokkerplanck_arrays_direct_integration(vperp,vpa) if vperp.n > 1 && precompute_weights @views init_Rosenbluth_potential_integration_weights!(fka.G0_weights, fka.G1_weights, fka.H0_weights, fka.H1_weights, fka.H2_weights, fka.H3_weights, vperp, vpa, print_to_screen=print_to_screen) @@ -478,408 +481,5 @@ function init_fokker_planck_collisions(vperp,vpa; precompute_weights=false, prin return fka end -""" -calculates the (normalised) Rosenbluth potential coefficients d2Gdvpa2, d2Gdvperpdvpa, ..., dHdvperp for a Maxwellian inputs. -""" -function calculate_Maxwellian_Rosenbluth_coefficients(dens,upar,vth,vpa,vperp,ivpa,ivperp,n_ion_species) # Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - # zero coefficients prior to looping over s' - Rosenbluth_d2Gdvpa2 = 0.0 - Rosenbluth_d2Gdvperpdvpa = 0.0 - Rosenbluth_d2Gdvperp2 = 0.0 - Rosenbluth_dHdvpa = 0.0 - Rosenbluth_dHdvperp = 0.0 - - # fill in value at (ivpa,ivperp) - for isp in 1:n_ion_species - Rosenbluth_d2Gdvpa2 += d2Gdvpa2(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) - Rosenbluth_d2Gdvperpdvpa += d2Gdvperpdvpa(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) - Rosenbluth_d2Gdvperp2 += d2Gdvperp2(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) - Rosenbluth_dHdvpa += dHdvpa(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) - Rosenbluth_dHdvperp += dHdvperp(dens[isp],upar[isp],vth[isp],vpa,vperp,ivpa,ivperp) - end - return Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa,Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp -end - - -""" -Function to carry out the integration of the revelant -distribution functions to form the required coefficients -for the full-F operator. We assume that the weights are -precalculated. The function takes as arguments the arrays -of coefficients (which we fill), the required distributions, -the precomputed weights, the indicies of the `field' velocities, -and the sizes of the primed vpa and vperp coordinates arrays. -""" -function get_local_Cssp_coefficients!(d2Gspdvpa2,dGspdvperp,d2Gspdvperpdvpa, - d2Gspdvperp2,dHspdvpa,dHspdvperp, - dfspdvpa,dfspdvperp,d2fspdvperpdvpa, - G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - ivpa,ivperp,nvpa,nvperp) - d2Gspdvpa2[ivpa,ivperp] = 0.0 - dGspdvperp[ivpa,ivperp] = 0.0 - d2Gspdvperpdvpa[ivpa,ivperp] = 0.0 - d2Gspdvperp2[ivpa,ivperp] = 0.0 - dHspdvpa[ivpa,ivperp] = 0.0 - dHspdvperp[ivpa,ivperp] = 0.0 - for ivperpp in 1:nvperp - for ivpap in 1:nvpa - #d2Gspdvpa2[ivpa,ivperp] += G_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvpa2[ivpap,ivperpp] - d2Gspdvpa2[ivpa,ivperp] += H3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] - dGspdvperp[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperpdvpa[ivpa,ivperp] += G1_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperpdvpa[ivpap,ivperpp] - #d2Gspdvperp2[ivpa,ivperp] += G2_weights[ivpap,ivperpp,ivpa,ivperp]*d2fspdvperp2[ivpap,ivperpp] + G3_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] - d2Gspdvperp2[ivpa,ivperp] += H2_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] - dHspdvpa[ivpa,ivperp] += H0_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvpa[ivpap,ivperpp] - dHspdvperp[ivpa,ivperp] += H1_weights[ivpap,ivperpp,ivpa,ivperp]*dfspdvperp[ivpap,ivperpp] - end - end - return nothing -end - - -""" -Evaluate the Fokker Planck collision Operator -using dummy arrays to store the 5 required derivatives. -For a single species, ir, and iz, this routine leaves -in place the fokkerplanck_arrays struct with testable -distributions function derivatives, Rosenbluth potentials, -and collision operator in place. -""" - -function explicit_fokker_planck_collisions!(pdf_out,pdf_in,dSdt,composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, - vpa_advect, z_advect, r_advect; diagnose_entropy_production = true) - n_ion_species = composition.n_ion_species - @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) - @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) - @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) - @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) - @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) - @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) - @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) - @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) - @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(dSdt,1) || throw(BoundsError(dSdt)) - @boundscheck r.n == size(dSdt,2) || throw(BoundsError(dSdt)) - @boundscheck n_ion_species == size(dSdt,3) || throw(BoundsError(dSdt)) - - # setup species information - mass = Array{mk_float,1}(undef,n_ion_species) - mass[1] = 1.0 # generalise! - nussp = Array{mk_float,2}(undef,n_ion_species,n_ion_species) - nussp[1,1] = collisions.nuii # generalise! - - # assign Cssp to a dummy array - Cssp = scratch_dummy.dummy_s - - # first, compute the require derivatives and store in the buffer arrays - dfdvpa = scratch_dummy.buffer_vpavperpzrs_1 - d2fdvpa2 = scratch_dummy.buffer_vpavperpzrs_2 - d2fdvperpdvpa = scratch_dummy.buffer_vpavperpzrs_3 - dfdvperp = scratch_dummy.buffer_vpavperpzrs_4 - d2fdvperp2 = scratch_dummy.buffer_vpavperpzrs_5 - logfC = scratch_dummy.buffer_vpavperpzrs_6 - - begin_s_r_z_vperp_region() - @loop_s_r_z_vperp is ir iz ivperp begin - @views derivative!(vpa.scratch, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) - @. dfdvpa[:,ivperp,iz,ir,is] = vpa.scratch - @views derivative!(vpa.scratch2, vpa.scratch, vpa, vpa_spectral) - @. d2fdvpa2[:,ivperp,iz,ir,is] = vpa.scratch2 - end - if vpa.discretization == "gausslegendre_pseudospectral" - @loop_s_r_z_vperp is ir iz ivperp begin - @views second_derivative!(vpa.scratch2, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) - @. d2fdvpa2[:,ivperp,iz,ir,is] = vpa.scratch2 - end - end - - begin_s_r_z_vpa_region() - - @loop_s_r_z_vpa is ir iz ivpa begin - @views derivative!(vperp.scratch, pdf_in[ivpa,:,iz,ir,is], vperp, vperp_spectral) - @. dfdvperp[ivpa,:,iz,ir,is] = vperp.scratch - @views derivative!(vperp.scratch2, vperp.scratch, vperp, vperp_spectral) - @. d2fdvperp2[ivpa,:,iz,ir,is] = vperp.scratch2 - @views derivative!(vperp.scratch, dfdvpa[ivpa,:,iz,ir,is], vperp, vperp_spectral) - @. d2fdvperpdvpa[ivpa,:,iz,ir,is] = vperp.scratch - end - - # to permit moment conservation, store the current moments of pdf_out - # this involves imposing the boundary conditions to the present pre-collisions pdf_out - if collisions.numerical_conserving_terms == "density+u||+T" || collisions.numerical_conserving_terms == "density" - store_moments_in_buffer!(pdf_out,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - end - # now parallelise over all dimensions and calculate the - # collision operator coefficients and the collision operator - # in one loop, noting that we only require data local to - # each ivpa,ivperp,iz,ir,is now that the derivatives are precomputed - fka = fokkerplanck_arrays - begin_s_r_z_vperp_vpa_region() - @loop_s_r_z is ir iz begin - @loop_vperp_vpa ivperp ivpa begin - for isp in 1:n_ion_species # make sure to sum over all ion species - # get the local (in ivpa, ivperp) values of the coeffs - @views get_local_Cssp_coefficients!(fka.d2Gdvpa2,fka.dGdvperp,fka.d2Gdvperpdvpa, - fka.d2Gdvperp2,fka.dHdvpa,fka.dHdvperp, - dfdvpa[:,:,iz,ir,isp],dfdvperp[:,:,iz,ir,isp],d2fdvperpdvpa[:,:,iz,ir,isp], - fka.G1_weights,fka.H0_weights,fka.H1_weights,fka.H2_weights,fka.H3_weights, - ivpa,ivperp,vpa.n,vperp.n) - - (Cssp[isp] = Cssp_fully_expanded_form(nussp[is,isp],mass[is],mass[isp], - d2fdvpa2[ivpa,ivperp,iz,ir,is],d2fdvperp2[ivpa,ivperp,iz,ir,is],d2fdvperpdvpa[ivpa,ivperp,iz,ir,is],dfdvpa[ivpa,ivperp,iz,ir,is],dfdvperp[ivpa,ivperp,iz,ir,is],pdf_in[ivpa,ivperp,iz,ir,is], - fka.d2Gdvpa2[ivpa,ivperp],fka.d2Gdvperp2[ivpa,ivperp],fka.d2Gdvperpdvpa[ivpa,ivperp],fka.dGdvperp[ivpa,ivperp], - fka.dHdvpa[ivpa,ivperp],fka.dHdvperp[ivpa,ivperp],pdf_in[ivpa,ivperp,iz,ir,isp],vperp.grid[ivperp]) ) - pdf_out[ivpa,ivperp,iz,ir,is] += dt*Cssp[isp] - # for testing - fka.Cssp_result_vpavperp[ivpa,ivperp] = Cssp[isp] - fka.dfdvpa[ivpa,ivperp] = dfdvpa[ivpa,ivperp,iz,ir,is] - fka.d2fdvpa2[ivpa,ivperp] = d2fdvpa2[ivpa,ivperp,iz,ir,is] - fka.d2fdvperpdvpa[ivpa,ivperp] = d2fdvperpdvpa[ivpa,ivperp,iz,ir,is] - fka.dfdvperp[ivpa,ivperp] = dfdvperp[ivpa,ivperp,iz,ir,is] - fka.d2fdvperp2[ivpa,ivperp] = d2fdvperp2[ivpa,ivperp,iz,ir,is] - end - # store the entropy production - # we use ln|f| to avoid problems with f < 0. This is ok if C_s[f,f] is small where f ~< 0 - # + 1.0e-15 in case f = 0 exactly - #println(Cssp[:]) - #println(pdf_in[ivpa,ivperp,iz,ir,is]) - logfC[ivpa,ivperp,iz,ir,is] = log(abs(pdf_in[ivpa,ivperp,iz,ir,is]) + 1.0e-15)*sum(Cssp[:]) - #println(dfdvpa[ivpa,ivperp,iz,ir,is]) - end - end - if diagnose_entropy_production - # compute entropy production diagnostic - begin_s_r_z_region() - @loop_s_r_z is ir iz begin - @views dSdt[iz,ir,is] = -integrate_over_vspace(logfC[:,:,iz,ir,is], vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts) - end - end - if collisions.numerical_conserving_terms == "density+u||+T" - # use an ad-hoc numerical model to conserve density, upar, vth - # a different model is required for inter-species collisions - # simply conserving particle density may be more appropriate in the multi-species case - apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - elseif collisions.numerical_conserving_terms == "density" - apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - elseif !(collisions.numerical_conserving_terms == "none") - println("ERROR: collisions.numerical_conserving_terms = ",collisions.numerical_conserving_terms," NOT SUPPORTED") - end - return nothing -end - -function explicit_fokker_planck_collisions_Maxwellian_coefficients!(pdf_out,pdf_in,dens_in,upar_in,vth_in, - composition,collisions,dt,fokkerplanck_arrays::fokkerplanck_arrays_struct, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) - n_ion_species = composition.n_ion_species - @boundscheck vpa.n == size(pdf_out,1) || throw(BoundsError(pdf_out)) - @boundscheck vperp.n == size(pdf_out,2) || throw(BoundsError(pdf_out)) - @boundscheck z.n == size(pdf_out,3) || throw(BoundsError(pdf_out)) - @boundscheck r.n == size(pdf_out,4) || throw(BoundsError(pdf_out)) - @boundscheck n_ion_species == size(pdf_out,5) || throw(BoundsError(pdf_out)) - @boundscheck vpa.n == size(pdf_in,1) || throw(BoundsError(pdf_in)) - @boundscheck vperp.n == size(pdf_in,2) || throw(BoundsError(pdf_in)) - @boundscheck z.n == size(pdf_in,3) || throw(BoundsError(pdf_in)) - @boundscheck r.n == size(pdf_in,4) || throw(BoundsError(pdf_in)) - @boundscheck n_ion_species == size(pdf_in,5) || throw(BoundsError(pdf_in)) - - mi = 1.0 # generalise this to an Array with size n_ion_species - mip = 1.0 # generalise this to an Array with size n_ion_species - cfreqii = collisions.nuii # generalise this to an Array with size (n_ion_species,n_ion_species) - fk = fokkerplanck_arrays - Cssp_result_vpavperp = scratch_dummy.dummy_vpavperp - pdf_buffer_1 = scratch_dummy.buffer_vpavperpzrs_1 - pdf_buffer_2 = scratch_dummy.buffer_vpavperpzrs_2 - - # precompute derivatives of the pdfs to benefit from parallelisation - # d F / d vpa - begin_s_r_z_vperp_region() - @loop_s_r_z_vperp is ir iz ivperp begin - @views derivative!(vpa.scratch, pdf_in[:,ivperp,iz,ir,is], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp,iz,ir,is] = vpa.scratch - end - # d F / d vperp - begin_s_r_z_vpa_region() - @loop_s_r_z_vpa is ir iz ivpa begin - @views derivative!(vperp.scratch, pdf_in[ivpa,:,iz,ir,is], vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:,iz,ir,is] = vperp.scratch - end - - begin_s_r_z_vperp_vpa_region() - @loop_s_r_z is ir iz begin - @loop_vperp_vpa ivperp ivpa begin - # first compute local (in z,r) Rosenbluth potential coefficients, summing over all s' - ((Rosenbluth_d2Gdvpa2, Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa, - Rosenbluth_dHdvperp) = calculate_Maxwellian_Rosenbluth_coefficients(dens_in[iz,ir,:], - upar_in[iz,ir,:],vth_in[iz,ir,:],vpa,vperp,ivpa,ivperp,n_ion_species) ) - - # now form the collisional fluxes at this s,z,r - ( (Cflux_vpa,Cflux_vperp) = calculate_collisional_fluxes(pdf_in[ivpa,ivperp,iz,ir,is], - pdf_buffer_1[ivpa,ivperp,iz,ir,is],pdf_buffer_2[ivpa,ivperp,iz,ir,is], - Rosenbluth_d2Gdvpa2,Rosenbluth_d2Gdvperpdvpa, - Rosenbluth_d2Gdvperp2,Rosenbluth_dHdvpa,Rosenbluth_dHdvperp, - mi,mip) ) - - # now overwrite the buffer arrays with the local values as we no longer need dFdvpa or dFdvperp at s,r,z - pdf_buffer_1[ivpa,ivperp,iz,ir,is] = Cflux_vpa - pdf_buffer_2[ivpa,ivperp,iz,ir,is] = Cflux_vperp - end - - end - - # now differentiate the fluxes to obtain the explicit operator - - # d Cflux_vpa / d vpa - begin_s_r_z_vperp_region() - @loop_s_r_z_vperp is ir iz ivperp begin - @views derivative!(vpa.scratch, pdf_buffer_1[:,ivperp,iz,ir,is], vpa, vpa_spectral) - @. pdf_buffer_1[:,ivperp,iz,ir,is] = vpa.scratch - end - # (1/vperp) d Cflux_vperp / d vperp - begin_s_r_z_vpa_region() - @loop_s_r_z_vpa is ir iz ivpa begin - @views @. vperp.scratch2 = vperp.grid*pdf_buffer_2[ivpa,:,iz,ir,is] - @views derivative!(vperp.scratch, vperp.scratch2, vperp, vperp_spectral) - @. pdf_buffer_2[ivpa,:,iz,ir,is] = vperp.scratch[:]/vperp.grid[:] - end - - # now add the result to the outgoing pdf - # d F / d t = nu_ii * ( d Cflux_vpa / d vpa + (1/vperp) d Cflux_vperp / d vperp) - begin_s_r_z_vperp_vpa_region() - @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin - pdf_out[ivpa,ivperp,iz,ir,is] += dt*cfreqii*(pdf_buffer_1[ivpa,ivperp,iz,ir,is] + pdf_buffer_1[ivpa,ivperp,iz,ir,is]) - end -end - -# applies the numerical conservation to pdf_out, the advanced distribution function -# uses the low-level moment integration routines from velocity moments -# conserves n, upar, total pressure of each species -# only correct for the self collision operator -# multi-species cases requires conservation of particle number and total momentum and total energy ( sum_s m_s upar_s, ... ) -function apply_numerical_conserving_terms!(pdf_out,pdf_in,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # enforce bc prior to imposing conservation - enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, - vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # buffer arrays - buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 - dummy_vpavperp = scratch_dummy.dummy_vpavperp - # data precalculated by store_moments_in_buffer! - buffer_n = scratch_dummy.buffer_zrs_1 - buffer_upar = scratch_dummy.buffer_zrs_2 - buffer_pressure = scratch_dummy.buffer_zrs_3 - mass = 1.0 - begin_s_r_z_region() - @loop_s_r_z is ir iz begin - # get moments of incoming and outgoing distribution functions - n_in = buffer_n[iz,ir,is] - upar_in = buffer_upar[iz,ir,is] - pressure_in = buffer_pressure[iz,ir,is] - - n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - upar_out = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, n_out) - ppar_out = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass) - pperp_out = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, mass) - pressure_out = get_pressure(ppar_out,pperp_out) - qpar_out = get_qpar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass, dummy_vpavperp) - rmom_out = get_rmom(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar_out, mass, dummy_vpavperp) - - # form the appropriate matrix coefficients - b0, b1, b2 = n_in, n_in*(upar_in - upar_out), (3.0/2.0)*(pressure_in/mass) + n_in*(upar_in - upar_out)^2 - A00, A02, A11, A12, A22 = n_out, (3.0/2.0)*(pressure_out/mass), 0.5*ppar_out/mass, qpar_out/mass, rmom_out/mass - - # obtain the coefficients for the corrections - (x0, x1, x2) = symmetric_matrix_inverse(A00,A02,A11,A12,A22,b0,b1,b2) - - # fill the buffer with the corrected pdf - @loop_vperp_vpa ivperp ivpa begin - wpar = vpa.grid[ivpa] - upar_out - buffer_pdf[ivpa,ivperp,iz,ir,is] = (x0 + x1*wpar + x2*(vperp.grid[ivperp]^2 + wpar^2) )*pdf_out[ivpa,ivperp,iz,ir,is] - end - - end - begin_s_r_z_vperp_vpa_region() - # update pdf_out - @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin - pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] - end - return nothing -end - -# function which corrects only for the loss of particles due to numerical error -# suitable for use with multiple species collisions -function apply_density_conserving_terms!(pdf_out,pdf_in,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # enforce bc prior to imposing conservation - enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, - vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # buffer array - buffer_pdf = scratch_dummy.buffer_vpavperpzrs_1 - # data precalculated by store_moments_in_buffer! - buffer_n = scratch_dummy.buffer_zrs_1 - begin_s_r_z_region() - @loop_s_r_z is ir iz begin - # get density moment of incoming and outgoing distribution functions - n_in = buffer_n[iz,ir,is] - - n_out = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - - # obtain the coefficient for the corrections - x0 = n_in/n_out - - # update pdf_out with the corrections - @loop_vperp_vpa ivperp ivpa begin - buffer_pdf[ivpa,ivperp,iz,ir,is] = x0*pdf_out[ivpa,ivperp,iz,ir,is] - end - - end - begin_s_r_z_vperp_vpa_region() - # update pdf_out - @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin - pdf_out[ivpa,ivperp,iz,ir,is] = buffer_pdf[ivpa,ivperp,iz,ir,is] - end - return nothing -end - -function store_moments_in_buffer!(pdf_out,boundary_distributions, - vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # enforce bc prior to calculating the moments - enforce_boundary_conditions!(pdf_out, boundary_distributions.pdf_rboundary_charged, - vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_advect, z_advect, r_advect, composition, - scratch_dummy, advance, vperp_spectral, vpa_spectral) - # buffer arrays - density = scratch_dummy.buffer_zrs_1 - upar = scratch_dummy.buffer_zrs_2 - pressure = scratch_dummy.buffer_zrs_3 - # set the mass - mass = 1.0 - - begin_s_r_z_region() - @loop_s_r_z is ir iz begin - density[iz,ir,is] = get_density(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp) - upar[iz,ir,is] = get_upar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, density[iz,ir,is]) - ppar = get_ppar(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, upar[iz,ir,is], mass) - pperp = get_pperp(@view(pdf_out[:,:,iz,ir,is]), vpa, vperp, mass) - pressure[iz,ir,is] = get_pressure(ppar,pperp) - end - return nothing -end - -######################################################## -# end functions associated with the strong-form operator -######################################################## end diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index ac36d3748..c767b186c 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -15,7 +15,7 @@ export assemble_explicit_collision_operator_rhs_parallel_analytical_inputs! export YY_collision_operator_arrays, calculate_YY_arrays export calculate_rosenbluth_potential_boundary_data! export elliptic_solve!, algebraic_solve! -export fokkerplanck_arrays_struct +export fokkerplanck_arrays_direct_integration_struct export fokkerplanck_weakform_arrays_struct export enforce_vpavperp_BCs! export calculate_rosenbluth_potentials_via_elliptic_solve! @@ -68,7 +68,7 @@ a struct of dummy arrays and precalculated coefficients for the strong-form Fokker-Planck collision operator """ -struct fokkerplanck_arrays_struct +struct fokkerplanck_arrays_direct_integration_struct G0_weights::MPISharedArray{mk_float,4} G1_weights::MPISharedArray{mk_float,4} H0_weights::MPISharedArray{mk_float,4} @@ -1096,7 +1096,7 @@ function calculate_boundary_data!(func_data::vpa_vperp_boundary_data, end function calculate_rosenbluth_potential_boundary_data!(rpbd::rosenbluth_potential_boundary_data, - fkpl::Union{fokkerplanck_arrays_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral; + fkpl::Union{fokkerplanck_arrays_direct_integration_struct,fokkerplanck_boundary_data_arrays_struct},pdf,vpa,vperp,vpa_spectral,vperp_spectral; calculate_GG=false,calculate_dGdvperp=false) # get derivatives of pdf dfdvperp = fkpl.dfdvperp @@ -2174,7 +2174,7 @@ function to calculate Rosenbluth potentials by direct integration function calculate_rosenbluth_potentials_via_direct_integration!(GG,HH,dHdvpa,dHdvperp, d2Gdvpa2,dGdvperp,d2Gdvperpdvpa,d2Gdvperp2,ffsp_in, - vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays::fokkerplanck_arrays_struct) + vpa,vperp,vpa_spectral,vperp_spectral,fkpl_arrays::fokkerplanck_arrays_direct_integration_struct) dfdvpa = fkpl_arrays.dfdvpa dfdvperp = fkpl_arrays.dfdvperp d2fdvperpdvpa = fkpl_arrays.d2fdvperpdvpa diff --git a/src/input_structs.jl b/src/input_structs.jl index e5728c763..50c79486d 100644 --- a/src/input_structs.jl +++ b/src/input_structs.jl @@ -62,9 +62,7 @@ mutable struct advance_info ionization_collisions_1V::Bool ionization_source::Bool krook_collisions::Bool - explicit_fp_collisions::Bool explicit_weakform_fp_collisions::Bool - explicit_fp_F_FM_collisions::Bool external_source::Bool numerical_dissipation::Bool source_terms::Bool @@ -320,14 +318,10 @@ mutable struct collisions_input krook_collision_frequency_prefactor::mk_float # Setting to switch between different options for Krook collision operator krook_collisions_option::String - # Fokker-Planck operator choice - weakform_fokker_planck::Bool # ion-ion self collision frequency + # nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 + # with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 nuii::mk_float - # ion-ion self collision frequency with C[F_s,F_Ms'] operator - nuii_pitch::mk_float - # numerical conserving terms (for strong form Fokker-Planck operator only) - numerical_conserving_terms::String end """ diff --git a/src/moment_kinetics_input.jl b/src/moment_kinetics_input.jl index d1ce6590c..1164f22cb 100644 --- a/src/moment_kinetics_input.jl +++ b/src/moment_kinetics_input.jl @@ -185,16 +185,8 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) error("Invalid option " * "krook_collisions_option=$(collisions.krook_collisions_option) passed") end - + # set the Fokker-Planck collision frequency collisions.nuii = get(scan_input, "nuii", 0.0) - collisions.weakform_fokker_planck = get(scan_input, "weakform_fokker_planck", true) - if !collisions.weakform_fokker_planck && global_rank[] == 0 - println("WARNING: you have used weakform_fokker_planck = false") - println("WARNING: you have selected a depreciated version of the ion-ion self collision operator") - end - # options below only used with collisions.weakform_fokker_planck = false - collisions.nuii_pitch = get(scan_input, "nuii_pitch", 0.0) - collisions.numerical_conserving_terms = get(scan_input, "numerical_conserving_terms", "density") # parameters related to the time stepping nstep = get(scan_input, "nstep", 5) @@ -1000,13 +992,9 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) ionization = 0.0 constant_ionization_rate = false krook_collision_frequency_prefactor = -1.0 - weakform_fokker_planck = true nuii = 0.0 - nuii_pitch = 0.0 - numerical_conserving_terms = "density" collisions = collisions_input(charge_exchange, ionization, constant_ionization_rate, - krook_collision_frequency_prefactor,"none", weakform_fokker_planck, - nuii, nuii_pitch, numerical_conserving_terms) + krook_collision_frequency_prefactor,"none", nuii) Bzed = 1.0 # magnetic field component along z Bmag = 1.0 # magnetic field strength bzed = 1.0 # component of b unit vector along z diff --git a/src/time_advance.jl b/src/time_advance.jl index ea4b20cd2..109e94ebb 100644 --- a/src/time_advance.jl +++ b/src/time_advance.jl @@ -55,8 +55,7 @@ using ..continuity: continuity_equation!, neutral_continuity_equation! using ..force_balance: force_balance!, neutral_force_balance! using ..energy_equation: energy_equation!, neutral_energy_equation! using ..em_fields: setup_em_fields, update_phi! -using ..fokker_planck: init_fokker_planck_collisions_weak_form, init_fokker_planck_collisions, explicit_fokker_planck_collisions! -using ..fokker_planck: explicit_fokker_planck_collisions_weak_form!, explicit_fokker_planck_collisions_Maxwellian_coefficients! +using ..fokker_planck: init_fokker_planck_collisions_weak_form, explicit_fokker_planck_collisions_weak_form! using ..manufactured_solns: manufactured_sources using ..advection: advection_info using ..utils: to_minutes @@ -128,11 +127,7 @@ struct scratch_dummy_arrays # needs to be shared memory buffer_vpavperpzrs_1::MPISharedArray{mk_float,5} buffer_vpavperpzrs_2::MPISharedArray{mk_float,5} - buffer_vpavperpzrs_3::MPISharedArray{mk_float,5} - buffer_vpavperpzrs_4::MPISharedArray{mk_float,5} - buffer_vpavperpzrs_5::MPISharedArray{mk_float,5} - buffer_vpavperpzrs_6::MPISharedArray{mk_float,5} - + buffer_vzvrvzetazsn_1::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_2::MPISharedArray{mk_float,5} buffer_vzvrvzetazsn_3::MPISharedArray{mk_float,5} @@ -218,9 +213,7 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, vz_spectral, 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_fp_collisions - fp_arrays = init_fokker_planck_collisions(vperp,vpa; precompute_weights=true) - elseif advance.explicit_weakform_fp_collisions + if advance.explicit_weakform_fp_collisions fp_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true) else fp_arrays = nothing @@ -485,9 +478,7 @@ function setup_advance_flags(moments, composition, t_input, collisions, r_diffusion = false vpa_diffusion = false vz_diffusion = false - explicit_fp_collisions = false explicit_weakform_fp_collisions = false - explicit_fp_F_FM_collisions = false # all advance flags remain false if using operator-splitting # otherwise, check to see if the flags need to be set to true if !t_input.split_operators @@ -496,22 +487,10 @@ function setup_advance_flags(moments, composition, t_input, collisions, advance_z_advection = true && z.n > 1 advance_r_advection = true && r.n > 1 if collisions.nuii > 0.0 && vperp.n > 1 - if collisions.weakform_fokker_planck - explicit_fp_collisions = false - explicit_weakform_fp_collisions = true - else - explicit_fp_collisions = true - explicit_weakform_fp_collisions = false - end - else - explicit_fp_collisions = false + explicit_weakform_fp_collisions = true + else explicit_weakform_fp_collisions = false end - if collisions.nuii_pitch > 0.0 && vperp.n > 1 - explicit_fp_F_FM_collisions = true - else - explicit_fp_F_FM_collisions = false - end # if neutrals present, check to see if different ion-neutral # collisions are enabled if composition.n_neutral_species > 0 @@ -600,7 +579,7 @@ function setup_advance_flags(moments, composition, t_input, collisions, # 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) # 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 || explicit_fp_collisions || explicit_fp_F_FM_collisions) + vpa_diffusion = ((advance_numerical_dissipation && num_diss_params.vpa_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions) vz_diffusion = (advance_numerical_dissipation && num_diss_params.vz_dissipation_coefficient > 0.0) end @@ -611,8 +590,7 @@ function setup_advance_flags(moments, composition, t_input, collisions, advance_neutral_vz_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, advance_ionization_source, advance_krook_collisions, - explicit_fp_collisions, explicit_weakform_fp_collisions, - explicit_fp_F_FM_collisions, + explicit_weakform_fp_collisions, advance_external_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, advance_energy, advance_neutral_external_source, @@ -681,11 +659,7 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_vpavperpzrs_1 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) buffer_vpavperpzrs_2 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) - buffer_vpavperpzrs_3 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) - buffer_vpavperpzrs_4 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) - buffer_vpavperpzrs_5 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) - buffer_vpavperpzrs_6 = allocate_shared_float(nvpa,nvperp,nz,nr,nspecies_ion) - + buffer_vzvrvzetazsn_1 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) buffer_vzvrvzetazsn_2 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) buffer_vzvrvzetazsn_3 = allocate_shared_float(nvz,nvr,nvzeta,nz,nspecies_neutral) @@ -717,7 +691,7 @@ function setup_dummy_and_buffer_arrays(nr,nz,nvpa,nvperp,nvz,nvr,nvzeta,nspecies buffer_zrs_1,buffer_zrs_2,buffer_zrs_3, buffer_vpavperpzs_1,buffer_vpavperpzs_2,buffer_vpavperpzs_3,buffer_vpavperpzs_4,buffer_vpavperpzs_5,buffer_vpavperpzs_6, buffer_vpavperprs_1,buffer_vpavperprs_2,buffer_vpavperprs_3,buffer_vpavperprs_4,buffer_vpavperprs_5,buffer_vpavperprs_6, - buffer_vpavperpzrs_1,buffer_vpavperpzrs_2,buffer_vpavperpzrs_3,buffer_vpavperpzrs_4,buffer_vpavperpzrs_5,buffer_vpavperpzrs_6, + buffer_vpavperpzrs_1,buffer_vpavperpzrs_2, buffer_vzvrvzetazsn_1,buffer_vzvrvzetazsn_2,buffer_vzvrvzetazsn_3,buffer_vzvrvzetazsn_4,buffer_vzvrvzetazsn_5,buffer_vzvrvzetazsn_6, buffer_vzvrvzetarsn_1,buffer_vzvrvzetarsn_2,buffer_vzvrvzetarsn_3,buffer_vzvrvzetarsn_4,buffer_vzvrvzetarsn_5,buffer_vzvrvzetarsn_6, buffer_vzvrvzetazrsn_1, buffer_vzvrvzetazrsn_2, @@ -1874,27 +1848,13 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, r_dissipation_neutral!(fvec_out.pdf_neutral, fvec_in.pdf_neutral, r, r_spectral, dt, num_diss_params, scratch_dummy) end - - if advance.explicit_fp_collisions - update_entropy_diagnostic = (istage == 1) - explicit_fokker_planck_collisions!(fvec_out.pdf, fvec_in.pdf, moments.charged.dSdt, composition,collisions,dt,fp_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral, boundary_distributions, advance, - vpa_advect, z_advect, r_advect, - diagnose_entropy_production = update_entropy_diagnostic) - #println(moments.charged.dSdt) - end + # 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, fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, diagnose_entropy_production = update_entropy_diagnostic) end - if advance.explicit_fp_F_FM_collisions - explicit_fokker_planck_collisions_Maxwellian_coefficients!(fvec_out.pdf, fvec_in.pdf, - fvec_in.density, fvec_in.upar, moments.charged.vth, - composition, collisions, dt, fp_arrays, - scratch_dummy, r, z, vperp, vpa, vperp_spectral, vpa_spectral) - end # End of advance for distribution function diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index 90acf5606..e2383ba46 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -15,7 +15,7 @@ using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_pressure using moment_kinetics.fokker_planck: init_fokker_planck_collisions_weak_form, fokker_planck_collision_operator_weak_form! -using moment_kinetics.fokker_planck: conserving_corrections!, init_fokker_planck_collisions +using moment_kinetics.fokker_planck: conserving_corrections!, init_fokker_planck_collisions_direct_integration using moment_kinetics.fokker_planck_test: print_test_data, plot_test_data, fkpl_error_data, allocate_error_data using moment_kinetics.fokker_planck_test: F_Maxwellian, G_Maxwellian, H_Maxwellian using moment_kinetics.fokker_planck_test: d2Gdvpa2_Maxwellian, d2Gdvperp2_Maxwellian, d2Gdvperpdvpa_Maxwellian, dGdvperp_Maxwellian @@ -442,7 +442,7 @@ function runtests() vpa, vpa_spectral, vperp, vperp_spectral = create_grids(ngrid,nelement_vpa,nelement_vperp, Lvpa=12.0,Lvperp=6.0) begin_serial_region() - fkpl_arrays = init_fokker_planck_collisions(vperp,vpa,precompute_weights=true,print_to_screen=print_to_screen) + fkpl_arrays = init_fokker_planck_collisions_direct_integration(vperp,vpa,precompute_weights=true,print_to_screen=print_to_screen) dummy_array = allocate_float(vpa.n,vperp.n) F_M = allocate_float(vpa.n,vperp.n) H_M_exact = allocate_float(vpa.n,vperp.n) From 515a9421986bc85b06325671900a9360af12bc88 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 28 Nov 2023 09:57:39 +0000 Subject: [PATCH 289/331] Add GR_jll to Project.toml to permit compiliation of Plots. --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 233c22b1b..97abfed83 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" +GR_jll = "d2c73de3-f751-5644-a686-071e5b155ba9" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" HDF5_jll = "0234f1f7-429e-5d53-9886-15a909be8d59" From f31977defd6d3e3654e37f171ceebe0c0f589101 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 28 Nov 2023 10:54:36 +0000 Subject: [PATCH 290/331] Refactor @test statements in test/fokker_planck_tests.jl. --- test/fokker_planck_tests.jl | 129 +++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 60 deletions(-) diff --git a/test/fokker_planck_tests.jl b/test/fokker_planck_tests.jl index e2383ba46..feb930de2 100644 --- a/test/fokker_planck_tests.jl +++ b/test/fokker_planck_tests.jl @@ -117,7 +117,7 @@ function runtests() if print_to_screen println("max(ravel_err)",max_ravel_err) end - @test isapprox(max_ravel_err, 1.0e-15 ; atol = 1.0e-15) + @test max_ravel_err < 1.0e-15 end #print_vector(fc,"fc",nc_global) # multiply by KKpar2D and fill dfc @@ -132,11 +132,11 @@ function runtests() ravel_c_to_vpavperp!(d2fvpavperp_dvperp2_num,gc,nc_global,vpa.n) @serial_region begin d2fvpavperp_dvpa2_max, d2fvpavperp_dvpa2_L2 = print_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fdvpa2",vpa,vperp,dummy_array,print_to_screen=print_to_screen) - @test isapprox(d2fvpavperp_dvpa2_max, 1.0e-7 ; atol=1.0e-7) - @test isapprox(d2fvpavperp_dvpa2_L2, 1.0e-8 ; atol=1.0e-8) + @test d2fvpavperp_dvpa2_max < 1.0e-7 + @test d2fvpavperp_dvpa2_L2 < 1.0e-8 d2fvpavperp_dvperp2_max, d2fvpavperp_dvperp2_L2 = print_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fdvperp2",vpa,vperp,dummy_array,print_to_screen=print_to_screen) - @test isapprox(d2fvpavperp_dvperp2_max, 1.0e-7 ; atol=1.0e-7) - @test isapprox(d2fvpavperp_dvperp2_L2, 1.0e-8 ; atol=1.0e-8) + @test d2fvpavperp_dvperp2_max < 1.0e-7 + @test d2fvpavperp_dvperp2_L2 < 1.0e-8 #if plot_test_output # plot_test_data(d2fvpavperp_dvpa2_exact,d2fvpavperp_dvpa2_num,d2fvpavperp_dvpa2_err,"d2fvpavperp_dvpa2",vpa,vperp) # plot_test_data(d2fvpavperp_dvperp2_exact,d2fvpavperp_dvperp2_num,d2fvpavperp_dvperp2_err,"d2fvpavperp_dvperp2",vpa,vperp) @@ -228,22 +228,22 @@ function runtests() max_dHdvperp_boundary_data_err, max_G_boundary_data_err, max_dGdvperp_boundary_data_err, max_d2Gdvperp2_boundary_data_err, max_d2Gdvperpdvpa_boundary_data_err, max_d2Gdvpa2_boundary_data_err = test_rosenbluth_potential_boundary_data(fkpl_arrays.rpbd,rpbd_exact,vpa,vperp,print_to_screen=print_to_screen) - rtol_max, atol_max = 2.0e-13, 2.0e-13 - @test isapprox(max_H_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 2.0e-12, 2.0e-12 - @test isapprox(max_dHdvpa_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 3.0e-9, 3.0e-9 - @test isapprox(max_dHdvperp_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 7.0e-12, 7.0e-12 - @test isapprox(max_G_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 2.0e-7, 2.0e-7 - @test isapprox(max_dGdvperp_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 2.0e-8, 2.0e-8 - @test isapprox(max_d2Gdvperp2_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 2.0e-8, 2.0e-8 - @test isapprox(max_d2Gdvperpdvpa_boundary_data_err, rtol_max ; atol=atol_max) - rtol_max, atol_max = 7.0e-12, 7.0e-12 - @test isapprox(max_d2Gdvpa2_boundary_data_err, rtol_max ; atol=atol_max) + atol_max = 2.0e-13 + @test max_H_boundary_data_err < atol_max + atol_max = 2.0e-12 + @test max_dHdvpa_boundary_data_err < atol_max + atol_max = 3.0e-9 + @test max_dHdvperp_boundary_data_err < atol_max + atol_max = 7.0e-12 + @test max_G_boundary_data_err < atol_max + atol_max = 2.0e-7 + @test max_dGdvperp_boundary_data_err < atol_max + atol_max = 2.0e-8 + @test max_d2Gdvperp2_boundary_data_err < atol_max + atol_max = 2.0e-8 + @test max_d2Gdvperpdvpa_boundary_data_err < atol_max + atol_max = 7.0e-12 + @test max_d2Gdvpa2_boundary_data_err < atol_max # test the elliptic solvers H_M_max, H_M_L2 = print_test_data(H_M_exact,H_M_num,H_M_err,"H_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) dHdvpa_M_max, dHdvpa_M_L2 = print_test_data(dHdvpa_M_exact,dHdvpa_M_num,dHdvpa_M_err,"dHdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) @@ -253,38 +253,38 @@ function runtests() dGdvperp_M_max, dGdvperp_M_L2 = print_test_data(dGdvperp_M_exact,dGdvperp_M_num,dGdvperp_M_err,"dGdvperp_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) d2Gdvperpdvpa_M_max, d2Gdvperpdvpa_M_L2 = print_test_data(d2Gdvperpdvpa_M_exact,d2Gdvperpdvpa_M_num,d2Gdvperpdvpa_M_err,"d2Gdvperpdvpa_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) d2Gdvperp2_M_max, d2Gdvperp2_M_L2 = print_test_data(d2Gdvperp2_M_exact,d2Gdvperp2_M_num,d2Gdvperp2_M_err,"d2Gdvperp2_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) - rtol_max, atol_max = 2.0e-7, 2.0e-7 - rtol_L2, atol_L2 = 5.0e-9, 5.0e-9 - @test isapprox(H_M_max, rtol_max ; atol=atol_max) - @test isapprox(H_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-6, 2.0e-6 - rtol_L2, atol_L2 = 5.0e-8, 5.0e-8 - @test isapprox(dHdvpa_M_max, rtol_max ; atol=atol_max) - @test isapprox(dHdvpa_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-5, 2.0e-5 - rtol_L2, atol_L2 = 1.0e-7, 1.0e-7 - @test isapprox(dHdvperp_M_max, rtol_max ; atol=atol_max) - @test isapprox(dHdvperp_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-8, 2.0e-8 - rtol_L2, atol_L2 = 7.0e-10, 7.0e-10 - @test isapprox(G_M_max, rtol_max ; atol=atol_max) - @test isapprox(G_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-7, 2.0e-7 - rtol_L2, atol_L2 = 4.0e-9, 4.0e-9 - @test isapprox(d2Gdvpa2_M_max, rtol_max ; atol=atol_max) - @test isapprox(d2Gdvpa2_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-6, 2.0e-6 - rtol_L2, atol_L2 = 2.0e-7, 2.0e-7 - @test isapprox(dGdvperp_M_max, rtol_max ; atol=atol_max) - @test isapprox(dGdvperp_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 2.0e-6, 2.0e-6 - rtol_L2, atol_L2 = 2.0e-8, 2.0e-8 - @test isapprox(d2Gdvperpdvpa_M_max, rtol_max ; atol=atol_max) - @test isapprox(d2Gdvperpdvpa_M_L2, rtol_L2 ; atol=atol_L2) - rtol_max, atol_max = 3.0e-7, 3.0e-7 - rtol_L2, atol_L2 = 2.0e-8, 2.0e-8 - @test isapprox(d2Gdvperp2_M_max, rtol_max ; atol=atol_max) - @test isapprox(d2Gdvperp2_M_L2, rtol_L2 ; atol=atol_L2) + atol_max = 2.0e-7 + atol_L2 = 5.0e-9 + @test H_M_max < atol_max + @test H_M_L2 < atol_L2 + atol_max = 2.0e-6 + atol_L2 = 5.0e-8 + @test dHdvpa_M_max < atol_max + @test dHdvpa_M_L2 < atol_L2 + atol_max = 2.0e-5 + atol_L2 = 1.0e-7 + @test dHdvperp_M_max < atol_max + @test dHdvperp_M_L2 < atol_L2 + atol_max = 2.0e-8 + atol_L2 = 7.0e-10 + @test G_M_max < atol_max + @test G_M_L2 < atol_L2 + atol_max = 2.0e-7 + atol_L2 = 4.0e-9 + @test d2Gdvpa2_M_max < atol_max + @test d2Gdvpa2_M_L2 < atol_L2 + atol_max = 2.0e-6 + atol_L2 = 2.0e-7 + @test dGdvperp_M_max < atol_max + @test dGdvperp_M_L2 < atol_L2 + atol_max = 2.0e-6 + atol_L2 = 2.0e-8 + @test d2Gdvperpdvpa_M_max < atol_max + @test d2Gdvperpdvpa_M_L2 < atol_L2 + atol_max = 3.0e-7 + atol_L2 = 2.0e-8 + @test d2Gdvperp2_M_max < atol_max + @test d2Gdvperp2_M_L2 < atol_L2 end finalize_comms!() end @@ -356,15 +356,24 @@ function runtests() begin_serial_region() @serial_region begin C_M_max, C_M_L2 = print_test_data(C_M_exact,C_M_num,C_M_err,"C_M",vpa,vperp,dummy_array,print_to_screen=print_to_screen) - if test_self_operator - rtol_max, atol_max = 6.0e-4, 6.0e-4 - rtol_L2, atol_L2 = 7.0e-6, 7.0e-6 + if test_self_operator && !test_numerical_conserving_terms && !use_Maxwellian_Rosenbluth_coefficients && !use_Maxwellian_field_particle_distribution + atol_max = 6.0e-4 + atol_L2 = 7.0e-6 + elseif test_self_operator && test_numerical_conserving_terms && !use_Maxwellian_Rosenbluth_coefficients && !use_Maxwellian_field_particle_distribution + atol_max = 7.0e-4 + atol_L2 = 7.0e-6 + elseif test_self_operator && !test_numerical_conserving_terms && use_Maxwellian_Rosenbluth_coefficients && !use_Maxwellian_field_particle_distribution + atol_max = 8.0e-4 + atol_L2 = 8.1e-6 + elseif test_self_operator && !test_numerical_conserving_terms && !use_Maxwellian_Rosenbluth_coefficients && use_Maxwellian_field_particle_distribution + atol_max = 1.1e-3 + atol_L2 = 9.0e-6 else - rtol_max, atol_max = 7.0e-2, 7.0e-2 - rtol_L2, atol_L2 = 6.0e-4, 6.0e-4 + atol_max = 7.0e-2 + atol_L2 = 6.0e-4 end - @test isapprox(C_M_max, rtol_max ; atol=atol_max) - @test isapprox(C_M_L2, rtol_L2 ; atol=atol_L2) + @test C_M_max < atol_max + @test C_M_L2 < atol_L2 # calculate the entropy production lnfC = fkpl_arrays.rhsvpavperp @loop_vperp_vpa ivperp ivpa begin From 2196a86c6ab7751bf5c6b8fb2d752273392835c0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 28 Nov 2023 11:26:20 +0000 Subject: [PATCH 291/331] Create test_scripts/ directory for storing interative and command-line test scripts. --- run_MPI_test.jl | 203 ----- run_MPI_test2D.jl | 731 ------------------ run_MPI_test_startingscript.jl | 49 -- .../2D_FEM_assembly_test.jl | 0 .../GaussLobattoLegendre_test.jl | 0 .../cheb_matrix_test.jl | 0 .../fkpl_direct_integration_test.jl | 0 .../spline_derivatives_test.jl | 0 8 files changed, 983 deletions(-) delete mode 100644 run_MPI_test.jl delete mode 100644 run_MPI_test2D.jl delete mode 100644 run_MPI_test_startingscript.jl rename 2D_FEM_assembly_test.jl => test_scripts/2D_FEM_assembly_test.jl (100%) rename GaussLobattoLegendre_test.jl => test_scripts/GaussLobattoLegendre_test.jl (100%) rename cheb_matrix_test.jl => test_scripts/cheb_matrix_test.jl (100%) rename fkpl_direct_integration_test.jl => test_scripts/fkpl_direct_integration_test.jl (100%) rename spline_derivatives_test.jl => test_scripts/spline_derivatives_test.jl (100%) diff --git a/run_MPI_test.jl b/run_MPI_test.jl deleted file mode 100644 index 818796312..000000000 --- a/run_MPI_test.jl +++ /dev/null @@ -1,203 +0,0 @@ -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - - import moment_kinetics - using moment_kinetics.input_structs: grid_input, advection_input - using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.calculus: derivative!, integral - import MPI - using Plots - MPI.Init() - comm = MPI.COMM_WORLD - nrank = MPI.Comm_size(comm) # number of ranks - irank = MPI.Comm_rank(comm) # rank of this process - #println("Hello world, I am $(irank) of $(nrank)") - MPI.Barrier(comm) - #println("comm: ",comm) - discretization = "chebyshev_pseudospectral" - - etol = 1.0e-15 - - - ################### - ## df/dx Nonperiodic (No) BC test - ################### - - # define inputs needed for the test - ngrid = 20 #number of points per element - nelement_local = 2 # number of elements per rank - nelement_global = nelement_local*nrank # total number of elements - if irank == 0 - println("ngrid = ",ngrid," nelement_local = ",nelement_local, - " nelement_global = ",nelement_global," nrank = ",nrank) - end - L = 6.0 #physical box size in reference units - bc = "" #not required to take a particular value, not used - # fd_option and adv_input not actually used so given values unimportant - fd_option = "" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - # create the 'input' struct containing input info needed to create a - # coordinate - input = grid_input("coord", ngrid, nelement_global, nelement_local, - nrank, irank, L, discretization, fd_option, bc, adv_input,comm) - # create the coordinate struct 'x' - #println("made inputs") - x = define_coordinate(input) - #println("made x") - # create arrays needed for Chebyshev pseudospectral treatment in x - # and create the plans for the forward and backward fast Chebyshev - # transforms - spectral = setup_chebyshev_pseudospectral(x) - #println("made spectral") - # create array for the function f(x) to be differentiated/integrated - f = Array{Float64,1}(undef, x.n) - g = Array{Float64,1}(undef, x.n) - x_for_plot = Array{Float64,2}(undef, x.n, nrank) - g_for_plot = Array{Float64,2}(undef, x.n, nrank) - df_for_plot = Array{Float64,2}(undef, x.n, nrank) - # create array for the derivative df/dx - df = Array{Float64,1}(undef, x.n) - # initialize f - for ix ∈ 1:x.n - f[ix] = ( (cospi(2.0*x.grid[ix]/x.L)+sinpi(2.0*x.grid[ix]/x.L)) - * exp(-x.grid[ix]^2) ) - g[ix] = (2.0*pi/x.L)*( (cospi(2.0*x.grid[ix]/x.L)-sinpi(2.0*x.grid[ix]/x.L)) - * exp(-x.grid[ix]^2) ) - 2.0*x.grid[ix]*f[ix] - end - # differentiate f - derivative!(df, f, x, spectral) - # plot df and g per process - outprefix = "run_MPI_test.plot." - plot([x.grid,x.grid], [g,df], xlabel="x", ylabel="", label=["g" "df"], - shape =:circle, markersize = 5, linewidth=2) - outfile = outprefix*string(irank)*".pdf" - savefig(outfile) - # plot df and g on rank 0 - x_for_plot .= 0.0 - g_for_plot .= 0.0 - df_for_plot .= 0.0 - for ix ∈ 1:x.n - x_for_plot[ix,irank+1] = x.grid[ix] - g_for_plot[ix,irank+1] = g[ix] - df_for_plot[ix,irank+1] = df[ix] - end - MPI.Reduce!(x_for_plot,.+,comm) - MPI.Reduce!(g_for_plot,.+,comm) - MPI.Reduce!(df_for_plot,.+,comm) - if irank == 0 - outprefix = "run_MPI_test.plot." - xlist = [x_for_plot[:,1]] - ylist = [g_for_plot[:,1]] - labels = Matrix{String}(undef, 1, 2*nrank) - labels[1] = "g" - for iproc in 2:nrank - push!(xlist,x_for_plot[:,iproc]) - push!(ylist,g_for_plot[:,iproc]) - labels[iproc] ="g" - end - push!(xlist,x_for_plot[:,1]) - push!(ylist,df_for_plot[:,1]) - labels[1+nrank]="df" - for iproc in 2:nrank - push!(xlist,x_for_plot[:,iproc]) - push!(ylist,df_for_plot[:,iproc]) - labels[iproc+nrank]="df" - end - #println(labels) - plot(xlist, ylist, xlabel="x", ylabel="", label=labels, markersize = 1, linewidth=1) - outfile = outprefix*"global.pdf" - savefig(outfile) - println(outfile) - end - # integrate df/dx - #println("x.grid",x.grid) - #println("x.wgts",x.wgts) - #println("df",df) - intdf = integral(df, x.wgts) - #println(intdf) - intdf_out = MPI.Reduce(intdf,+,comm) - # Test that error intdf is less than the specified error tolerance etol - #@test abs(intdf) < etol - if(irank == 0) - println( "abs(intdf_out) = ", abs(intdf_out), ": etol = ",etol) - end - - ################### - ## df/dx Periodic BC test - ################### - MPI.Barrier(comm) - - - bc = "periodic" - # create the 'input' struct containing input info needed to create a - # coordinate, other values taken from above - input = grid_input("coord", ngrid, nelement_global, nelement_local, - nrank, irank, L, discretization, fd_option, bc, adv_input,comm) - # create the coordinate struct 'x' - x = define_coordinate(input) - # create arrays needed for Chebyshev pseudospectral treatment in x - # and create the plans for the forward and backward fast Chebyshev - # transforms - spectral = setup_chebyshev_pseudospectral(x) - # initialize f - for ix ∈ 1:x.n - # sine wave test - f[ix] = - cospi(2.0*x.grid[ix]/x.L) +2.0*x.grid[ix]/x.L - g[ix] = (2.0*pi/x.L)*sinpi(2.0*x.grid[ix]/x.L) + 2.0/x.L - end - # differentiate f - derivative!(df, f, x, spectral) - # plot df and g per process - outprefix = "run_MPI_test.dfperiodic.plot." - plot([x.grid,x.grid], [g,df], xlabel="x", ylabel="", label=["g" "df"], - shape =:circle, markersize = 5, linewidth=2) - outfile = outprefix*string(irank)*".pdf" - savefig(outfile) - # plot df and g on rank 0 - x_for_plot .= 0.0 - g_for_plot .= 0.0 - df_for_plot .= 0.0 - for ix ∈ 1:x.n - x_for_plot[ix,irank+1] = x.grid[ix] - g_for_plot[ix,irank+1] = g[ix] - df_for_plot[ix,irank+1] = df[ix] - end - MPI.Reduce!(x_for_plot,.+,comm) - MPI.Reduce!(g_for_plot,.+,comm) - MPI.Reduce!(df_for_plot,.+,comm) - if irank == 0 - outprefix = "run_MPI_test.dfperiodic.plot." - xlist = [x_for_plot[:,1]] - ylist = [g_for_plot[:,1]] - labels = Matrix{String}(undef, 1, 2*nrank) - labels[1] = "g" - for iproc in 2:nrank - push!(xlist,x_for_plot[:,iproc]) - push!(ylist,g_for_plot[:,iproc]) - labels[iproc] ="g" - end - push!(xlist,x_for_plot[:,1]) - push!(ylist,df_for_plot[:,1]) - labels[1+nrank]="df" - for iproc in 2:nrank - push!(xlist,x_for_plot[:,iproc]) - push!(ylist,df_for_plot[:,iproc]) - labels[iproc+nrank]="df" - end - plot(xlist, ylist, xlabel="x", ylabel="", label=labels, markersize = 1, linewidth=1) - outfile = outprefix*"global.pdf" - savefig(outfile) - println(outfile) - end - # integrate df/dx - intdf = integral(df, x.wgts) - intdf_out = MPI.Reduce(intdf,+,comm) - # Test that error intdf -2.0 is less than the specified error tolerance etol - #@test abs(intdf) < etol - if(irank == 0) - println( "abs(intdf_out-2.0) = ", abs(intdf_out-2.0), ": etol = ",etol) - end - MPI.Finalize() -end \ No newline at end of file diff --git a/run_MPI_test2D.jl b/run_MPI_test2D.jl deleted file mode 100644 index 50816ec00..000000000 --- a/run_MPI_test2D.jl +++ /dev/null @@ -1,731 +0,0 @@ -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - - import moment_kinetics - using moment_kinetics.input_structs: grid_input, advection_input - using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.calculus: derivative!, integral - #using coordinates: coordinate_info - import MPI - using Plots - - function reconcile_element_boundaries_MPI!(df1d::Array{Float64,Ndims}, - dfdx_lower_endpoints::Array{Float64,N}, dfdx_upper_endpoints::Array{Float64,N}, - send_buffer::Array{Float64,N}, receive_buffer::Array{Float64,N}, coord) where {Ndims,N} - - #counter to test if endpoint data assigned - assignment_counter = 0 - - # now deal with endpoints that are stored across ranks - comm = coord.comm - nrank = coord.nrank - irank = coord.irank - #send_buffer = coord.send_buffer - #receive_buffer = coord.receive_buffer - # sending pattern is cyclic. First we send data form irank -> irank + 1 - # to fix the lower endpoints, then we send data from irank -> irank - 1 - # to fix upper endpoints. Special exception for the periodic points. - # receive_buffer[1] is for data received, send_buffer[1] is data to be sent - - send_buffer .= dfdx_upper_endpoints #highest end point on THIS rank - # pass data from irank -> irank + 1, receive data from irank - 1 - idst = mod(irank+1,nrank) # destination rank for sent data - isrc = mod(irank-1,nrank) # source rank for received data - #MRH what value should tag take here and below? Esp if nrank >= 32 - rreq = MPI.Irecv!(receive_buffer, comm; source=isrc, tag=isrc+32) - sreq = MPI.Isend(send_buffer, comm; dest=idst, tag=irank+32) - #print("$irank: Sending $irank -> $idst = $send_buffer\n") - stats = MPI.Waitall([rreq, sreq]) - #print("$irank: Received $isrc -> $irank = $receive_buffer\n") - MPI.Barrier(comm) - - # no update receive buffer, taking into account the reconciliation - if irank == 0 - if coord.bc == "periodic" - #update the extreme lower endpoint with data from irank = nrank -1 - receive_buffer .= 0.5*(receive_buffer .+ dfdx_lower_endpoints) - else #directly use value from Cheb - receive_buffer .= dfdx_lower_endpoints - end - else # enforce continuity at lower endpoint - receive_buffer .= 0.5*(receive_buffer .+ dfdx_lower_endpoints) - end - - #now update the df1d array -- using a slice appropriate to the dimension reconciled - # 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("coord.name: ",coord.name," Ndims: ",Ndims) - if coord.name == "x" && Ndims==2 - df1d[1,:] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name == "x" && Ndims==3 - df1d[1,:,:] .= receive_buffer[:,:] - assignment_counter += 1 - elseif coord.name == "y" && Ndims==2 - df1d[:,1] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name == "y" && Ndims==3 - df1d[:,1,:] .= receive_buffer[:,:] - assignment_counter += 1 - end - - send_buffer .= dfdx_lower_endpoints #lowest end point on THIS rank - # pass data from irank -> irank - 1, receive data from irank + 1 - idst = mod(irank-1,nrank) # destination rank for sent data - isrc = mod(irank+1,nrank) # source rank for received data - #MRH what value should tag take here and below? Esp if nrank >= 32 - rreq = MPI.Irecv!(receive_buffer, comm; source=isrc, tag=isrc+32) - sreq = MPI.Isend(send_buffer, comm; dest=idst, tag=irank+32) - #print("$irank: Sending $irank -> $idst = $send_buffer\n") - stats = MPI.Waitall([rreq, sreq]) - #print("$irank: Received $isrc -> $irank = $receive_buffer\n") - MPI.Barrier(comm) - - if irank == nrank-1 - if coord.bc == "periodic" - #update the extreme upper endpoint with data from irank = 0 - receive_buffer .= 0.5*(receive_buffer .+ dfdx_upper_endpoints) - else #directly use value from Cheb - receive_buffer .= dfdx_upper_endpoints - end - else # enforce continuity at upper endpoint - receive_buffer .= 0.5*(receive_buffer .+ dfdx_upper_endpoints) - end - - #now update the df1d array -- using a slice appropriate to the dimension reconciled - # 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("coord.name: ",coord.name," Ndims: ",Ndims) - if coord.name=="x" && Ndims ==2 - df1d[end,:] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name=="x" && Ndims ==3 - df1d[end,:,:] .= receive_buffer[:,:] - assignment_counter += 1 - elseif coord.name=="y" && Ndims ==2 - df1d[:,end] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name=="y" && Ndims ==3 - df1d[:,end,:] .= receive_buffer[:,:] - assignment_counter += 1 - end - - if !(assignment_counter == 2) - println("ERROR: failure to assign endpoints in reconcile_element_boundaries_MPI! (centered): coord.name: ",coord.name," Ndims: ",Ndims) - end - end - - function apply_adv_fac!(buffer::Array{Float64,Ndims},adv_fac::Array{Float64,Ndims},endpoints::Array{Float64,Ndims},sgn::Int64) where Ndims - #buffer contains off-process endpoint - #adv_fac < 0 is positive advection speed - #adv_fac > 0 is negative advection speed - #endpoint is local on-process endpoint - #sgn = 1 for send irank -> irank + 1 - #sgn = -1 for send irank + 1 -> irank - #loop over all indices in array - for i in eachindex(buffer,adv_fac,endpoints) - if sgn*adv_fac[i] > 0.0 - # replace buffer value (c with endpoint value - buffer[i] = endpoints[i] - elseif sgn*adv_fac[i] < 0.0 - break #do nothing - else #average values - buffer[i] = 0.5*(buffer[i] + endpoints[i]) - end - end - - end - - function reconcile_element_boundaries_MPI!(df1d::Array{Float64,Ndims}, - adv_fac_lower_endpoints::Array{Float64,N}, adv_fac_upper_endpoints::Array{Float64,N}, - dfdx_lower_endpoints::Array{Float64,N}, dfdx_upper_endpoints::Array{Float64,N}, - send_buffer::Array{Float64,N}, receive_buffer::Array{Float64,N}, coord) where {Ndims,N} - - #counter to test if endpoint data assigned - assignment_counter = 0 - - # now deal with endpoints that are stored across ranks - comm = coord.comm - nrank = coord.nrank - irank = coord.irank - #send_buffer = coord.send_buffer - #receive_buffer = coord.receive_buffer - # sending pattern is cyclic. First we send data form irank -> irank + 1 - # to fix the lower endpoints, then we send data from irank -> irank - 1 - # to fix upper endpoints. Special exception for the periodic points. - # receive_buffer[1] is for data received, send_buffer[1] is data to be sent - - send_buffer .= dfdx_upper_endpoints #highest end point on THIS rank - # pass data from irank -> irank + 1, receive data from irank - 1 - idst = mod(irank+1,nrank) # destination rank for sent data - isrc = mod(irank-1,nrank) # source rank for received data - #MRH what value should tag take here and below? Esp if nrank >= 32 - rreq = MPI.Irecv!(receive_buffer, comm; source=isrc, tag=isrc+32) - sreq = MPI.Isend(send_buffer, comm; dest=idst, tag=irank+32) - #print("$irank: Sending $irank -> $idst = $send_buffer\n") - stats = MPI.Waitall([rreq, sreq]) - #print("$irank: Received $isrc -> $irank = $receive_buffer\n") - MPI.Barrier(comm) - - # no update receive buffer, taking into account the reconciliation - if irank == 0 - if coord.bc == "periodic" - # depending on adv_fac, update the extreme lower endpoint with data from irank = nrank -1 - apply_adv_fac!(receive_buffer,adv_fac_lower_endpoints,1,dfdx_lower_endpoints) - else # directly use value from Cheb at extreme lower point - receive_buffer .= dfdx_lower_endpoints - end - else # depending on adv_fac, update the lower endpoint with data from irank = nrank -1 - apply_adv_fac!(receive_buffer,adv_fac_lower_endpoints,dfdx_lower_endpoints,1) - end - - #now update the df1d array -- using a slice appropriate to the dimension reconciled - # 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("coord.name: ",coord.name," Ndims: ",Ndims) - if coord.name == "x" && Ndims==2 - df1d[1,:] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name == "x" && Ndims==3 - df1d[1,:,:] .= receive_buffer[:,:] - assignment_counter += 1 - elseif coord.name == "y" && Ndims==2 - df1d[:,1] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name == "y" && Ndims==3 - df1d[:,1,:] .= receive_buffer[:,:] - assignment_counter += 1 - end - - send_buffer .= dfdx_lower_endpoints #lowest end point on THIS rank - # pass data from irank -> irank - 1, receive data from irank + 1 - idst = mod(irank-1,nrank) # destination rank for sent data - isrc = mod(irank+1,nrank) # source rank for received data - #MRH what value should tag take here and below? Esp if nrank >= 32 - rreq = MPI.Irecv!(receive_buffer, comm; source=isrc, tag=isrc+32) - sreq = MPI.Isend(send_buffer, comm; dest=idst, tag=irank+32) - #print("$irank: Sending $irank -> $idst = $send_buffer\n") - stats = MPI.Waitall([rreq, sreq]) - #print("$irank: Received $isrc -> $irank = $receive_buffer\n") - MPI.Barrier(comm) - - if irank == nrank-1 - if coord.bc == "periodic" - # depending on adv_fac, update the extreme upper endpoint with data from irank = 0 - apply_adv_fac!(receive_buffer,adv_fac_lower_endpoints,-1,dfdx_upper_endpoints) - else #directly use value from Cheb - receive_buffer .= dfdx_upper_endpoints - end - else # enforce continuity at upper endpoint - apply_adv_fac!(receive_buffer,adv_fac_lower_endpoints,dfdx_upper_endpoints,-1) - end - - #now update the df1d array -- using a slice appropriate to the dimension reconciled - # 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("coord.name: ",coord.name," Ndims: ",Ndims) - if coord.name=="x" && Ndims ==2 - df1d[end,:] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name=="x" && Ndims ==3 - df1d[end,:,:] .= receive_buffer[:,:] - assignment_counter += 1 - elseif coord.name=="y" && Ndims ==2 - df1d[:,end] .= receive_buffer[:] - assignment_counter += 1 - elseif coord.name=="y" && Ndims ==3 - df1d[:,end,:] .= receive_buffer[:,:] - assignment_counter += 1 - end - - if !(assignment_counter == 2) - println("ERROR: failure to assign endpoints in reconcile_element_boundaries_MPI! (upwind): coord.name: ",coord.name," Ndims: ",Ndims) - end - end - - #3D version - function derivative_x!(dfdx::Array{Float64,3},f::Array{Float64,3}, - dfdx_lower_endpoints::Array{Float64,2}, dfdx_upper_endpoints::Array{Float64,2}, - x_send_buffer::Array{Float64,2},x_receive_buffer::Array{Float64,2}, - x_spectral,x,y,z) - - # differentiate f w.r.t x - for iz in 1:z.n - for iy in 1:y.n - @views derivative!(dfdx[:,iy,iz], f[:,iy,iz], x, adv_fac[:,iy,iz], x_spectral) - # get external endpoints to reconcile via MPI - dfdx_lower_endpoints[iy,iz] = x.scratch_2d[1,1] - dfdx_upper_endpoints[iy,iz] = x.scratch_2d[end,end] - end - end - # now reconcile element boundaries across - # processes with large message involving all y - if x.nelement_local < x.nelement_global - reconcile_element_boundaries_MPI!(dfdx, - adv_fac[1,:,:], adv_fac[end,:,:], - dfdx_lower_endpoints,dfdx_upper_endpoints, - x_send_buffer, x_receive_buffer, x) - end - - end - - #3D version with upwind - function derivative_x!(dfdx::Array{Float64,3},f::Array{Float64,3}, - dfdx_lower_endpoints::Array{Float64,2}, dfdx_upper_endpoints::Array{Float64,2}, - x_send_buffer::Array{Float64,2},x_receive_buffer::Array{Float64,2}, - x_spectral,x,y,z,adv_fac::Array{Float64,3}, - adv_fac_lower_buffer::Array{Float64,2},adv_fac_upper_buffer::Array{Float64,2}) - - # differentiate f w.r.t x - for iz in 1:z.n - for iy in 1:y.n - @views derivative!(dfdx[:,iy,iz], f[:,iy,iz], x, x_spectral) - # get external endpoints to reconcile via MPI - dfdx_lower_endpoints[iy,iz] = x.scratch_2d[1,1] - dfdx_upper_endpoints[iy,iz] = x.scratch_2d[end,end] - adv_fac_lower_buffer[iy,iz] = adv_fac[1,iy,iz] - adv_fac_upper_buffer[iy,iz] = adv_fac[end,iy,iz] - end - end - # now reconcile element boundaries across - # processes with large message involving all y - if x.nelement_local < x.nelement_global - reconcile_element_boundaries_MPI!(dfdx, - adv_fac_lower_buffer, adv_fac_upper_buffer, - dfdx_lower_endpoints,dfdx_upper_endpoints, - x_send_buffer, x_receive_buffer, x) - end - - end - - #2D version - function derivative_x!(dfdx::Array{Float64,2},f::Array{Float64,2}, - dfdx_lower_endpoints::Array{Float64,1}, dfdx_upper_endpoints::Array{Float64,1}, - x_send_buffer::Array{Float64,1},x_receive_buffer::Array{Float64,1}, - x_spectral,x,y) - - # differentiate f w.r.t x - for iy in 1:y.n - @views derivative!(dfdx[:,iy], f[:,iy], x, x_spectral) - # get external endpoints to reconcile via MPI - dfdx_lower_endpoints[iy] = x.scratch_2d[1,1] - dfdx_upper_endpoints[iy] = x.scratch_2d[end,end] - end - # now reconcile element boundaries across - # processes with large message involving all y - if x.nelement_local < x.nelement_global - reconcile_element_boundaries_MPI!(dfdx, - dfdx_lower_endpoints,dfdx_upper_endpoints, - x_send_buffer, x_receive_buffer, x) - end - - end - - #2D version with upwind - function derivative_x!(dfdx::Array{Float64,2},f::Array{Float64,2}, - dfdx_lower_endpoints::Array{Float64,1}, dfdx_upper_endpoints::Array{Float64,1}, - x_send_buffer::Array{Float64,1},x_receive_buffer::Array{Float64,1}, - x_spectral,x,y,adv_fac::Array{Float64,2}, - adv_fac_lower_buffer::Array{Float64,1},adv_fac_upper_buffer::Array{Float64,1}) - - # differentiate f w.r.t x - for iy in 1:y.n - @views derivative!(dfdx[:,iy], f[:,iy], x, adv_fac[:,iy], x_spectral) - # get external endpoints to reconcile via MPI - dfdx_lower_endpoints[iy] = x.scratch_2d[1,1] - dfdx_upper_endpoints[iy] = x.scratch_2d[end,end] - adv_fac_lower_buffer[iy] = adv_fac[1,iy] - adv_fac_upper_buffer[iy] = adv_fac[end,iy] - end - # now reconcile element boundaries across - # processes with large message involving all y - # pass adv_fac[1,:] -- lower x endpoints for all y - # and adv_fac[end,:] -- upper x endpoints for all y - if x.nelement_local < x.nelement_global - @views reconcile_element_boundaries_MPI!(dfdx, - adv_fac_lower_buffer, adv_fac_upper_buffer, - dfdx_lower_endpoints,dfdx_upper_endpoints, - x_send_buffer, x_receive_buffer, x) - end - - end - - #3D version - function derivative_y!(dfdy::Array{Float64,3},f::Array{Float64,3}, - dfdy_lower_endpoints::Array{Float64,2}, dfdy_upper_endpoints::Array{Float64,2}, - y_send_buffer::Array{Float64,2},y_receive_buffer::Array{Float64,2}, - y_spectral,x,y,z) - - # differentiate f w.r.t y - for iz in 1:z.n - for ix in 1:x.n - @views derivative!(dfdy[ix,:,iz], f[ix,:,iz], y, y_spectral) - # get external endpoints to reconcile via MPI - dfdy_lower_endpoints[ix,iz] = y.scratch_2d[1,1] - dfdy_upper_endpoints[ix,iz] = y.scratch_2d[end,end] - end - end - # now reconcile element boundaries across - # processes with large message involving all y - if y.nelement_local < y.nelement_global - reconcile_element_boundaries_MPI!(dfdy, - dfdy_lower_endpoints,dfdy_upper_endpoints, - y_send_buffer, y_receive_buffer, y) - end - end - - #2D version - function derivative_y!(dfdy::Array{Float64,2},f::Array{Float64,2}, - dfdy_lower_endpoints::Array{Float64,1}, dfdy_upper_endpoints::Array{Float64,1}, - y_send_buffer::Array{Float64,1},y_receive_buffer::Array{Float64,1}, - y_spectral,x,y) - - # differentiate f w.r.t y - for ix in 1:x.n - @views derivative!(dfdy[ix,:], f[ix,:], y, y_spectral) - # get external endpoints to reconcile via MPI - dfdy_lower_endpoints[ix] = y.scratch_2d[1,1] - dfdy_upper_endpoints[ix] = y.scratch_2d[end,end] - end - # now reconcile element boundaries across - # processes with large message involving all y - if y.nelement_local < y.nelement_global - reconcile_element_boundaries_MPI!(dfdy, - dfdy_lower_endpoints,dfdy_upper_endpoints, - y_send_buffer, y_receive_buffer, y) - end - end - - # define inputs needed for the xy calculus test - # nrank must be nrank = y_nblocks*x_nblocks, i.e., - # mpirun -n nrank run_MPI_test2D.jl - x_ngrid = 10 #number of points per element - x_nelement_local = 1 - x_nelement_global = 1 # number of elements - y_ngrid = 12 - y_nelement_local = 1 - y_nelement_global = 4 - - y_nblocks = floor(Int,x_nelement_global/x_nelement_local) - x_nblocks = floor(Int,y_nelement_global/y_nelement_local) - - MPI.Init() - comm = MPI.COMM_WORLD - nrank = MPI.Comm_size(comm) # number of ranks - irank = MPI.Comm_rank(comm) # rank of this process - MPI.Barrier(comm) - - - if irank == 0 - println("x_ngrid = ",x_ngrid," x_nelement_local = ",x_nelement_local, - " x_nelement_global = ",x_nelement_global) - println("y_ngrid = ",y_ngrid," y_nelement_local = ", - y_nelement_local," y_nelement_global = ",y_nelement_global) - println("nrank: ",nrank) - println("y_nblocks: ",y_nblocks) - println("x_nblocks: ",x_nblocks) - - - end - - discretization = "chebyshev_pseudospectral" - etol = 1.0e-15 - - y_nrank_per_block = floor(Int,nrank/y_nblocks) - y_iblock = mod(irank,y_nblocks) # irank - > y_iblock - y_irank_sub = floor(Int,irank/y_nblocks) # irank -> y_irank_sub - # irank = y_iblock + x_nrank_per_block * y_irank_sub - # useful information for debugging - #println("y_nrank_per_block: ",y_nrank_per_block) - #println("y_iblock: ",y_iblock) - #println("y_irank_sub: ",y_irank_sub) - - x_nrank_per_block = floor(Int,nrank/x_nblocks) - x_iblock = y_irank_sub # irank - > x_iblock - x_irank_sub = y_iblock # irank -> x_irank_sub - # irank = x_iblock * x_nrank_per_block + x_irank_sub - # useful information for debugging - #println("x_nrank_per_block: ",x_nrank_per_block) - #println("x_iblock: ",x_iblock) - #println("x_irank_sub: ",x_irank_sub) - - # MPI.Comm_split(comm,color,key) - # comm -> communicator to be split - # color -> label of group of processes - # key -> label of process in group - y_comm_sub = MPI.Comm_split(comm,y_iblock,y_irank_sub) - x_comm_sub = MPI.Comm_split(comm,x_iblock,x_irank_sub) - - L = 6.0 #physical box size in reference units - bc = "" #not required to take a particular value, not used - # fd_option and adv_input not actually used so given values unimportant - fd_option = "" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - # create the 'input' struct containing input info needed to create a coordinate - y_input = grid_input("y", y_ngrid, y_nelement_global, y_nelement_local, - y_nrank_per_block, y_irank_sub, L, discretization, fd_option, bc, adv_input,y_comm_sub) - x_input = grid_input("x", x_ngrid, x_nelement_global, x_nelement_local, - x_nrank_per_block, x_irank_sub, L, discretization, fd_option, bc, adv_input,x_comm_sub) - # z dimension kept entirely local - z_ngrid = 7 - z_nelement_local = 1 - z_nelement_global = 1 - z_nrank_per_block = 0 # dummy value - z_irank_sub = 0 #dummy value - z_comm_sub = false #dummy value - z_input = grid_input("z", z_ngrid, x_nelement_global, x_nelement_local, - x_nrank_per_block, x_irank_sub, L, discretization, fd_option, bc, adv_input,z_comm_sub) - z = define_coordinate(z_input) - - # create the coordinate struct 'x' - y = define_coordinate(y_input) - x = define_coordinate(x_input) - # create arrays needed for Chebyshev pseudospectral treatment in x - y_spectral = setup_chebyshev_pseudospectral(y) - x_spectral = setup_chebyshev_pseudospectral(x) - - - # create a 3D array for the function f(x,y) to be differentiated/integrated - df3Ddx = Array{Float64,3}(undef, x.n, y.n, z.n) - f3D = Array{Float64,3}(undef, x.n, y.n, z.n) - adv_fac3D = Array{Float64,3}(undef, x.n, y.n, z.n) - g3D = Array{Float64,3}(undef, x.n, y.n, z.n) - dk3Ddy = Array{Float64,3}(undef, x.n, y.n, z.n) - k3D = Array{Float64,3}(undef, x.n, y.n, z.n) - h3D = Array{Float64,3}(undef, x.n, y.n, z.n) - # create a 2D array for the function f(x,y) to be differentiated/integrated - k = Array{Float64,2}(undef, x.n, y.n) - f = Array{Float64,2}(undef, x.n, y.n) - adv_fac = Array{Float64,2}(undef, x.n, y.n) - g = Array{Float64,2}(undef, x.n, y.n) - h = Array{Float64,2}(undef, x.n, y.n) - # create array for the derivative df/dx - dfdx = Array{Float64,2}(undef, x.n, y.n) - dkdy = Array{Float64,2}(undef, x.n, y.n) - - y_for_plot = Array{Float64,2}(undef, y.n, nrank) - x_for_plot = Array{Float64,2}(undef, x.n, nrank) - h_for_plot = Array{Float64,3}(undef, x.n, y.n, nrank) - g_for_plot = Array{Float64,3}(undef, x.n, y.n, nrank) - dfdx_for_plot = Array{Float64,3}(undef, x.n, y.n, nrank) - dkdy_for_plot = Array{Float64,3}(undef, x.n, y.n, nrank) - - # initialize f - - #println("x",x.grid) - #println("y",y.grid) - for iz ∈ 1:z.n - for iy ∈ 1:y.n - for ix ∈ 1:x.n - #adv_fac3D[ix,iy,iz] = 1.0 # so always take upper endpoint - adv_fac3D[ix,iy,iz] = -1.0 # so always take lower endpoint - f3D[ix,iy,iz] = sinpi(2.0*x.grid[ix]/x.L) #*sinpi(2.0*y.grid[iy]/y.L) - g3D[ix,iy,iz] = (2.0*pi/x.L)*cospi(2.0*x.grid[ix]/x.L) #*sinpi(2.0*y.grid[iy]/y.L) - k3D[ix,iy,iz] = sinpi(2.0*y.grid[iy]/y.L) #*sinpi(2.0*y.grid[iy]/y.L) - h3D[ix,iy,iz] = (2.0*pi/y.L)*cospi(2.0*y.grid[iy]/y.L) #*cospi(2.0*x.grid[ix]/x.L) - end - end - end - - for iy ∈ 1:y.n - for ix ∈ 1:x.n - #adv_fac[ix,iy] = 1.0 # so always take upper endpoint - adv_fac[ix,iy] = -1.0 # so always take lower endpoint - k[ix,iy] = sinpi(2.0*y.grid[iy]/y.L) #*sinpi(2.0*y.grid[iy]/y.L) - f[ix,iy] = sinpi(2.0*x.grid[ix]/x.L) #*sinpi(2.0*y.grid[iy]/y.L) - g[ix,iy] = (2.0*pi/x.L)*cospi(2.0*x.grid[ix]/x.L) #*sinpi(2.0*y.grid[iy]/y.L) - h[ix,iy] = (2.0*pi/y.L)*cospi(2.0*y.grid[iy]/y.L) #*cospi(2.0*x.grid[ix]/x.L) - end - end - - #required buffer arrays - x_send_buffer = Array{Float64,1}(undef,y.n) - x_receive_buffer = Array{Float64,1}(undef,y.n) - dfdx_lower_endpoints = Array{Float64,1}(undef,y.n) - dfdx_upper_endpoints = Array{Float64,1}(undef,y.n) - adv_lw = Array{Float64,1}(undef, y.n) - adv_up = Array{Float64,1}(undef, y.n) - # differentiate f w.r.t x - #derivative_x!(dfdx,f,dfdx_lower_endpoints,dfdx_upper_endpoints,x_send_buffer,x_receive_buffer,x_spectral,x,y) - derivative_x!(dfdx,f,dfdx_lower_endpoints,dfdx_upper_endpoints,x_send_buffer,x_receive_buffer,x_spectral,x,y,adv_fac,adv_lw,adv_up) - - #required buffer arrays - x3D_send_buffer = Array{Float64,2}(undef,y.n,z.n) - x3D_receive_buffer = Array{Float64,2}(undef,y.n,z.n) - df3Ddx_lower_endpoints = Array{Float64,2}(undef,y.n,z.n) - df3Ddx_upper_endpoints = Array{Float64,2}(undef,y.n,z.n) - adv_lw3D = Array{Float64,2}(undef, y.n, z.n) - adv_up3D = Array{Float64,2}(undef, y.n, z.n) - # differentiate f w.r.t x - #derivative_x!(df3Ddx,f3D,df3Ddx_lower_endpoints,df3Ddx_upper_endpoints,x3D_send_buffer,x3D_receive_buffer,x_spectral,x,y,z) - derivative_x!(df3Ddx,f3D,df3Ddx_lower_endpoints,df3Ddx_upper_endpoints,x3D_send_buffer,x3D_receive_buffer,x_spectral,x,y,z,adv_fac3D,adv_lw3D,adv_up3D) - - #required buffer arrays - y_send_buffer = Array{Float64,1}(undef,x.n) - y_receive_buffer = Array{Float64,1}(undef,x.n) - dkdy_lower_endpoints = Array{Float64,1}(undef,x.n) - dkdy_upper_endpoints = Array{Float64,1}(undef,x.n) - # differentiate k w.r.t y - derivative_y!(dkdy,k,dkdy_lower_endpoints,dkdy_upper_endpoints,y_send_buffer,y_receive_buffer,y_spectral,x,y) - - y3D_send_buffer = Array{Float64,2}(undef,x.n,z.n) - y3D_receive_buffer = Array{Float64,2}(undef,x.n,z.n) - dk3Ddy_lower_endpoints = Array{Float64,2}(undef,x.n,z.n) - dk3Ddy_upper_endpoints = Array{Float64,2}(undef,x.n,z.n) - # differentiate f w.r.t x - derivative_y!(dk3Ddy,k3D,dk3Ddy_lower_endpoints,dk3Ddy_upper_endpoints,y3D_send_buffer,y3D_receive_buffer,y_spectral,x,y,z) - - # Test that error intdf is less than the specified error tolerance etol - #@test abs(intdf) < etol - # here we do a 1D integral in the x and y dimensions separately - - for iz in 1:1 - for iy in 1:1 - intdf3D = integral(df3Ddx[:,iy,iz], x.wgts) - intdf3D_out = MPI.Reduce(intdf3D,+,x.comm) - if(x_irank_sub == 0 && x_iblock == 0) - println( "abs(intdf3D_out) = ", abs(intdf3D_out), ": etol = ",etol) - end - end - end - - for iy in 1:1 - intdf = integral(dfdx[:,iy], x.wgts) - intdf_out = MPI.Reduce(intdf,+,x.comm) - if(x_irank_sub == 0 && x_iblock == 0) - println( "abs(intdf_out) = ", abs(intdf_out), ": etol = ",etol) - end - end - - for iz in 1:1 - for ix in 1:1 - intdk3D = integral(dkdy[ix,:,iz], y.wgts) - intdk3D_out = MPI.Reduce(intdk3D,+,y.comm) - if(y_irank_sub == 0 && y_iblock == 0) - println( "abs(intdk3D_out) = ", abs(intdk3D_out), ": etol = ",etol) - end - end - end - - for ix in 1:1 - intdk = integral(dkdy[ix,:], y.wgts) - intdk_out = MPI.Reduce(intdk,+,y.comm) - if(y_irank_sub == 0 && y_iblock == 0) - println( "abs(intdk_out) = ", abs(intdk_out), ": etol = ",etol) - end - end - #println(intdf) - - - - #if(irank == 0) - #println( "abs(intdf_out) = ", abs(intdf_out), ": etol = ",etol) - #end - - - # plot df g h per process - outprefix = "run_MPI_test2D.plot." - for iy in 1:1 - plot([x.grid,x.grid], [g[:,iy],dfdx[:,iy]], xlabel="x", ylabel="", label=["g" "df/dx"], - line = (2, [:solid :dash]), markersize = 2, linewidth=1) - outfile = outprefix*"iy."*string(iy)*"."*string(irank)*".pdf" - savefig(outfile) - end - for ix in 1:1 - plot([y.grid,y.grid], [h[ix,:],dkdy[ix,:]], xlabel="y", ylabel="", label=["h" "dk/dy"], - line = (2, [:solid :dash]), markersize = 2, linewidth=1) - outfile = outprefix*"ix."*string(ix)*"."*string(irank)*".pdf" - savefig(outfile) - end - - # get data onto irank = 0 for plotting - - y_for_plot .= 0.0 - x_for_plot .= 0.0 - h_for_plot .= 0.0 - g_for_plot .= 0.0 - dfdx_for_plot .= 0.0 - dkdy_for_plot .= 0.0 - - y_for_plot[:,irank+1] .= y.grid[:] - x_for_plot[:,irank+1] .= x.grid[:] - h_for_plot[:,:,irank+1] .= h[:,:] - g_for_plot[:,:,irank+1] .= g[:,:] - dfdx_for_plot[:,:,irank+1] .= dfdx[:,:] - dkdy_for_plot[:,:,irank+1] .= dkdy[:,:] - - MPI.Reduce!(y_for_plot,.+,comm) - MPI.Reduce!(x_for_plot,.+,comm) - MPI.Reduce!(g_for_plot,.+,comm) - MPI.Reduce!(h_for_plot,.+,comm) - MPI.Reduce!(dfdx_for_plot,.+,comm) - MPI.Reduce!(dkdy_for_plot,.+,comm) - - #plot the data after the reduction operation - if irank == 0 - for iy in 1:1 - # plot all x blocks with iy = 1 - for x_iblockprim = 0:x_nblocks-1 - xlist = [] - func_list = [] - labels = Matrix{String}(undef, 1, 2*x_nrank_per_block) - - for iproc in 0:x_nrank_per_block-1 - irankprim = x_iblockprim * x_nrank_per_block + iproc - push!(xlist,x_for_plot[:,irankprim+1]) - push!(func_list,g_for_plot[:,iy,irankprim+1]) - labels[iproc+1] ="g" - end - - for iproc in 0:x_nrank_per_block-1 - irankprim = x_iblockprim * x_nrank_per_block + iproc - push!(xlist,x_for_plot[:,irankprim+1]) - push!(func_list,dfdx_for_plot[:,iy,irankprim+1]) - labels[iproc+1+x_nrank_per_block] ="df/dx" - end - #println(xlist) - #println(func_list) - #println(labels) - plot(xlist, func_list, xlabel="x", ylabel="", label=labels, markersize = 1, linewidth=1) - outfile = "run_MPI_test2D.plot.iy."*string(iy)*".x_iblock."*string(x_iblockprim)*".global.pdf" - savefig(outfile) - println(outfile) - end - end - - for ix in 1:1 - # plot all y blocks with ix = 1 - for y_iblockprim = 0:y_nblocks-1 - xlist = [] - func_list = [] - labels = Matrix{String}(undef, 1, 2*y_nrank_per_block) - - for iproc in 0:y_nrank_per_block-1 - irankprim = y_iblockprim + x_nrank_per_block * iproc - push!(xlist,y_for_plot[:,irankprim+1]) - push!(func_list,h_for_plot[ix,:,irankprim+1]) - labels[iproc+1] ="h" - end - - for iproc in 0:y_nrank_per_block-1 - irankprim = y_iblockprim + x_nrank_per_block * iproc - push!(xlist,y_for_plot[:,irankprim+1]) - push!(func_list,dkdy_for_plot[ix,:,irankprim+1]) - labels[iproc+1+y_nrank_per_block] ="dk/dy" - end - #println(xlist) - #println(func_list) - #println(labels) - plot(xlist, func_list, xlabel="y", ylabel="", label=labels, markersize = 1, linewidth=1) - outfile = "run_MPI_test2D.plot.ix."*string(ix)*".y_iblock."*string(y_iblockprim)*".global.pdf" - savefig(outfile) - println(outfile) - end - end - end - - MPI.Finalize() -end - \ No newline at end of file diff --git a/run_MPI_test_startingscript.jl b/run_MPI_test_startingscript.jl deleted file mode 100644 index 696a89293..000000000 --- a/run_MPI_test_startingscript.jl +++ /dev/null @@ -1,49 +0,0 @@ -if abspath(PROGRAM_FILE) == @__FILE__ - using Pkg - Pkg.activate(".") - - import moment_kinetics - using moment_kinetics.input_structs: grid_input, advection_input - using moment_kinetics.coordinates: define_coordinate - using moment_kinetics.chebyshev: setup_chebyshev_pseudospectral - using moment_kinetics.calculus: derivative!, integral - - discretization = "chebyshev_pseudospectral" - - etol = 1.0e-15 - # define inputs needed for the test - ngrid = 8 - nelement = 5 - L = 6.0 - bc = "periodic" - # fd_option and adv_input not actually used so given values unimportant - fd_option = "" - adv_input = advection_input("default", 1.0, 0.0, 0.0) - # create the 'input' struct containing input info needed to create a - # coordinate - input = grid_input("coord", ngrid, nelement, L, - discretization, fd_option, bc, adv_input) - # create the coordinate struct 'x' - x = define_coordinate(input) - # create arrays needed for Chebyshev pseudospectral treatment in x - # and create the plans for the forward and backward fast Chebyshev - # transforms - spectral = setup_chebyshev_pseudospectral(x) - # create array for the function f(x) to be differentiated/integrated - f = Array{Float64,1}(undef, x.n) - # create array for the derivative df/dx - df = Array{Float64,1}(undef, x.n) - # initialize f - for ix ∈ 1:x.n - f[ix] = ( (cospi(2.0*x.grid[ix]/x.L)+sinpi(2.0*x.grid[ix]/x.L)) - * exp(-x.grid[ix]^2) ) - end - # differentiate f - derivative!(df, f, x, spectral) - # integrate df/dx - intdf = integral(df, x.wgts) - - # Test that error intdf is less than the specified error tolerance etol - #@test abs(intdf) < etol - println( "abs(intdf) = ", abs(intdf), ": etol = ",etol) -end \ No newline at end of file diff --git a/2D_FEM_assembly_test.jl b/test_scripts/2D_FEM_assembly_test.jl similarity index 100% rename from 2D_FEM_assembly_test.jl rename to test_scripts/2D_FEM_assembly_test.jl diff --git a/GaussLobattoLegendre_test.jl b/test_scripts/GaussLobattoLegendre_test.jl similarity index 100% rename from GaussLobattoLegendre_test.jl rename to test_scripts/GaussLobattoLegendre_test.jl diff --git a/cheb_matrix_test.jl b/test_scripts/cheb_matrix_test.jl similarity index 100% rename from cheb_matrix_test.jl rename to test_scripts/cheb_matrix_test.jl diff --git a/fkpl_direct_integration_test.jl b/test_scripts/fkpl_direct_integration_test.jl similarity index 100% rename from fkpl_direct_integration_test.jl rename to test_scripts/fkpl_direct_integration_test.jl diff --git a/spline_derivatives_test.jl b/test_scripts/spline_derivatives_test.jl similarity index 100% rename from spline_derivatives_test.jl rename to test_scripts/spline_derivatives_test.jl From e6ca1af5009114eb401a81189ee31d591a1ab569 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 28 Nov 2023 12:16:27 +0000 Subject: [PATCH 292/331] Correct typo in post_processing.jl, presumably from merge. --- src/post_processing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/post_processing.jl b/src/post_processing.jl index 34823dc47..984f681ca 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -2051,7 +2051,7 @@ function plot_moments(density, delta_density, density_fldline_avg, # plot the time trace of n_s(z=z0)-density_fldline_avg plot(legend=legend) for (t, dupar, run_label) ∈ zip(time, delta_upar, run_name_labels) - @views plot!(t, abs.(du[iz0,is,:]), yaxis=:log, label=run_label) + @views plot!(t, abs.(dupar[iz0,is,:]), yaxis=:log, label=run_label) end outfile = string(prefix, "_$(label)_delta_upar0_vs_t_spec", spec_string, ".pdf") trysavefig(outfile) From 8b9e8724792fa62139b058d066555d1bba83e575 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 29 Nov 2023 10:12:56 +0000 Subject: [PATCH 293/331] Add calls to run_assembly_test() to generate the plots for the Fokker-Planck operator report from a single call to 2D_FEM_assembly_test() from the command line. --- test_scripts/2D_FEM_assembly_test.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test_scripts/2D_FEM_assembly_test.jl b/test_scripts/2D_FEM_assembly_test.jl index 4798cd647..a86eed2c3 100644 --- a/test_scripts/2D_FEM_assembly_test.jl +++ b/test_scripts/2D_FEM_assembly_test.jl @@ -581,5 +581,9 @@ if abspath(PROGRAM_FILE) == @__FILE__ using Pkg Pkg.activate(".") - run_assembly_test() + run_assembly_test() # to ensure routines are compiled before plots are made + run_assembly_test(ngrid=3,nelement_list=[8,16,32,64,128],plot_scan=true) + run_assembly_test(ngrid=5,nelement_list=[4,8,16,32,64],plot_scan=true) + run_assembly_test(ngrid=7,nelement_list=[2,4,8,16,32],plot_scan=true) + run_assembly_test(ngrid=9,nelement_list=[2,4,8,16],plot_scan=true) end From c24dbc6b1cc8828273da1c822913b7221eebdce8 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 29 Nov 2023 10:13:58 +0000 Subject: [PATCH 294/331] Add log scale to entropy_production plot and add a plot with n - n(0), upar - upar(0) and p - p(0) for compact presentation. --- src/post_processing.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/post_processing.jl b/src/post_processing.jl index 984f681ca..48c1b10ec 100644 --- a/src/post_processing.jl +++ b/src/post_processing.jl @@ -3710,6 +3710,9 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, @views plot(time[2:ntime], entropy_production[iz0,ir0,is,2:ntime], xlabel=L"t/(L_{ref}/c_{ref})", ylabel=L"\dot{S}(t)", label = "") outfile = string(run_name, "_entropy production"*description*"(iz0,ir0)_vs_t.pdf") trysavefig(outfile) + @views plot(time[2:ntime], entropy_production[iz0,ir0,is,2:ntime], xlabel=L"t/(L_{ref}/c_{ref})", ylabel=L"\dot{S}(t)", label = "", yscale=:log10) + outfile = string(run_name, "_entropy production_log_scale"*description*"(iz0,ir0)_vs_t.pdf") + trysavefig(outfile) end if pp.plot_chodura_integral # plot the Chodura condition integrals calculated at run time @@ -3728,6 +3731,17 @@ function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, outfile = string(run_name, "_chodura_integral_upper"*description*"_vs_r_t.pdf") trysavefig(outfile) end + if pp.plot_dens0_vs_t && pp.plot_ppar0_vs_t && pp.plot_pperp0_vs_t && pp.plot_upar0_vs_t + xlist = [time,time,time] + ylist = [ density[iz0,ir0,is,:] .- density[iz0,ir0,is,1], + parallel_flow[iz0,ir0,is,:] .- parallel_flow[iz0,ir0,is,1], + (2.0/3.0).*(perpendicular_pressure[iz0,ir0,is,:] .- perpendicular_pressure[iz0,ir0,is,1]).+ + (1.0/3.0).*(parallel_pressure[iz0,ir0,is,:] .- parallel_pressure[iz0,ir0,is,1])] + ylabels = [L"n_i(t) - n_i(0)" L"u_{i\|\|}(t) - u_{i\|\|}(0)" L"p_{i}(t) - p_{i}(0)"] + @views plot(xlist,ylist, xlabel=L"t/ (L_{ref}/c_{ref})", ylabel="", label = ylabels) + outfile = string(run_name, "_delta_moments"*description*"(iz0,ir0)_vs_t.pdf") + savefig(outfile) + end end println("done.") end From 87459f057bc3fe93360b2a5235efe32eab77790b Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Thu, 30 Nov 2023 09:30:31 +0000 Subject: [PATCH 295/331] Support ppari for manufactured solutions test with periodic boundary conditions in z. --- src/manufactured_solns.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/manufactured_solns.jl b/src/manufactured_solns.jl index a3050a4b3..3130bd674 100644 --- a/src/manufactured_solns.jl +++ b/src/manufactured_solns.jl @@ -205,7 +205,7 @@ using IfElse # ion mean parallel flow symbolic function function upari_sym(Lr,Lz,r_bc,z_bc,composition,geometry,nr,manufactured_solns_input,species) if z_bc == "periodic" - upari = 0.0 #not supported + upari = 0.0 elseif z_bc == "wall" densi = densi_sym(Lr,Lz,r_bc,z_bc,composition,manufactured_solns_input,species) Er, Ez, phi = electric_fields(Lr,Lz,r_bc,z_bc,composition,nr,manufactured_solns_input,species) @@ -222,10 +222,11 @@ using IfElse # ion parallel pressure symbolic function function ppari_sym(Lr,Lz,r_bc,z_bc,composition,manufactured_solns_input,species) - # normalisation factor due to strange pressure normalisation convention in master + # normalisation factor due to pressure normalisation convention in master pref = nref mref cref^2 norm_fac = 0.5 if z_bc == "periodic" - ppari = 0.0 # not supported + densi = densi_sym(Lr,Lz,r_bc,z_bc,composition,manufactured_solns_input,species) + ppari = densi elseif z_bc == "wall" densi = densi_sym(Lr,Lz,r_bc,z_bc,composition,manufactured_solns_input,species) epsilon = manufactured_solns_input.epsilon_offset From ad2fb1647a3d0fa385f787f465dd809029046f68 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 1 Dec 2023 15:34:03 +0000 Subject: [PATCH 296/331] Add API docs pages for Fokker-Planck collision operator modules --- docs/src/zz_fokker_planck.md | 6 ++++++ docs/src/zz_fokker_planck_calculus.md | 6 ++++++ docs/src/zz_fokker_planck_test.md | 6 ++++++ 3 files changed, 18 insertions(+) create mode 100644 docs/src/zz_fokker_planck.md create mode 100644 docs/src/zz_fokker_planck_calculus.md create mode 100644 docs/src/zz_fokker_planck_test.md diff --git a/docs/src/zz_fokker_planck.md b/docs/src/zz_fokker_planck.md new file mode 100644 index 000000000..e52ff4a07 --- /dev/null +++ b/docs/src/zz_fokker_planck.md @@ -0,0 +1,6 @@ +`fokker_planck` +=============== + +```@autodocs +Modules = [moment_kinetics.fokker_planck] +``` diff --git a/docs/src/zz_fokker_planck_calculus.md b/docs/src/zz_fokker_planck_calculus.md new file mode 100644 index 000000000..5b7f2121f --- /dev/null +++ b/docs/src/zz_fokker_planck_calculus.md @@ -0,0 +1,6 @@ +`fokker_planck_calculus` +======================== + +```@autodocs +Modules = [moment_kinetics.fokker_planck_calculus] +``` diff --git a/docs/src/zz_fokker_planck_test.md b/docs/src/zz_fokker_planck_test.md new file mode 100644 index 000000000..1c2308d76 --- /dev/null +++ b/docs/src/zz_fokker_planck_test.md @@ -0,0 +1,6 @@ +`fokker_planck_test` +==================== + +```@autodocs +Modules = [moment_kinetics.fokker_planck_test] +``` From ab4a096f24af38043f751563717fcd0d733e753e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 1 Dec 2023 15:57:47 +0000 Subject: [PATCH 297/331] Add API docs page for Gauss-Legendre discretization module --- docs/src/zz_gauss_legendre.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/src/zz_gauss_legendre.md diff --git a/docs/src/zz_gauss_legendre.md b/docs/src/zz_gauss_legendre.md new file mode 100644 index 000000000..0194f8183 --- /dev/null +++ b/docs/src/zz_gauss_legendre.md @@ -0,0 +1,6 @@ +`gauss_legendre` +================ + +```@autodocs +Modules = [moment_kinetics.gauss_legendre] +``` From 6147c23cae1154e34a43ea9dd93de2889fc69377 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Fri, 1 Dec 2023 17:09:10 +0000 Subject: [PATCH 298/331] Update debug_test/fokker_planck_collisions_tests.jl Co-authored-by: John Omotani --- debug_test/fokker_planck_collisions_tests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_test/fokker_planck_collisions_tests.jl b/debug_test/fokker_planck_collisions_tests.jl index 502e987f8..5fd838da8 100644 --- a/debug_test/fokker_planck_collisions_tests.jl +++ b/debug_test/fokker_planck_collisions_tests.jl @@ -1,6 +1,6 @@ module FokkerPlanckCollisionsDebug -# Debug test using wall boundary conditions. +# Debug test using Fokker-Planck collision operator include("setup.jl") From be9fbad8c8f9cd993fc9a802d0fdaab376f23c7b Mon Sep 17 00:00:00 2001 From: mrhardman Date: Fri, 1 Dec 2023 17:14:17 +0000 Subject: [PATCH 299/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index fccd5f493..95a72283c 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -73,7 +73,6 @@ using numerical integration to compute the Rosenbluth potentials only at the boundary and using an elliptic solve to obtain the potentials in the rest of the velocity space domain. """ - function init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=false, test_dense_matrix_construction=false, print_to_screen=true) bwgt = allocate_boundary_integration_weights(vpa,vperp) if vperp.n > 1 && precompute_weights From 2fde5b6df1f831b16e16defdd4131e27d641ebd6 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Fri, 1 Dec 2023 17:14:37 +0000 Subject: [PATCH 300/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 95a72283c..05f09c7c1 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -36,7 +36,6 @@ using SpecialFunctions: ellipk, ellipe, erf using FastGaussQuadrature using Dates using LinearAlgebra: lu -using ..initial_conditions: enforce_boundary_conditions! using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..communication: MPISharedArray, global_rank, _block_synchronize From 75090058eb62f383ad09e97bf66c9ba20dcf5645 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Fri, 1 Dec 2023 17:14:49 +0000 Subject: [PATCH 301/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 05f09c7c1..2df89d280 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -41,7 +41,6 @@ using ..array_allocation: allocate_float, allocate_shared_float using ..communication: MPISharedArray, global_rank, _block_synchronize using ..velocity_moments: integrate_over_vspace using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom -using ..calculus: derivative!, second_derivative! using ..looping using ..fokker_planck_calculus: init_Rosenbluth_potential_integration_weights! using ..fokker_planck_calculus: init_Rosenbluth_potential_boundary_integration_weights! From 3fdf636a1c108e5070a33c29e3ce6048964660f8 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Fri, 1 Dec 2023 17:15:43 +0000 Subject: [PATCH 302/331] Update src/file_io.jl Co-authored-by: John Omotani --- src/file_io.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/file_io.jl b/src/file_io.jl index 98b3d1c54..648790f06 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -547,7 +547,6 @@ function define_io_coordinate!(parent, coord, coord_name, description, parallel_ write_single_value!(group, "fd_option", coord.fd_option; parallel_io=parallel_io, description="type of finite difference for $coord_name, if used") - # write the finite-difference option for the coordinate write_single_value!(group, "cheb_option", coord.cheb_option; parallel_io=parallel_io, description="type of chebyshev differentiation used for $coord_name, if used") From fef6fc3a9352bd9343b3a47a6d98782330bb9469 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 4 Dec 2023 08:27:08 +0000 Subject: [PATCH 303/331] Update src/file_io.jl Co-authored-by: John Omotani --- src/file_io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file_io.jl b/src/file_io.jl index 648790f06..c6e40edca 100644 --- a/src/file_io.jl +++ b/src/file_io.jl @@ -648,7 +648,7 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, description="charged species thermal speed", units="c_ref") - # io_vth is the handle for the ion thermal speed + # 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, From e7407a63bfaaf47f7543c167606bac1662cb8a85 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 10 Dec 2023 12:13:10 +0000 Subject: [PATCH 304/331] Remove unused arguments from cheb_derivative_matrix_elementwise!. --- src/chebyshev.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index fbca571a7..fe2c1868d 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -71,7 +71,7 @@ function setup_chebyshev_pseudospectral_lobatto(coord) backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) # create array for differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) - cheb_derivative_matrix_elementwise!(Dmat,coord.ngrid,coord.L,coord.nelement_global) + cheb_derivative_matrix_elementwise!(Dmat,coord.ngrid) # return a structure containing the information needed to carry out # a 1D Chebyshev transform return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) @@ -789,7 +789,7 @@ https://people.maths.ox.ac.uk/trefethen/8all.pdf full list of Chapters may be obtained here https://people.maths.ox.ac.uk/trefethen/pdetext.html """ - function cheb_derivative_matrix_elementwise!(D::Array{Float64,2},n::Int64,L::Float64,nelement::Int64) + function cheb_derivative_matrix_elementwise!(D::Array{Float64,2},n::Int64) # define Gauss-Lobatto Chebyshev points in reversed order x_j = { -1, ... , 1} # consistent with use in elements of the grid From c45eedf3d09ce44d14ba8cb3c8f260b2d641ecc0 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 10 Dec 2023 12:19:39 +0000 Subject: [PATCH 305/331] Rename function used to compute Dmat for the Chebyshev-Radau grid, for better documentation. --- src/chebyshev.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/chebyshev.jl b/src/chebyshev.jl index fe2c1868d..bfde7c59a 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -92,7 +92,7 @@ function setup_chebyshev_pseudospectral_radau(coord) backward_transform = plan_ifft!(fext, flags=FFTW.MEASURE) # create array for differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) - cheb_derivative_matrix_elementwise_FFT!(Dmat, coord, fcheby, dcheby, fext, forward_transform) + cheb_derivative_matrix_elementwise_radau_by_FFT!(Dmat, coord, fcheby, dcheby, fext, forward_transform) # return a structure containing the information needed to carry out # a 1D Chebyshev transform return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) @@ -866,9 +866,11 @@ https://people.maths.ox.ac.uk/trefethen/pdetext.html return (c_j/c_k)*((-1)^(k+j))/(x[j] - x[k]) end """ - derivative matrix for Chebyshev grid using the FFT + Derivative matrix for Chebyshev-Radau grid using the FFT. + Note that a similar function could be constructed for the + Chebyshev-Lobatto grid, if desired. """ - function cheb_derivative_matrix_elementwise_FFT!(D::Array{Float64,2}, coord, f, df, fext, forward) + function cheb_derivative_matrix_elementwise_radau_by_FFT!(D::Array{Float64,2}, coord, f, df, fext, forward) ff_buffer = Array{Float64,1}(undef,coord.ngrid) df_buffer = Array{Float64,1}(undef,coord.ngrid) # use response matrix approach to calculate derivative matrix D From 4c3e1c56749056414277cb9121ea803b3475c182 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 10 Dec 2023 12:32:16 +0000 Subject: [PATCH 306/331] Update comments in second_derivative! and laplacian_derivative!. --- src/calculus.jl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/calculus.jl b/src/calculus.jl index 05536fd67..6c6305a7a 100644 --- a/src/calculus.jl +++ b/src/calculus.jl @@ -158,12 +158,15 @@ Apply 'K-matrix' as part of a weak-form second derivative function elementwise_apply_Kmat! end function second_derivative!(d2f, f, coord, spectral::weak_discretization_info) - # get the derivative at each grid point within each element and store in df + # obtain the RHS of numerical weak-form of the equation + # g = d^2 f / d coord^2, which is + # M * g = K * f, with M the mass matrix and K an appropriate stiffness matrix + # by multiplying by basis functions and integrating by parts elementwise_apply_Kmat!(coord, f, spectral) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the average of the derivatives from neighboring elements. + # map the RHS vector K * f from the elemental grid to the full grid; + # at element boundaries, use the average of K * f from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) - # solve weak form problem M * d2f = K * f + # solve weak form matrix problem M * g = K * f to obtain g = d^2 f / d coord^2 mass_matrix_solve!(d2f, coord.scratch, spectral) end @@ -173,12 +176,16 @@ Apply 'L-matrix' as part of a weak-form Laplacian derivative function elementwise_apply_Lmat! end function laplacian_derivative!(d2f, f, coord, spectral::weak_discretization_info) - # get the derivative at each grid point within each element and store in df + # for coord.name 'vperp' obtain the RHS of numerical weak-form of the equation + # g = (1/coord) d/d coord ( coord d f / d coord ), which is + # M * g = K * f, with M the mass matrix, and K an appropriate stiffness matrix, + # by multiplying by basis functions and integrating by parts. + # for all other coord.name, do exactly the same as second_derivative! above. elementwise_apply_Lmat!(coord, f, spectral) - # map the derivative from the elemental grid to the full grid; - # at element boundaries, use the average of the derivatives from neighboring elements. + # map the RHS vector K * f from the elemental grid to the full grid; + # at element boundaries, use the average of K * f from neighboring elements. derivative_elements_to_full_grid!(coord.scratch, coord.scratch_2d, coord) - # solve weak form problem M * d2f = K * f + # solve weak form matrix problem M * g = K * f to obtain g = d^2 f / d coord^2 mass_matrix_solve!(d2f, coord.scratch, spectral) end From 0fda1f6902726a09578ac75c4e3fffd9f761cce1 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 10 Dec 2023 13:52:37 +0000 Subject: [PATCH 307/331] Added derivative vector D0 for computing the derivative at vperp = 0 (eventually) for imposing the regularity condition d F / d vperp. In normalised coordinates, D0 computes the derivative at x = -1, which is useful on the Radau grid because the grid runs in normalised units from (-1,1]. Note that like the other derivative matrices and vectors, an explicit scale length must be included. A test script is provided to check the functionality. --- src/chebyshev.jl | 43 ++++++++++- test_scripts/chebyshev_radau_test.jl | 107 +++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 test_scripts/chebyshev_radau_test.jl diff --git a/src/chebyshev.jl b/src/chebyshev.jl index bfde7c59a..9bac36633 100644 --- a/src/chebyshev.jl +++ b/src/chebyshev.jl @@ -39,6 +39,8 @@ struct chebyshev_base_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs backward::TBackward # elementwise differentiation matrix (ngrid*ngrid) Dmat::Array{mk_float,2} + # elementwise differentiation vector (ngrid) for the point x = -1 + D0::Array{mk_float,1} end struct chebyshev_info{TForward <: FFTW.cFFTWPlan, TBackward <: AbstractFFTs.ScaledPlan} <: discretization_info @@ -72,9 +74,11 @@ function setup_chebyshev_pseudospectral_lobatto(coord) # create array for differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) cheb_derivative_matrix_elementwise!(Dmat,coord.ngrid) + D0 = allocate_float(coord.ngrid) + D0 .= Dmat[1,:] # return a structure containing the information needed to carry out # a 1D Chebyshev transform - return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat, D0) end function setup_chebyshev_pseudospectral_radau(coord) @@ -93,9 +97,11 @@ function setup_chebyshev_pseudospectral_radau(coord) # create array for differentiation matrix Dmat = allocate_float(coord.ngrid, coord.ngrid) cheb_derivative_matrix_elementwise_radau_by_FFT!(Dmat, coord, fcheby, dcheby, fext, forward_transform) + D0 = allocate_float(coord.ngrid) + cheb_lower_endpoint_derivative_vector_elementwise_radau_by_FFT!(D0, coord, fcheby, dcheby, fext, forward_transform) # return a structure containing the information needed to carry out # a 1D Chebyshev transform - return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat) + return chebyshev_base_info(fext, fcheby, dcheby, forward_transform, backward_transform, Dmat, D0) end """ @@ -780,6 +786,22 @@ function chebyshev_radau_forward_transform!(chebyf, fext, ff, transform, n) # inverse Chebyshev transform to get df/dcoord chebyshev_radau_backward_transform!(df, cheby_fext, cheby_df, forward, coord.ngrid) end + function chebyshev_radau_derivative_lower_endpoint(ff, cheby_f, cheby_df, cheby_fext, forward, coord) + # calculate the Chebyshev coefficients of the real-space function ff and return + # as cheby_f + chebyshev_radau_forward_transform!(cheby_f, cheby_fext, ff, forward, coord.ngrid) + # calculate the Chebyshev coefficients of the derivative of ff with respect to coord.grid + chebyshev_spectral_derivative!(cheby_df, cheby_f) + # form the derivative at x = - 1 using that T_n(-1) = (-1)^n + # and converting the normalisation factors to undo the normalisation in the FFT + # df = d0 + sum_n=1 (-1)^n d_n/2 with d_n the coeffs + # of the Cheb derivative in the Fourier representation + df = cheby_df[1] + for i in 2:coord.ngrid + df += ((-1)^(i-1))*0.5*cheby_df[i] + end + return df + end """ @@ -888,5 +910,22 @@ https://people.maths.ox.ac.uk/trefethen/pdetext.html D[j,j] = -sum(D[j,:]) end end + + function cheb_lower_endpoint_derivative_vector_elementwise_radau_by_FFT!(D::Array{Float64,1}, coord, f, df, fext, forward) + ff_buffer = Array{Float64,1}(undef,coord.ngrid) + df_buffer = Array{Float64,1}(undef,coord.ngrid) + # use response matrix approach to calculate derivative vector D + for j in 1:coord.ngrid + ff_buffer .= 0.0 + ff_buffer[j] = 1.0 + @views df_buffer = chebyshev_radau_derivative_lower_endpoint(ff_buffer[:], + f[:,1], df, fext, forward, coord) + D[j] = df_buffer # assign appropriate value of derivative vector + end + # correct diagonal elements to gurantee numerical stability + # gives D*[1.0, 1.0, ... 1.0] = [0.0, 0.0, ... 0.0] + D[1] = 0.0 + D[1] = -sum(D[:]) + end end diff --git a/test_scripts/chebyshev_radau_test.jl b/test_scripts/chebyshev_radau_test.jl new file mode 100644 index 000000000..57f2a0782 --- /dev/null +++ b/test_scripts/chebyshev_radau_test.jl @@ -0,0 +1,107 @@ +export chebyshevradau_test + +using Printf +using Plots +using LaTeXStrings +using MPI +using Measures + +import moment_kinetics +using moment_kinetics.chebyshev +using moment_kinetics.input_structs: grid_input, advection_input +using moment_kinetics.coordinates: define_coordinate +using moment_kinetics.calculus: derivative! + + +function print_matrix(matrix,name,n,m) + println("\n ",name," \n") + for i in 1:n + for j in 1:m + @printf("%.3f ", matrix[i,j]) + end + println("") + end + println("\n") +end + +function print_vector(vector,name,m) + println("\n ",name," \n") + for j in 1:m + @printf("%.3f ", vector[j]) + end + println("") + println("\n") +end +""" +Test for derivative vector D0 that is used to compute +the numerical derivative on the Chebyshev-Radau elements +at the lower endpoint of the domain (-1,1] in the normalised +coordinate x. Here in the tests the shifted coordinate y +is used with the vperp label so that the grid runs from (0,L]. +""" +function chebyshevradau_test(; ngrid=5, L_in=3.0) + + # elemental grid tests + #ngrid = 17 + #nelement = 4 + y_ngrid = ngrid #number of points per element + y_nelement_local = 1 # number of elements per rank + y_nelement_global = y_nelement_local # total number of elements + y_L = L_in + bc = "zero" + discretization = "gausslegendre_pseudospectral" + # fd_option and adv_input not actually used so given values unimportant + fd_option = "fourth_order_centered" + cheb_option = "matrix" + adv_input = advection_input("default", 1.0, 0.0, 0.0) + nrank = 1 + irank = 0#1 + comm = MPI.COMM_NULL + element_spacing_option = "uniform" + # create the 'input' struct containing input info needed to create a + # coordinate + y_name = "vperp" # to use radau grid + y_input = grid_input(y_name, y_ngrid, y_nelement_global, y_nelement_local, + nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) + y, y_spectral = define_coordinate(y_input) + + Dmat = y_spectral.radau.Dmat + print_matrix(Dmat,"Radau Dmat",y.ngrid,y.ngrid) + D0 = y_spectral.radau.D0 + print_vector(D0,"Radau D0",y.ngrid) + + ff_err = Array{Float64,1}(undef,y.n) + ff = Array{Float64,1}(undef,y.n) + for iy in 1:y.n + ff[iy] = exp(-4.0*y.grid[iy]) + end + df_exact = -4.0 + df_num = sum(D0.*ff)/y.element_scale[1] + df_err = abs(df_num - df_exact) + println("f(y) = exp(-4 y) test") + println("exact df: ",df_exact," num df: ",df_num," abs(err): ",df_err) + + for iy in 1:y.n + ff[iy] = sin(y.grid[iy]) + end + df_exact = 1.0 + df_num = sum(D0.*ff)/y.element_scale[1] + df_err = abs(df_num - df_exact) + println("f(y) = sin(y) test") + println("exact df: ",df_exact," num df: ",df_num," abs(err): ",df_err) + for iy in 1:y.n + ff[iy] = y.grid[iy] + (y.grid[iy])^2 + (y.grid[iy])^3 + end + df_exact = 1.0 + df_num = sum(D0.*ff)/y.element_scale[1] + df_err = abs(df_num - df_exact) + println("f(y) = y + y^2 + y^3 test") + println("exact df: ",df_exact," num df: ",df_num," abs(err): ",df_err) +end + +if abspath(PROGRAM_FILE) == @__FILE__ + using Pkg + Pkg.activate(".") + + chebyshevradau_test() +end \ No newline at end of file From 147ec48c07f82671a3efc00822394f79a0804ecd Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Sun, 10 Dec 2023 18:36:15 +0000 Subject: [PATCH 308/331] Permit d F / d vperp = 0 to be imposed using the D0 vector for chebyshev radau grids (from the previous commit). This addresses issue https://github.com/mabarnes/moment_kinetics/issues/157. --- src/initial_conditions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 88dbc1d70..a420cf5ac 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -2185,7 +2185,7 @@ function enforce_vperp_boundary_condition!(f, bc, vperp, vperp_spectral) f[ivpa,nvperp,iz,ir,is] = 0.0 end # set regularity condition d F / d vperp = 0 at vperp = 0 - if vperp.discretization == "gausslegendre_pseudospectral" + if vperp.discretization == "gausslegendre_pseudospectral" || vperp.discretization == "chebyshev_pseudospectral" D0 = vperp_spectral.radau.D0 @loop_s_r_z_vpa is ir iz ivpa begin # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 From 5952f614807b8397bda747662715cff6bc19144a Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:06:23 +0000 Subject: [PATCH 309/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 2df89d280..155a8b571 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -145,7 +145,6 @@ end """ Function for advancing with the explicit, weak-form, self-collision operator """ - function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,composition,collisions,dt, fkpl_arrays::fokkerplanck_weakform_arrays_struct, r, z, vperp, vpa, vperp_spectral, vpa_spectral, scratch_dummy; From 51d77a9cd9a42db03ec3494541402868ffd62d13 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:08:19 +0000 Subject: [PATCH 310/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 155a8b571..330341880 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -423,7 +423,6 @@ end """ allocate the required ancilliary arrays """ - function allocate_fokkerplanck_arrays_direct_integration(vperp,vpa) nvpa = vpa.n nvperp = vperp.n From 59d76a26ff3cd2cc5be0d6cbe5095e9a192b155c Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:08:39 +0000 Subject: [PATCH 311/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 330341880..5a6eb0351 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -466,7 +466,6 @@ of the direct integration method, the struct 'fka' created here does not contain all of the arrays necessary to compute the weak-form operator. This functionality could be ported if necessary. """ - function init_fokker_planck_collisions_direct_integration(vperp,vpa; precompute_weights=false, print_to_screen=false) fka = allocate_fokkerplanck_arrays_direct_integration(vperp,vpa) if vperp.n > 1 && precompute_weights From 304db7897c1b413a95d0d05e3551e66dedcf6864 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:12:33 +0000 Subject: [PATCH 312/331] Update src/fokker_planck_calculus.jl Co-authored-by: John Omotani --- src/fokker_planck_calculus.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index c767b186c..3124c2bce 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -644,7 +644,7 @@ end # `ellipe(k) = \int^{\pi/2}\_0 \frac{1}{\sqrt{ 1 - m \sin^2(\theta)}} d \theta` function local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, - nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vperp grids + nquad_vpa,ielement_vpa,vpa_nodes,vpa, # info about primed vpa grids nquad_vperp,ielement_vperp,vperp_nodes,vperp, # info about primed vperp grids x_vpa, w_vpa, x_vperp, w_vperp, # points and weights for primed (source) grids vpa_val, vperp_val) # values and indices for unprimed (field) grids From 5c8b3eea99d2efc49b3e48cb209ace738a5b97a3 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:19:10 +0000 Subject: [PATCH 313/331] Update src/fokker_planck_calculus.jl Co-authored-by: John Omotani --- src/fokker_planck_calculus.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 3124c2bce..0946a9244 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -930,7 +930,7 @@ function icsc_func(ivpa_local::mk_int,ivpap_local::mk_int, ielement_vperp::mk_int, ngrid_vperp::mk_int,nelement_vperp::mk_int) ntot_vpa = (nelement_vpa - 1)*(ngrid_vpa^2 - 1) + ngrid_vpa^2 - ntot_vperp = (nelement_vperp - 1)*(ngrid_vperp^2 - 1) + ngrid_vperp^2 + #ntot_vperp = (nelement_vperp - 1)*(ngrid_vperp^2 - 1) + ngrid_vperp^2 icsc_vpa = ((ivpap_local - 1) + (ivpa_local - 1)*ngrid_vpa + (ielement_vpa - 1)*(ngrid_vpa^2 - 1)) From 4ffe729649e459a91d79f11534e1f389d2e429fd Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:20:52 +0000 Subject: [PATCH 314/331] Update src/fokker_planck_calculus.jl Co-authored-by: John Omotani --- src/fokker_planck_calculus.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 0946a9244..e4e4f1c65 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -605,7 +605,6 @@ function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, end function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) - zero = 1.0e-6 @. x_scaled = 0.0 @. w_scaled = 0.0 #println("coord: ",coord_val," node_max: ",node_max," node_min: ",node_min) From 3103c413a10e66be960a73c477808ff058672250 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:36:02 +0000 Subject: [PATCH 315/331] Update src/fokker_planck_calculus.jl Co-authored-by: John Omotani --- src/fokker_planck_calculus.jl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index e4e4f1c65..da1d57c13 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -1502,13 +1502,6 @@ function assemble_matrix_operators_dirichlet_bc(vpa,vperp,vpa_spectral,vperp_spe if global_rank[] == 0 && print_to_screen println("finished elliptic operator assignment ", Dates.format(now(), dateformat"H:MM:SS")) end - if nc_global < 60 - print_matrix(MM2D,"MM2D",nc_global,nc_global) - #print_matrix(KKpar2D,"KKpar2D",nc_global,nc_global) - #print_matrix(KKperp2D,"KKperp2D",nc_global,nc_global) - #print_matrix(LP2D,"LP",nc_global,nc_global) - #print_matrix(LV2D,"LV",nc_global,nc_global) - end # convert these matrices to sparse matrices if global_rank[] == 0 && print_to_screen println("begin conversion to sparse matrices ", Dates.format(now(), dateformat"H:MM:SS")) From 727fd6fa40d77e700a4a8d430394b4d24253df9c Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 13:37:40 +0000 Subject: [PATCH 316/331] Update src/fokker_planck.jl Co-authored-by: John Omotani --- src/fokker_planck.jl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/fokker_planck.jl b/src/fokker_planck.jl index 5a6eb0351..82d2b3a9c 100644 --- a/src/fokker_planck.jl +++ b/src/fokker_planck.jl @@ -207,13 +207,19 @@ function explicit_fokker_planck_collisions_weak_form!(pdf_out,pdf_in,dSdt,compos return nothing end + """ -Function for evaluating Css' = Css'[Fs,Fs'] -The result is stored in the array fkpl_arrays.CC +Function for evaluating \$C_{ss'} = C_{ss'}[F_s,F_{s'}]\$ + +The result is stored in the array `fkpl_arrays.CC`. + +The normalised collision frequency is defined by +```math +\\nu_{ss'} = \\frac{\\gamma_{ss'} n_\\mathrm{ref}}{2 m_s^2 c_\\mathrm{ref}^3} +``` +with \$\\gamma_{ss'} = 2 \\pi (Z_s Z_{s'})^2 e^4 \\ln \\Lambda_{ss'} / (4 \\pi +\\epsilon_0)^2\$. """ -# the normalised collision frequency is defined by -# nu_{ss'} = gamma_{ss'} n_{ref} / 2 (m_s)^2 (c_{ref})^3 -# with gamma_ss' = 2 pi (Z_s Z_s')^2 e^4 ln \Lambda_{ss'} / (4 pi \epsilon_0)^2 function fokker_planck_collision_operator_weak_form!(ffs_in,ffsp_in,ms,msp,nussp, fkpl_arrays::fokkerplanck_weakform_arrays_struct, vperp, vpa, vperp_spectral, vpa_spectral; From 209f3a5b6f25ed53aa1bcafee2765985c447de58 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 11 Dec 2023 14:30:09 +0000 Subject: [PATCH 317/331] Comment for algebraic_solve!(). --- src/fokker_planck_calculus.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index da1d57c13..0ba31c89e 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -2038,8 +2038,10 @@ function elliptic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_bounda return nothing end -# same as above but source is made of two different terms -# with different weak matrices +# Same as elliptic_solve!() above but no Dirichlet boundary conditions are imposed, +# because the function is only used where the lu_object_lhs is derived from a mass matrix. +# The source is made of two different terms with different weak matrices +# because of the form of the only algebraic equation that we consider. function algebraic_solve!(field,source_1,source_2,boundary_data::vpa_vperp_boundary_data, lu_object_lhs,matrix_rhs_1,matrix_rhs_2,rhsc_1,rhsc_2,sc_1,sc_2,vpa,vperp) # get data into the compound index format From 43ad983a066e9cfc34828ba5210a4337982dda36 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 11 Dec 2023 14:42:36 +0000 Subject: [PATCH 318/331] Comment for loop_over_vperp_vpa_elements_no_divergences!(). --- src/fokker_planck_calculus.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 0ba31c89e..0179d4915 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -843,6 +843,12 @@ function loop_over_vperp_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weigh return nothing end +# The function loop_over_vperp_vpa_elements_no_divergences!() was for debugging. +# By changing the source where loop_over_vperp_vpa_elements!() is called to +# instead call this function we can verify that the Gauss-Legendre quadrature +# is adequate for integrating a divergence-free integrand. This function should be +# kept until the problems with the pure integration method of computing the +# Rosenbluth potentials are understood. function loop_over_vperp_vpa_elements_no_divergences!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperp_low,ielement_vperp_hi, # info about primed vperp grids From bc8e78d7e81e0dbe736e3e3bc2606872ab30bb5e Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Mon, 11 Dec 2023 15:00:44 +0000 Subject: [PATCH 319/331] Rename get_scaled_x_w!(). Comment for get_scaled_x_w_with_divergences!() and get_scaled_x_w_no_divergences!(). --- src/fokker_planck_calculus.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/fokker_planck_calculus.jl b/src/fokker_planck_calculus.jl index 0179d4915..46d4d7111 100644 --- a/src/fokker_planck_calculus.jl +++ b/src/fokker_planck_calculus.jl @@ -513,7 +513,13 @@ function lagrange_poly(j,x_nodes,x) return poly end -function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) +# Function to get the local integration grid and quadrature weights +# to integrate a 1D element in the 2D representation of the +# velocity space distribution functions. This function assumes that +# there is a divergence at the point coord_val, and splits the grid +# and integration weights appropriately, using Gauss-Laguerre points +# near the divergence and Gauss-Legendre points away from the divergence. +function get_scaled_x_w_with_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, w_laguerre, node_min, node_max, nodes, igrid_coord, coord_val) #println("nodes ",nodes) zero = 1.0e-10 @. x_scaled = 0.0 @@ -603,7 +609,9 @@ function get_scaled_x_w!(x_scaled, w_scaled, x_legendre, w_legendre, x_laguerre, #println("w_scaled",w_scaled) return nquad_coord end - +# Function to get the local grid and integration weights assuming +# no divergences of the function on the 1D element. Gauss-Legendre +# quadrature is used for the entire element. function get_scaled_x_w_no_divergences!(x_scaled, w_scaled, x_legendre, w_legendre, node_min, node_max) @. x_scaled = 0.0 @. w_scaled = 0.0 @@ -743,14 +751,14 @@ function loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_ x_vpa, w_vpa, x_vperp, w_vperp, vpa_val, vperp_val) end - nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + nquad_vperp = get_scaled_x_w_with_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) for ielement_vpap in ielement_vpa_low:ielement_vpa_hi #for ielement_vpap in 1:vpa.nelement_local # use general grid function that checks divergences vpa_nodes = get_nodes(vpa,ielement_vpap) vpa_min, vpa_max = vpa_nodes[1], vpa_nodes[end] #nquad_vpa = get_scaled_x_w_no_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, vpa_min, vpa_max) - nquad_vpa = get_scaled_x_w!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) + nquad_vpa = get_scaled_x_w_with_divergences!(x_vpa, w_vpa, x_legendre, w_legendre, x_laguerre, w_laguerre, vpa_min, vpa_max, vpa_nodes, igrid_vpa, vpa_val) @views local_element_integration!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, nquad_vpa,ielement_vpap,vpa_nodes,vpa, nquad_vperp,ielement_vperpp,vperp_nodes,vperp, @@ -819,7 +827,7 @@ function loop_over_vperp_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weigh #vperp_max = vperp_nodes[end] #vperp_min = vperp_nodes[1]*nel_low(ielement_vperpp,vperp.nelement_local) #nquad_vperp = get_scaled_x_w_no_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, vperp_min, vperp_max) - #nquad_vperp = get_scaled_x_w!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) + #nquad_vperp = get_scaled_x_w_with_divergences!(x_vperp, w_vperp, x_legendre, w_legendre, x_laguerre, w_laguerre, vperp_min, vperp_max, vperp_nodes, igrid_vperp, vperp_val) @views loop_over_vpa_elements!(G0_weights,G1_weights,H0_weights,H1_weights,H2_weights,H3_weights, vpa,ielement_vpa_low,ielement_vpa_hi, # info about primed vpa grids vperp,ielement_vperpp, # info about primed vperp grids From c15fc27da0e66eaef0431be15e19a11442877496 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Dec 2023 15:47:52 +0000 Subject: [PATCH 320/331] Remove redundant array copies in numerical diffusion functions --- src/numerical_dissipation.jl | 37 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/numerical_dissipation.jl b/src/numerical_dissipation.jl index 136fb701f..64eb28138 100644 --- a/src/numerical_dissipation.jl +++ b/src/numerical_dissipation.jl @@ -354,17 +354,14 @@ function z_dissipation!(f_out, f_in, z, z_spectral::T_spectral, dt, scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, scratch_dummy.buffer_vpavperprs_3,scratch_dummy.buffer_vpavperprs_4, z_spectral,z) - @loop_s_r_vperp_vpa is ir ivperp ivpa begin - @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,:,ir,is] = scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,:,ir,is] - end - # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #1 - derivative_z!(scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperpzrs_2[:,:,:,:,:], + # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #2 + derivative_z!(scratch_dummy.buffer_vpavperpzrs_2, scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, scratch_dummy.buffer_vpavperprs_3,scratch_dummy.buffer_vpavperprs_4, z_spectral,z) # advance f due to diffusion_coefficient * d^2 f / d z^2 @loop_s_r_vperp_vpa is ir ivperp ivpa begin - @views @. f_out[ivpa,ivperp,:,ir,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,:,ir,is] + @views @. f_out[ivpa,ivperp,:,ir,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,:,ir,is] end return nothing @@ -403,17 +400,14 @@ function r_dissipation!(f_out, f_in, r, r_spectral::T_spectral, dt, scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2, scratch_dummy.buffer_vpavperpzs_3,scratch_dummy.buffer_vpavperpzs_4, r_spectral,r) - @loop_s_z_vperp_vpa is iz ivperp ivpa begin - @. scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,iz,:,is] = scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,iz,:,is] - end - # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #1 - derivative_r!(scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperpzrs_2[:,:,:,:,:], + # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #2 + derivative_r!(scratch_dummy.buffer_vpavperpzrs_2, scratch_dummy.buffer_vpavperpzrs_1, scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2, scratch_dummy.buffer_vpavperpzs_3,scratch_dummy.buffer_vpavperpzs_4, r_spectral,r) # advance f due to diffusion_coefficient * d^2 f / d r^2 @loop_s_z_vperp_vpa is iz ivperp ivpa begin - @views @. f_out[ivpa,ivperp,iz,:,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_1[ivpa,ivperp,iz,:,is] + @views @. f_out[ivpa,ivperp,iz,:,is] += dt * diffusion_coefficient * scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,iz,:,is] end return nothing @@ -480,17 +474,14 @@ function z_dissipation_neutral!(f_out, f_in, z, z_spectral::T_spectral, dt, scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, scratch_dummy.buffer_vzvrvzetarsn_3,scratch_dummy.buffer_vzvrvzetarsn_4, z_spectral,z) - @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,:,ir,isn] = scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,:,ir,isn] - end - # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #1 - derivative_z!(scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetazrsn_2, + # compute d^2 f / d z^2 using centred reconciliation and place in dummy array #2 + derivative_z!(scratch_dummy.buffer_vzvrvzetazrsn_2, scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, scratch_dummy.buffer_vzvrvzetarsn_3,scratch_dummy.buffer_vzvrvzetarsn_4, z_spectral,z) # advance f due to diffusion_coefficient * d^2 f/ d z^2 @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - @views @. f_out[ivz,ivr,ivzeta,:,ir,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,:,ir,isn] + @views @. f_out[ivz,ivr,ivzeta,:,ir,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,:,ir,isn] end return nothing @@ -529,18 +520,14 @@ function r_dissipation_neutral!(f_out, f_in, r, r_spectral::T_spectral, dt, scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, scratch_dummy.buffer_vzvrvzetazsn_3,scratch_dummy.buffer_vzvrvzetazsn_4, r_spectral,r) - # form Q d f / d r and place in dummy array #2 - @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - @. scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,iz,:,isn] = scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,iz,:,isn] - end - # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #1 - derivative_r!(scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetazrsn_2, + # compute d^2 f / d r^2 using centred reconciliation and place in dummy array #2 + derivative_r!(scratch_dummy.buffer_vzvrvzetazrsn_2, scratch_dummy.buffer_vzvrvzetazrsn_1, scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, scratch_dummy.buffer_vzvrvzetazsn_3,scratch_dummy.buffer_vzvrvzetazsn_4, r_spectral,r) # advance f due to diffusion_coefficient * d / d r ( Q d f / d r ) @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - @views @. f_out[ivz,ivr,ivzeta,iz,:,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_1[ivz,ivr,ivzeta,iz,:,isn] + @views @. f_out[ivz,ivr,ivzeta,iz,:,isn] += dt * diffusion_coefficient * scratch_dummy.buffer_vzvrvzetazrsn_2[ivz,ivr,ivzeta,iz,:,isn] end return nothing From 0982a3d547a94e5a474b5a3cc3c6df17d81d00b3 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 16:45:16 +0000 Subject: [PATCH 321/331] Update src/plot_MMS_sequence.jl Co-authored-by: John Omotani --- src/plot_MMS_sequence.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plot_MMS_sequence.jl b/src/plot_MMS_sequence.jl index 5c3cba55a..d11a53c10 100644 --- a/src/plot_MMS_sequence.jl +++ b/src/plot_MMS_sequence.jl @@ -305,7 +305,6 @@ function get_MMS_error_data(path_list,scan_type,scan_name) ylabel_phi = L"\varepsilon(\widetilde{\phi})" ylabel_Er = L"\varepsilon(\widetilde{E}_r)" ylabel_Ez = L"\varepsilon(\widetilde{E}_z)" - ylabel_Ez = L"\varepsilon(\widetilde{E}_z)" expected_label = L"(1/N_{el})^{n_g - 1}" if scan_type == "vpa_nelement" xlabel = L"v_{||}"*" "*L"N_{element}" From d4626673eb3e7218c1fa88d2103dee2d7c6d1eca Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 16:47:59 +0000 Subject: [PATCH 322/331] Update src/fokker_planck_test.jl Co-authored-by: John Omotani --- src/fokker_planck_test.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fokker_planck_test.jl b/src/fokker_planck_test.jl index 244045ca7..b0391a087 100644 --- a/src/fokker_planck_test.jl +++ b/src/fokker_planck_test.jl @@ -243,7 +243,6 @@ taking floats as arguments. This function is designed to be used at the lowest level of a coordinate loop, with derivatives and integrals all previously calculated. """ - function Cssp_fully_expanded_form(nussp,ms,msp, d2fsdvpa2,d2fsdvperp2,d2fsdvperpdvpa,dfsdvpa,dfsdvperp,fs, d2Gspdvpa2,d2Gspdvperp2,d2Gspdvperpdvpa,dGspdvperp, From 425d9c14b3a6cc728ce79d61db0b56cfe661c6f3 Mon Sep 17 00:00:00 2001 From: mrhardman Date: Mon, 11 Dec 2023 16:48:57 +0000 Subject: [PATCH 323/331] Update src/initial_conditions.jl Co-authored-by: John Omotani --- src/initial_conditions.jl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index a420cf5ac..29b57d62a 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -2156,15 +2156,12 @@ function enforce_v_boundary_condition_local!(f, bc, speed, v_diffusion, v, v_spe f[end] = 0.0 elseif bc == "zero_gradient" D0 = v_spectral.lobatto.Dmat[1,:] - @loop_s_r_z_vperp is ir iz ivperp begin - # adjust F(vpa = -L/2) so that d F / d vpa = 0 at vpa = -L/2 - f[1,ivperp,iz,ir,is] = -sum(D0[2:ngrid].*f[2:ngrid,ivperp,iz,ir,is])/D0[1] - end + # 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,:] - @loop_s_r_z_vperp is ir iz ivperp begin - # adjust F(vpa = L/2) so that d F / d vpa = 0 at vpa = L/2 - f[nvpa,ivperp,iz,ir,is] = -sum(D0[1:ngrid-1].*f[nvpa-ngrid+1:nvpa-1,ivperp,iz,ir,is])/D0[ngrid] - 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] From ee12e1a669375adf512f3e591697bd2566aeb606 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Dec 2023 20:11:57 +0000 Subject: [PATCH 324/331] Simplify type annotation in `get_QQ_local!` by using `AbstractArray` --- src/gauss_legendre.jl | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index c7ec4872b..11bd5f501 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -223,9 +223,9 @@ function setup_gausslegendre_pseudospectral_radau(coord;init_YY=true) return gausslegendre_base_info(Dmat,M0,M1,M2,S0,S1, K0,K1,K2,P0,P1,P2,D0,Y00,Y01,Y10,Y11,Y20,Y21,Y30,Y31) end - + function elementwise_derivative!(coord, ff, gausslegendre::gausslegendre_info) - df = coord.scratch_2d + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -261,14 +261,14 @@ function elementwise_derivative!(coord, ff, gausslegendre::gausslegendre_info) return nothing end - -# Spectral element method does not use upwinding within an element -function elementwise_derivative!(coord, ff, adv_fac, spectral::gausslegendre_info) - return elementwise_derivative!(coord, ff, spectral) -end + +# Spectral element method does not use upwinding within an element +function elementwise_derivative!(coord, ff, adv_fac, spectral::gausslegendre_info) + return elementwise_derivative!(coord, ff, spectral) +end function elementwise_apply_Kmat!(coord, ff, gausslegendre::gausslegendre_info) - df = coord.scratch_2d + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -307,7 +307,7 @@ function elementwise_apply_Kmat!(coord, ff, gausslegendre::gausslegendre_info) end function elementwise_apply_Lmat!(coord, ff, gausslegendre::gausslegendre_info) - df = coord.scratch_2d + df = coord.scratch_2d # define local variable nelement for convenience nelement = coord.nelement_local # check array bounds @@ -485,15 +485,15 @@ function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,2},ngrid,x,wgts,o # appropriate inner product of Legendre polys # definition depends on required matrix # for M0: AA = < P_i P_j > - # for M1: AA = < P_i P_j x > + # for M1: AA = < P_i P_j x > # for M2: AA = < P_i P_j x^2 > # for S0: AA = -< P'_i P_j > # for S1: AA = -< P'_i P_j x > # for K0: AA = -< P'_i P'_j > - # for K1: AA = -< P'_i P'_j x > + # for K1: AA = -< P'_i P'_j x > # for K2: AA = -< P'_i P'_j x^2 > - # for P0: AA = < P_i P'_j > - # for P1: AA = < P_i P'_j x > + # for P0: AA = < P_i P'_j > + # for P1: AA = < P_i P'_j x > # for P2: AA = < P_i P'_j x^2 > AA = allocate_float(ngrid,ngrid) nquad = 2*ngrid @@ -622,13 +622,13 @@ function GaussLegendre_weak_product_matrix!(QQ::Array{mk_float,3},ngrid,x,wgts,o # appropriate inner product of Legendre polys # definition depends on required matrix # for Y00: AA = < P_i P_j P_k > - # for Y01: AA = < P_i P_j P_k x > + # for Y01: AA = < P_i P_j P_k x > # for Y10: AA = < P_i P_j P'_k > - # for Y11: AA = < P_i P_j P'_k x > + # for Y11: AA = < P_i P_j P'_k x > # for Y20: AA = < P_i P'_j P'_k > # for Y21: AA = < P_i P'_j P'_k x > # for Y31: AA = < P_i P'_j P_k x > - # for Y30: AA = < P_i P'_j P_k > + # for Y30: AA = < P_i P'_j P_k > AA = allocate_float(ngrid,ngrid,ngrid) nquad = 2*ngrid zz, wz = gausslegendre(nquad) @@ -1185,11 +1185,7 @@ construction function for nonlinear diffusion matrices, only used in the assembly of the collision operator """ -function get_QQ_local!(QQ::Union{Array{mk_float,3}, - SubArray{Float64, 3, Array{Float64, 4}, - Tuple{Base.Slice{Base.OneTo{Int64}}, - Base.Slice{Base.OneTo{Int64}}, - Base.Slice{Base.OneTo{Int64}}, Int64}, true}}, +function get_QQ_local!(QQ::AbstractArray{mk_float,3}, ielement,lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, coord,option) From 816e058a125b773df1af649a4a7145570d737998 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 12 Dec 2023 11:34:35 +0000 Subject: [PATCH 325/331] Switch from reconcile_element_boundaries_MPI!() using centred averaging of element boundaries to upwind choice for element boundaries. This requires extra dummy arrays to be passed to the boundary condition functions. --- src/initial_conditions.jl | 57 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index 29b57d62a..b52950aec 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1157,14 +1157,15 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v @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) + scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4, + scratch_dummy.buffer_vpavperprs_5, scratch_dummy.buffer_vpavperprs_6) 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, + scratch_dummy.buffer_vpavperpzs_3, scratch_dummy.buffer_vpavperpzs_4, scratch_dummy.buffer_vpavperpzs_5, scratch_dummy.buffer_vpavperpzs_6, r_diffusion) end end @@ -1181,7 +1182,8 @@ 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}, + r_advect, vpa, vperp, z, r, composition, adv1::AbstractArray{mk_float,4}, + adv2::AbstractArray{mk_float,4}, end1::AbstractArray{mk_float,4}, end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, buffer2::AbstractArray{mk_float,4}, r_diffusion::Bool) @@ -1191,10 +1193,12 @@ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc: # reconcile internal element boundaries across processes # & enforce periodicity and external boundaries if needed @loop_s_z_vperp_vpa is iz ivperp ivpa begin + adv1[ivpa,ivperp,iz,is] = r_advect[is].adv_fac[1,ivpa,ivperp,iz] + adv2[ivpa,ivperp,iz,is] = r_advect[is].adv_fac[end,ivpa,ivperp,iz] 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, + @views reconcile_element_boundaries_MPI!(f, adv1, adv2, end1, end2, buffer1, buffer2, r) end @@ -1216,11 +1220,11 @@ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc: # 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) + if r.irank == 0 && (r_diffusion || r_advect[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) + if r.irank == r.nrank - 1 && (r_diffusion || r_advect[is].speed[ir,ivpa,ivperp,iz] < -zero) f[ivpa,ivperp,iz,ir,is] = f_r_bc[ivpa,ivperp,iz,end,is] end end @@ -1230,8 +1234,9 @@ end """ enforce boundary conditions on charged particle f in z """ -function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, adv, - z, vperp, vpa, composition, end1::AbstractArray{mk_float,4}, +function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, z_advect, + z, vperp, vpa, composition, adv1::AbstractArray{mk_float,4}, + adv2::AbstractArray{mk_float,4}, 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 @@ -1240,11 +1245,13 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St # & enforce periodicity and external boundaries if needed nz = z.n @loop_s_r_vperp_vpa is ir ivperp ivpa begin + adv1[ivpa,ivperp,ir,is] = z_advect[is].adv_fac[1,ivpa,ivperp,ir] + adv2[ivpa,ivperp,ir,is] = z_advect[is].adv_fac[end,ivpa,ivperp,ir] 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, + @views reconcile_element_boundaries_MPI!(pdf, adv1, adv2, end1, end2, buffer1, buffer2, z) end # define a zero that accounts for finite precision @@ -1257,14 +1264,14 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St 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 + if z_advect[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 + if z_advect[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 @@ -1292,7 +1299,7 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St else @loop_r ir begin @views enforce_zero_incoming_bc!(pdf[:,:,:,ir,is], - adv[is].speed[:,:,:,ir], z, zero) + z_advect[is].speed[:,:,:,ir], z, zero) end end end @@ -1348,7 +1355,8 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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) + scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4, + scratch_dummy.buffer_vzvrvzetarsn_5, scratch_dummy.buffer_vzvrvzetarsn_6) end if r.n > 1 begin_sn_z_vzeta_vr_vz_region() @@ -1356,12 +1364,14 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, + scratch_dummy.buffer_vzvrvzetazsn_5, scratch_dummy.buffer_vzvrvzetazsn_6, 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, + f_r_bc::AbstractArray{mk_float,6}, r_advect, vz, vr, vzeta, z, r, composition, + adv1::AbstractArray{mk_float,5}, adv2::AbstractArray{mk_float,5}, end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}, r_diffusion) #f_initial, @@ -1373,10 +1383,12 @@ function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, # 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 + adv1[ivz,ivr,ivzeta,iz,isn] = r_advect[isn].speed[1,ivz,ivr,ivzeta,iz] + adv2[ivz,ivr,ivzeta,iz,isn] = r_advect[isn].speed[nr,ivz,ivr,ivzeta,iz] 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, + @views reconcile_element_boundaries_MPI!(f, adv1, adv2, end1, end2, buffer1, buffer2, r) end # 'periodic' BC enforces periodicity by taking the average of the boundary points @@ -1396,12 +1408,12 @@ function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, @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) + if r.irank == 0 && (r_diffusion || r_advect[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) + if r.irank == r.nrank - 1 && (r_diffusion || r_advect[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 @@ -1412,8 +1424,9 @@ 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, + upar_ion, Er, boundary_distributions, z_advect, z, vzeta, vr, vz, composition, geometry, + adv1::AbstractArray{mk_float,5}, adv2::AbstractArray{mk_float,5}, end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}) @@ -1423,11 +1436,13 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de # & enforce periodicity and external boundaries if needed nz = z.n @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin + adv1[ivz,ivr,ivzeta,ir,isn] = z_advect[isn].speed[1,ivz,ivr,ivzeta,ir] + adv2[ivz,ivr,ivzeta,ir,isn] = z_advect[isn].speed[nz,ivz,ivr,ivzeta,ir] 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, + @views reconcile_element_boundaries_MPI!(pdf, adv1, adv2, end1, end2, buffer1, buffer2, z) end @@ -1440,7 +1455,7 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de 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 + if z_advect[isn].speed[1,ivz,ivr,ivzeta,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) @@ -1449,7 +1464,7 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de 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 + if z_advect[isn].speed[end,ivz,ivr,ivzeta,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) From fb6eb61f70d7e71a29530293dd433c154e835d15 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 12 Dec 2023 17:01:36 +0000 Subject: [PATCH 326/331] Bring test_scripts/GaussLobattoLegendre_test.jl up to date. --- test_scripts/GaussLobattoLegendre_test.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test_scripts/GaussLobattoLegendre_test.jl b/test_scripts/GaussLobattoLegendre_test.jl index e60ece8d8..80b865716 100644 --- a/test_scripts/GaussLobattoLegendre_test.jl +++ b/test_scripts/GaussLobattoLegendre_test.jl @@ -14,6 +14,7 @@ using moment_kinetics.gauss_legendre using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.coordinates: define_coordinate using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_derivative! +using moment_kinetics.calculus: mass_matrix_solve! function print_matrix(matrix,name,n,m) @@ -69,7 +70,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv nrank, irank, y_L, discretization, fd_option, cheb_option, bc, adv_input,comm,element_spacing_option) # create the coordinate structs - y, y_spectral = define_coordinate(y_input) + y, y_spectral = define_coordinate(y_input,init_YY=false) #print_matrix(Mmat,"Mmat",y.n,y.n) #print_matrix(y_spectral.radau.M0,"local radau mass matrix M0",y.ngrid,y.ngrid) #print_matrix(y_spectral.radau.M1,"local radau mass matrix M1",y.ngrid,y.ngrid) @@ -147,14 +148,14 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv println("max(d2f_err) (double first derivative by interpolation): ",maximum(d2f_err)) if y.name == "vpa" mul!(b,y_spectral.S_matrix,f_exact) - gausslegendre_mass_matrix_solve!(df_num,b,y_spectral) + mass_matrix_solve!(df_num,b,y_spectral) @. df_err = df_num - df_exact #println("df_num (weak form): ",df_num) #println("df_exact (weak form): ",df_exact) println("max(df_err) (weak form): ",maximum(df_err)) second_derivative!(d2f_num, f_exact, y, y_spectral) #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) + #mass_matrix_solve!(d2f_num,b,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -166,7 +167,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv elseif y.name == "vperp" #println("condition: ",cond(y_spectral.mass_matrix)) mul!(b,y_spectral.S_matrix,g_exact) - gausslegendre_mass_matrix_solve!(divg_num,b,y_spectral) + mass_matrix_solve!(divg_num,b,y_spectral) @. divg_err = abs(divg_num - divg_exact) #println("divg_b (weak form): ",b) #println("divg_num (weak form): ",divg_num) @@ -175,7 +176,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv second_derivative!(d2f_num, f_exact, y, y_spectral) #mul!(b,y_spectral.K_matrix,f_exact) - #gausslegendre_mass_matrix_solve!(d2f_num,b,y_spectral) + #mass_matrix_solve!(d2f_num,b,y_spectral) @. d2f_err = abs(d2f_num - d2f_exact) #(0.5*y.L/y.nelement_global)* #println(d2f_num) #println(d2f_exact) @@ -187,7 +188,7 @@ using moment_kinetics.calculus: derivative!, second_derivative!, laplacian_deriv laplacian_derivative!(laph_num, h_exact, y, y_spectral) #mul!(b,y_spectral.L_matrix,h_exact) - #gausslegendre_mass_matrix_solve!(laph_num,b,y_spectral) + #mass_matrix_solve!(laph_num,b,y_spectral) @. laph_err = abs(laph_num - laph_exact) #(0.5*y.L/y.nelement_global)* #println(b[1:10]) #println(laph_num) From d306695e713bbd35c5dadc5c3fd7ecc194de7c66 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 12 Dec 2023 18:19:19 +0000 Subject: [PATCH 327/331] Modify the explicit boundary terms option for K (d^2 / d coord^2) and L (Laplacian) stiffness matrices so that internal boundary terms are not included. This appears to improve the numerical stability of the diffusion operators without losing accuracy in a single application of the derivative on a smooth function. --- src/gauss_legendre.jl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index 11bd5f501..ddc131530 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -986,7 +986,7 @@ function get_KK_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, coord;explicit_BC_terms=false) - + nelement = coord.nelement_local scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp @@ -995,16 +995,18 @@ function get_KK_local!(QQ,ielement, if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 - lobatto.P0 # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == 1 imin = coord.imin[ielement] - 1 - imax = coord.imax[ielement] @. QQ[1,:] -= coord.grid[imin]*lobatto.Dmat[1,:]/scale_factor + end + if explicit_BC_terms && ielement == nelement + imax = coord.imax[ielement] @. QQ[coord.ngrid,:] += coord.grid[imax]*lobatto.Dmat[coord.ngrid,:]/scale_factor end else # radau points @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 - radau.P0 # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == nelement imax = coord.imax[ielement] @. QQ[coord.ngrid,:] += coord.grid[imax]*radau.Dmat[coord.ngrid,:]/scale_factor end @@ -1012,8 +1014,10 @@ function get_KK_local!(QQ,ielement, else # assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.K0/scale_factor # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == 1 @. QQ[1,:] -= lobatto.Dmat[1,:]/scale_factor + end + if explicit_BC_terms && ielement == nelement @. QQ[coord.ngrid,:] += lobatto.Dmat[coord.ngrid,:]/scale_factor end end @@ -1050,7 +1054,7 @@ function get_LL_local!(QQ,ielement, lobatto::gausslegendre_base_info, radau::gausslegendre_base_info, coord;explicit_BC_terms=false) - + nelement = coord.nelement_local scale_factor = coord.element_scale[ielement] shift_factor = coord.element_shift[ielement] if coord.name == "vperp" # assume integrals of form int^infty_0 (.) vperp d vperp @@ -1059,16 +1063,18 @@ function get_LL_local!(QQ,ielement, if ielement > 1 || coord.irank > 0 # lobatto points @. QQ = (shift_factor/scale_factor)*lobatto.K0 + lobatto.K1 # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == 1 imin = coord.imin[ielement] - 1 - imax = coord.imax[ielement] @. QQ[1,:] -= coord.grid[imin]*lobatto.Dmat[1,:]/scale_factor + end + if explicit_BC_terms && ielement == nelement + imax = coord.imax[ielement] @. QQ[coord.ngrid,:] += coord.grid[imax]*lobatto.Dmat[coord.ngrid,:]/scale_factor end else # radau points @. QQ = (shift_factor/scale_factor)*radau.K0 + radau.K1 # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == nelement imax = coord.imax[ielement] @. QQ[coord.ngrid,:] += coord.grid[imax]*radau.Dmat[coord.ngrid,:]/scale_factor end @@ -1076,8 +1082,10 @@ function get_LL_local!(QQ,ielement, else # d^2 (.) d vpa^2 -- assume integrals of form int^infty_-infty (.) d vpa @. QQ = lobatto.K0/scale_factor # boundary terms from integration by parts - if explicit_BC_terms + if explicit_BC_terms && ielement == 1 @. QQ[1,:] -= lobatto.Dmat[1,:]/scale_factor + end + if explicit_BC_terms && ielement == nelement @. QQ[coord.ngrid,:] += lobatto.Dmat[coord.ngrid,:]/scale_factor end end From 6dfd0d9e310ab9de8fc9e385d9e3dc325aeda934 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Tue, 12 Dec 2023 18:26:54 +0000 Subject: [PATCH 328/331] Cheap input file for relaxing an initial 0D2V distribution function with numerical dissipation. --- .../num-diss-relaxation.toml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 examples/numerical-dissipation/num-diss-relaxation.toml diff --git a/examples/numerical-dissipation/num-diss-relaxation.toml b/examples/numerical-dissipation/num-diss-relaxation.toml new file mode 100644 index 000000000..29965e248 --- /dev/null +++ b/examples/numerical-dissipation/num-diss-relaxation.toml @@ -0,0 +1,68 @@ +# cheap input file for a 0D2V relaxation with numerical diffusion terms d^2 F / dvpa^2 and d^2 F / vperp^2. +n_ion_species = 1 +n_neutral_species = 0 +electron_physics = "boltzmann_electron_response" +evolve_moments_density = false +evolve_moments_parallel_flow = false +evolve_moments_parallel_pressure = false +evolve_moments_conservation = false +T_e = 1.0 +T_wall = 1.0 +rhostar = 1.0 +Bzed = 1.0 +Bmag = 1.0 +initial_density1 = 0.5 +initial_temperature1 = 1.0 +initial_density2 = 0.5 +initial_temperature2 = 1.0 +z_IC_option1 = "sinusoid" +z_IC_density_amplitude1 = 0.001 +z_IC_density_phase1 = 0.0 +z_IC_upar_amplitude1 = 0.0 +z_IC_upar_phase1 = 0.0 +z_IC_temperature_amplitude1 = 0.0 +z_IC_temperature_phase1 = 0.0 +z_IC_option2 = "sinusoid" +z_IC_density_amplitude2 = 0.001 +z_IC_density_phase2 = 0.0 +z_IC_upar_amplitude2 = 0.0 +z_IC_upar_phase2 = 0.0 +z_IC_temperature_amplitude2 = 0.0 +z_IC_temperature_phase2 = 0.0 +charge_exchange_frequency = 0.0 +ionization_frequency = 0.0 +constant_ionization_rate = false +nuii = 0.0 +nstep = 2000 +dt = 1.0e-3 +nwrite = 2000 +nwrite_dfns = 2000 +use_semi_lagrange = false +n_rk_stages = 4 +split_operators = false +z_ngrid = 1 +z_nelement = 1 +z_nelement_local = 1 +z_bc = "wall" +z_discretization = "chebyshev_pseudospectral" +r_ngrid = 1 +r_nelement = 1 +r_nelement_local = 1 +r_bc = "periodic" +r_discretization = "chebyshev_pseudospectral" +vpa_ngrid = 5 +vpa_nelement = 16 +vpa_L = 6.0 +vpa_bc = "zero" +vpa_discretization = "gausslegendre_pseudospectral" +vperp_ngrid = 5 +vperp_nelement = 8 +vperp_L = 3.0 +vperp_bc = "zero" +vperp_discretization = "gausslegendre_pseudospectral" + +[numerical_dissipation] +vpa_dissipation_coefficient = 0.1 +vperp_dissipation_coefficient = 0.1 +z_dissipation_coefficient = -1.0 +r_dissipation_coefficient = -1.0 From 97caaad1de5216848f6c5575bc989f0fc26503b3 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 13 Dec 2023 10:04:58 +0000 Subject: [PATCH 329/331] Remove unused code from setup_global_weak_form_matrix!(). This function is now explicitly intended for constructing weak matrices for use with numerical differentiation and not for the solving of 1D ODEs. This is because the experimental features for imposing boundary conditions on the matrix are removed. --- src/gauss_legendre.jl | 60 +++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/gauss_legendre.jl b/src/gauss_legendre.jl index ddc131530..c93d263c2 100644 --- a/src/gauss_legendre.jl +++ b/src/gauss_legendre.jl @@ -826,11 +826,26 @@ function scaled_gauss_legendre_radau_grid(ngrid, nelement_local, n_local, elemen end """ -function that assigns the local weak-form matrices to +A function that assigns the local weak-form matrices to a global array QQ_global for later solving weak form of required -1D equation +1D equation. This function only supports fully local grids +that have coord.nelement_local = coord.nelement_global. -option choosing type of matrix to be constructed -- "M" (mass matrix), "S" (derivative matrix) +The 'option' variable is a flag for +choosing the type of matrix to be constructed. +Currently the function is set up to assemble the +elemental matrices without imposing boundary conditions on the +first and final rows of the matrix. This means that +the operators constructed from this function can only be used +for differentiation, and not solving 1D ODEs. +The shared points in the element assembly are +averaged (instead of simply added) to be consistent with the +derivative_elements_to_full_grid!() function in calculus.jl, +which is used to form the RHS of the equation + + M * d2f = K * f + +where M is the mass matrix and K is the stiffness matrix. """ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, lobatto::gausslegendre_base_info, @@ -843,55 +858,32 @@ function setup_global_weak_form_matrix!(QQ_global::Array{mk_float,2}, imin = coord.imin imax = coord.imax @. QQ_global = 0.0 - mass_matrix = (option == "M") && false - if coord.name == "vperp" - zero_bc_upper_boundary = true && mass_matrix - zero_bc_lower_boundary = false && mass_matrix - zero_gradient_bc_lower_boundary = false && mass_matrix - else - zero_bc_upper_boundary = (coord.bc == "zero" || coord.bc == "zero_upper") && mass_matrix - zero_bc_lower_boundary = (coord.bc == "zero" || coord.bc == "zero_lower") && mass_matrix - zero_gradient_bc_lower_boundary = false && mass_matrix - end + # fill in first element j = 1 # N.B. QQ varies with ielement for vperp, but not vpa + # a radau element is used for the vperp grid (see get_QQ_local!()) get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) - - if zero_bc_lower_boundary #x.bc == "zero" - QQ_global[imin[j],imin[j]:imax[j]] .= 0.0 - QQ_global[imin[j],imin[j]] = 1.0 - elseif zero_gradient_bc_lower_boundary - QQ_global[imin[j],imin[j]:imax[j]] .= radau.D0[:] - else - QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:] - end + QQ_global[imin[j],imin[j]:imax[j]] .+= QQ_j[1,:] for k in 2:imax[j]-imin[j] QQ_global[k,imin[j]:imax[j]] .+= QQ_j[k,:] end - if zero_bc_upper_boundary && coord.nelement_local == 1 - QQ_global[imax[j],imin[j]:imax[j]] .= 0.0 - QQ_global[imax[j],imax[j]] = 1.0 - elseif coord.nelement_local > 1 #x.bc == "zero" + if coord.nelement_local > 1 QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:]./2.0 else QQ_global[imax[j],imin[j]:imax[j]] .+= QQ_j[ngrid,:] - end + end # remaining elements recalling definitions of imax and imin for j in 2:coord.nelement_local get_QQ_local!(QQ_j,j,lobatto,radau,coord,option) - - #lower boundary condition on element + #lower boundary assembly on element QQ_global[imin[j]-1,imin[j]-1:imax[j]] .+= QQ_j[1,:]./2.0 for k in 2:imax[j]-imin[j]+1 QQ_global[k+imin[j]-2,imin[j]-1:imax[j]] .+= QQ_j[k,:] end - # upper boundary condition on element - if j == coord.nelement_local && !(zero_bc_upper_boundary) + # upper boundary assembly on element + if j == coord.nelement_local QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:] - elseif j == coord.nelement_local && zero_bc_upper_boundary - QQ_global[imax[j],imin[j]-1:imax[j]] .= 0.0 #contributions from this element/2 - QQ_global[imax[j],imax[j]] = 1.0 else QQ_global[imax[j],imin[j]-1:imax[j]] .+= QQ_j[ngrid,:]./2.0 end From a1e44155601a7c97aee4a517a7c2dc90c6b3aa22 Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 13 Dec 2023 10:15:23 +0000 Subject: [PATCH 330/331] Correct typo in unused (but potentially useful) function setup_distributed_memory_MPI_for_weights_precomputation() to likely correct value based on comparison to corresponding lines in setup_distributed_memory_MPI(). --- src/communication.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication.jl b/src/communication.jl index f18fe6729..395c57b51 100644 --- a/src/communication.jl +++ b/src/communication.jl @@ -298,7 +298,7 @@ function setup_distributed_memory_MPI_for_weights_precomputation(vpa_nelement_gl # construct a communicator for intra-block communication comm_block[] = MPI.Comm_split(comm_vpavperp,iblock,irank_block) - vpa_ngroup = vpa_nchunks + vpa_ngroup = vperp_nchunks vpa_nrank_per_group = vpa_nchunks vpa_igroup = floor(mk_int,iblock/vpa_nchunks) # iblock(irank) - > vpa_igroup vpa_irank = mod(iblock,vpa_nchunks) # iblock(irank) -> vpa_irank From ca15efd7e2e1bc74999f7e1dcc6947cfe0dd808c Mon Sep 17 00:00:00 2001 From: Michael Hardman Date: Wed, 13 Dec 2023 12:08:54 +0000 Subject: [PATCH 331/331] Revert "Switch from reconcile_element_boundaries_MPI!() using centred averaging of element boundaries to upwind choice for element boundaries. This requires extra dummy arrays to be passed to the boundary condition functions." This reverts commit 816e058a125b773df1af649a4a7145570d737998. --- src/initial_conditions.jl | 57 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/src/initial_conditions.jl b/src/initial_conditions.jl index b52950aec..29b57d62a 100644 --- a/src/initial_conditions.jl +++ b/src/initial_conditions.jl @@ -1157,15 +1157,14 @@ function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, v @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, - scratch_dummy.buffer_vpavperprs_5, scratch_dummy.buffer_vpavperprs_6) + 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, scratch_dummy.buffer_vpavperpzs_5, scratch_dummy.buffer_vpavperpzs_6, + scratch_dummy.buffer_vpavperpzs_3, scratch_dummy.buffer_vpavperpzs_4, r_diffusion) end end @@ -1182,8 +1181,7 @@ end enforce boundary conditions on f in r """ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc::String, - r_advect, vpa, vperp, z, r, composition, adv1::AbstractArray{mk_float,4}, - adv2::AbstractArray{mk_float,4}, end1::AbstractArray{mk_float,4}, + 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) @@ -1193,12 +1191,10 @@ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc: # reconcile internal element boundaries across processes # & enforce periodicity and external boundaries if needed @loop_s_z_vperp_vpa is iz ivperp ivpa begin - adv1[ivpa,ivperp,iz,is] = r_advect[is].adv_fac[1,ivpa,ivperp,iz] - adv2[ivpa,ivperp,iz,is] = r_advect[is].adv_fac[end,ivpa,ivperp,iz] 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, adv1, adv2, + @views reconcile_element_boundaries_MPI!(f, end1, end2, buffer1, buffer2, r) end @@ -1220,11 +1216,11 @@ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc: # 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 || r_advect[is].speed[ir,ivpa,ivperp,iz] > zero) + 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 || r_advect[is].speed[ir,ivpa,ivperp,iz] < -zero) + 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 @@ -1234,9 +1230,8 @@ end """ enforce boundary conditions on charged particle f in z """ -function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, z_advect, - z, vperp, vpa, composition, adv1::AbstractArray{mk_float,4}, - adv2::AbstractArray{mk_float,4}, end1::AbstractArray{mk_float,4}, +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 @@ -1245,13 +1240,11 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St # & enforce periodicity and external boundaries if needed nz = z.n @loop_s_r_vperp_vpa is ir ivperp ivpa begin - adv1[ivpa,ivperp,ir,is] = z_advect[is].adv_fac[1,ivpa,ivperp,ir] - adv2[ivpa,ivperp,ir,is] = z_advect[is].adv_fac[end,ivpa,ivperp,ir] 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, adv1, adv2, + @views reconcile_element_boundaries_MPI!(pdf, end1, end2, buffer1, buffer2, z) end # define a zero that accounts for finite precision @@ -1264,14 +1257,14 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St vwidth = 1.0 if z.irank == 0 @loop_s_r_vperp_vpa is ir ivperp ivpa begin - if z_advect[is].speed[ivpa,1,ir] > 0.0 + 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 z_advect[is].speed[ivpa,end,ir] > 0.0 + 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 @@ -1299,7 +1292,7 @@ function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::St else @loop_r ir begin @views enforce_zero_incoming_bc!(pdf[:,:,:,ir,is], - z_advect[is].speed[:,:,:,ir], z, zero) + adv[is].speed[:,:,:,ir], z, zero) end end end @@ -1355,8 +1348,7 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, - scratch_dummy.buffer_vzvrvzetarsn_5, scratch_dummy.buffer_vzvrvzetarsn_6) + scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4) end if r.n > 1 begin_sn_z_vzeta_vr_vz_region() @@ -1364,14 +1356,12 @@ function enforce_neutral_boundary_conditions!(f_neutral, f_charged, 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, - scratch_dummy.buffer_vzvrvzetazsn_5, scratch_dummy.buffer_vzvrvzetazsn_6, r_diffusion) end end function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, - f_r_bc::AbstractArray{mk_float,6}, r_advect, vz, vr, vzeta, z, r, composition, - adv1::AbstractArray{mk_float,5}, adv2::AbstractArray{mk_float,5}, + 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, @@ -1383,12 +1373,10 @@ function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, # 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 - adv1[ivz,ivr,ivzeta,iz,isn] = r_advect[isn].speed[1,ivz,ivr,ivzeta,iz] - adv2[ivz,ivr,ivzeta,iz,isn] = r_advect[isn].speed[nr,ivz,ivr,ivzeta,iz] 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, adv1, adv2, + @views reconcile_element_boundaries_MPI!(f, end1, end2, buffer1, buffer2, r) end # 'periodic' BC enforces periodicity by taking the average of the boundary points @@ -1408,12 +1396,12 @@ function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, @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 || r_advect[isn].speed[ir,ivz,ivr,ivzeta,iz] > zero) + 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 || r_advect[isn].speed[ir,ivz,ivr,ivzeta,iz] < -zero) + 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 @@ -1424,9 +1412,8 @@ 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, z_advect, + upar_ion, Er, boundary_distributions, adv, z, vzeta, vr, vz, composition, geometry, - adv1::AbstractArray{mk_float,5}, adv2::AbstractArray{mk_float,5}, end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}) @@ -1436,13 +1423,11 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de # & enforce periodicity and external boundaries if needed nz = z.n @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - adv1[ivz,ivr,ivzeta,ir,isn] = z_advect[isn].speed[1,ivz,ivr,ivzeta,ir] - adv2[ivz,ivr,ivzeta,ir,isn] = z_advect[isn].speed[nz,ivz,ivr,ivzeta,ir] 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, adv1, adv2, + @views reconcile_element_boundaries_MPI!(pdf, end1, end2, buffer1, buffer2, z) end @@ -1455,7 +1440,7 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de vwidth = 1.0 if z.irank == 0 @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - if z_advect[isn].speed[1,ivz,ivr,ivzeta,ir] > 0.0 + 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) @@ -1464,7 +1449,7 @@ function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, de end if z.irank == z.nrank - 1 @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - if z_advect[isn].speed[end,ivz,ivr,ivzeta,ir] > 0.0 + 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)