Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement collision source terms for multi-ion MHD #2213

Merged
merged 31 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
23d74ec
Add collision source terms for multi-ion MHD
amrueda Dec 17, 2024
f021401
Fixed docstring issue
amrueda Dec 18, 2024
9ef9542
Use zero(eltype(u)) instead of 0
amrueda Dec 18, 2024
e611c30
Removed unexisting function from export
amrueda Dec 18, 2024
5dabad1
Fixed more docstring issues
amrueda Dec 18, 2024
5c772f6
Skip computation of collision sources of an ion species with itself
amrueda Dec 18, 2024
6104177
Added missing elixir
amrueda Dec 18, 2024
98949a4
Improved documentation
amrueda Dec 18, 2024
02aca9b
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Dec 18, 2024
0971c19
Double precision variables in initial condition
amrueda Dec 18, 2024
fde0b1d
Removed extra backtick
amrueda Dec 18, 2024
51f651e
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Dec 19, 2024
22bf32f
Use CarpenterKennedy2N54 for the collision sources' test
amrueda Dec 19, 2024
f6ad4f7
Rewrote temperature functions in elixir to increase coverage
amrueda Dec 19, 2024
1b036f4
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Dec 20, 2024
0160b44
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Jan 6, 2025
25d78c3
Apply suggestions from code review
amrueda Jan 6, 2025
3d7875b
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Feb 19, 2025
8d0552a
Removed unnecessary sqrt(v)^2
amrueda Feb 19, 2025
6bf1d48
Added DOIs and bib codes
amrueda Feb 19, 2025
7eeaf9b
Added note about mesh
amrueda Feb 19, 2025
85213cb
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Feb 20, 2025
d373da8
Apply suggestions from code review
amrueda Feb 20, 2025
3347267
Update examples/tree_2d_dgsem/elixir_mhdmultiion_collisions.jl
amrueda Feb 20, 2025
41cc9c4
Changed index of species k'->l to avoid confusion
amrueda Feb 20, 2025
00f942f
Changed variable name
amrueda Feb 20, 2025
42739b1
Update examples/tree_2d_dgsem/elixir_mhdmultiion_collisions.jl
amrueda Feb 20, 2025
cf30eda
Added missing DOI
amrueda Feb 20, 2025
2d8c769
Merge branch 'main' into arr/multi_ion_collision_sources
amrueda Feb 21, 2025
28235a6
Made example compatible with other real types
amrueda Feb 21, 2025
c8b4bf4
psi defined separately in initial condition
amrueda Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions examples/tree_2d_dgsem/elixir_mhdmultiion_collisions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@

using OrdinaryDiffEq
using Trixi

###############################################################################
# This elixir describes the frictional slowing of an ionized carbon fluid (C6+) with respect to another species
# of a background ionized carbon fluid with an initially nonzero relative velocity. It is the second slow-down
# test (fluids with different densities) described in:
# - Ghosh, D., Chapman, T. D., Berger, R. L., Dimits, A., & Banks, J. W. (2019). A
# multispecies, multifluid model for laser–induced counterstreaming plasma simulations.
# Computers & Fluids, 186, 38-57.
#
# This is effectively a zero-dimensional case because the spatial gradients are zero, and we use it to test the
# collision source terms.
#
# To run this physically relevant test, we use the following characteristic quantities to non-dimensionalize
# the equations:
# Characteristic length: L_inf = 1.00E-03 m (domain size)
# Characteristic density: rho_inf = 1.99E+00 kg/m^3 (corresponds to a number density of 1e20 cm^{-3})
# Characteristic vacuum permeability: mu0_inf = 1.26E-06 N/A^2 (for equations with mu0 = 1)
# Characteristic gas constant: R_inf = 6.92237E+02 J/kg/K (specific gas constant for a Carbon fluid)
# Characteristic velocity: V_inf = 1.00E+06 m/s
#
# The results of the paper can be reproduced using `source_terms = source_terms_collision_ion_ion` (i.e., only
# taking into account ion-ion collisions). However, we include ion-electron collisions assuming a constant
# electron temperature of 1 keV in this elixir to test the function `source_terms_collision_ion_electron`

# Return the electron pressure for a constant electron temperature Te = 1 keV
function electron_pressure_constantTe(u, equations::IdealGlmMhdMultiIonEquations2D)
@unpack charge_to_mass = equations
Te = 0.008029953773 # [nondimensional] = 1 [keV]
total_electron_charge = zero(eltype(u))
for k in eachcomponent(equations)
rho_k = u[3 + (k - 1) * 5 + 1]
total_electron_charge += rho_k * charge_to_mass[k]
end

# Boltzmann constant divided by elementary charge
kB_e = 7.86319034E-02 #[nondimensional]

return total_electron_charge * kB_e * Te
end

# Return the constant electron temperature Te = 1 keV
function electron_temperature_constantTe(u, equations::IdealGlmMhdMultiIonEquations2D)
return 0.008029953773 # [nondimensional] = 1 [keV]
end

# semidiscretization of the ideal MHD equations
equations = IdealGlmMhdMultiIonEquations2D(gammas = (5 / 3, 5 / 3),
charge_to_mass = (76.3049060157692000,
76.3049060157692000), # [nondimensional]
gas_constants = (1.0, 1.0), # [nondimensional]
molar_masses = (1.0, 1.0), # [nondimensional]
ion_ion_collision_constants = [0.0 0.4079382480442680;
0.4079382480442680 0.0], # [nondimensional] (computed with eq (4.142) of Schunk&Nagy (2009))
ion_electron_collision_constants = (8.56368379833E-06,
8.56368379833E-06), # [nondimensional] (computed with eq (9) of Ghosh et al. (2019))
electron_pressure = electron_pressure_constantTe,
electron_temperature = electron_temperature_constantTe,
initial_c_h = 0.0) # Deactivate GLM divergence cleaning

# Frictional slowing of an ionized carbon fluid with respect to another background carbon fluid in motion
function initial_condition_slow_down(x, t, equations::IdealGlmMhdMultiIonEquations2D)
v11 = 0.65508770000000
v21 = 0.0
v2 = v3 = 0.0
B1 = B2 = B3 = 0.0
rho1 = 0.1
rho2 = 1.0

p1 = 0.00040170535986
p2 = 0.00401705359856

return prim2cons(SVector(B1, B2, B3, rho1, v11, v2, v3, p1, rho2, v21, v2, v3, p2, 0.0),
equations)
end

# Temperature of ion 1
function temperature1(u, equations::IdealGlmMhdMultiIonEquations2D)
rho_1, _ = Trixi.get_component(1, u, equations)
p = pressure(u, equations)

return p[1] / rho_1 / equations.gas_constants[1]
end

# Temperature of ion 2
function temperature2(u, equations::IdealGlmMhdMultiIonEquations2D)
rho_2, _ = Trixi.get_component(2, u, equations)
p = pressure(u, equations)

return p[2] / rho_2 / equations.gas_constants[2]
end

initial_condition = initial_condition_slow_down
tspan = (0.0, 0.1) # 100 [ps]

# Entropy conservative volume numerical fluxes with standard LLF dissipation at interfaces
volume_flux = (flux_ruedaramirez_etal, flux_nonconservative_ruedaramirez_etal)
surface_flux = (flux_lax_friedrichs, flux_nonconservative_central)

solver = DGSEM(polydeg = 3, surface_flux = surface_flux,
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))

coordinates_min = (0.0, 0.0)
coordinates_max = (1.0, 1.0)
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 1,
n_cells_max = 1_000_000)

# Ion-ion and ion-electron collision source terms
# In this particular case, we can omit source_terms_lorentz because the magnetic field is zero!
function source_terms(u, x, t, equations::IdealGlmMhdMultiIonEquations2D)
source_terms_collision_ion_ion(u, x, t, equations) +
source_terms_collision_ion_electron(u, x, t, equations)
end

semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
source_terms = source_terms)

###############################################################################
# ODE solvers, callbacks etc.

ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 1
analysis_callback = AnalysisCallback(semi,
save_analysis = true,
interval = analysis_interval,
extra_analysis_integrals = (temperature1,
temperature2))
alive_callback = AliveCallback(analysis_interval = analysis_interval)

stepsize_callback = StepsizeCallback(cfl = 0.01) # Very small CFL due to the stiff source terms

save_restart = SaveRestartCallback(interval = 100,
save_final_restart = true)

callbacks = CallbackSet(summary_callback,
analysis_callback, alive_callback,
save_restart,
stepsize_callback)

###############################################################################

sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

summary_callback() # print the timer summary
3 changes: 2 additions & 1 deletion src/Trixi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ export boundary_condition_do_nothing,
BoundaryConditionCoupled

export initial_condition_convergence_test, source_terms_convergence_test,
source_terms_lorentz
source_terms_lorentz, source_terms_collision_ion_electron,
source_terms_collision_ion_ion
export source_terms_harmonic
export initial_condition_poisson_nonperiodic, source_terms_poisson_nonperiodic,
boundary_condition_poisson_nonperiodic
Expand Down
184 changes: 184 additions & 0 deletions src/equations/ideal_glm_mhd_multiion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@ divergence_cleaning_field(u, equations::AbstractIdealGlmMhdMultiIonEquations) =
return rho
end

@inline function pressure(u, equations::AbstractIdealGlmMhdMultiIonEquations)
B1, B2, B3, _ = u
p = zero(MVector{ncomponents(equations), real(equations)})
for k in eachcomponent(equations)
rho, rho_v1, rho_v2, rho_v3, rho_e = get_component(k, u, equations)
v1 = rho_v1 / rho
v2 = rho_v2 / rho
v3 = rho_v3 / rho
v_mag = sqrt(v1^2 + v2^2 + v3^2)
gamma = equations.gammas[k]
p[k] = (gamma - 1) *
(rho_e - 0.5f0 * rho * v_mag^2 - 0.5f0 * (B1^2 + B2^2 + B3^2))
end
return SVector{ncomponents(equations), real(equations)}(p)
end

#Convert conservative variables to primitive
function cons2prim(u, equations::AbstractIdealGlmMhdMultiIonEquations)
@unpack gammas = equations
Expand Down Expand Up @@ -471,4 +487,172 @@ end

return dissipation
end

@doc raw"""
source_terms_collision_ion_ion(u, x, t,
equations::AbstractIdealGlmMhdMultiIonEquations)

Compute the ion-ion collision source terms for the momentum and energy equations of each ion species as
```math
\begin{aligned}
\vec{s}_{\rho_k \vec{v}_k} =& \rho_k\sum_{k'}\bar{\nu}_{kk'}(\vec{v}_{k'} - \vec{v}_k),\\
s_{E_k} =&
3 \sum_{k'} \left(
\bar{\nu}_{kk'} \frac{\rho_k M_1}{M_{k'} + M_k} R_1 (T_{k'} - T_k)
\right) +
\sum_{k'} \left(
\bar{\nu}_{kk'} \rho_k \frac{M_{k'}}{M_{k'} + M_k} \|\vec{v}_{k'} - \vec{v}_k\|^2
\right)
+
\vec{v}_k \cdot \vec{s}_{\rho_k \vec{v}_k},
\end{aligned}
```
where ``M_k`` is the molar mass of ion species `k` provided in `equations.molar_masses`,
``R_k`` is the specific gas constant of ion species `k` provided in `equations.gas_constants`, and
``\bar{\nu}_{kk'}`` is the effective collision frequency of species `k` with species `k'`, which is computed as
```math
\begin{aligned}
\bar{\nu}_{kk'} = \bar{\nu}^1_{kk'} \tilde{B}_{kk'} \frac{\rho_{k'}}{T_{k k'}^{3/2}},
\end{aligned}
```
with the so-called reduced temperature ``T_{k k'}`` and the ion-ion collision constants ``\tilde{B}_{kk'}`` provided
in `equations.ion_electron_collision_constants` (see [`IdealGlmMhdMultiIonEquations2D`](@ref)).

The additional coefficient ``\bar{\nu}^1_{kk'}`` is a non-dimensional drift correction factor proposed by Rambo and
Denavit.

References:
- P. Rambo, J. Denavit, Interpenetration and ion separation in colliding plasmas, Physics of Plasmas 1 (1994) 4050–4060.
- Schunk, R. W., Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.
Cambridge university press.
"""
function source_terms_collision_ion_ion(u, x, t,
equations::AbstractIdealGlmMhdMultiIonEquations)
s = zero(MVector{nvariables(equations), eltype(u)})
@unpack gas_constants, molar_masses, ion_ion_collision_constants = equations

prim = cons2prim(u, equations)

for k in eachcomponent(equations)
rho_k, v1_k, v2_k, v3_k, p_k = get_component(k, prim, equations)
T_k = p_k / (rho_k * gas_constants[k])

S_q1 = zero(eltype(u))
S_q2 = zero(eltype(u))
S_q3 = zero(eltype(u))
S_E = zero(eltype(u))
for l in eachcomponent(equations)
# Do not compute collisions of an ion species with itself
k == l && continue

rho_l, v1_l, v2_l, v3_l, p_l = get_component(l, prim, equations)
T_l = p_l / (rho_l * gas_constants[l])

# Reduced temperature
T_kl = (molar_masses[l] * T_k + molar_masses[k] * T_l) /
(molar_masses[k] + molar_masses[l])

delta_v2 = (v1_l - v1_k)^2 + (v2_l - v2_k)^2 + (v3_l - v3_k)^2

# Compute collision frequency without drifting correction
v_kl = ion_ion_collision_constants[k, l] * rho_l / T_kl^(3 / 2)

# Correct the collision frequency with the drifting effect
z2 = delta_v2 / (p_l / rho_l + p_k / rho_k)
v_kl /= (1 + (2 / (9 * pi))^(1 / 3) * z2)^(3 / 2)

S_q1 += rho_k * v_kl * (v1_l - v1_k)
S_q2 += rho_k * v_kl * (v2_l - v2_k)
S_q3 += rho_k * v_kl * (v3_l - v3_k)

S_E += (3 * molar_masses[1] * gas_constants[1] * (T_l - T_k)
+
molar_masses[l] * delta_v2) * v_kl * rho_k /
(molar_masses[k] + molar_masses[l])
end

S_E += (v1_k * S_q1 + v2_k * S_q2 + v3_k * S_q3)

set_component!(s, k, 0, S_q1, S_q2, S_q3, S_E, equations)
end
return SVector{nvariables(equations), real(equations)}(s)
end

@doc raw"""
source_terms_collision_ion_electron(u, x, t,
equations::AbstractIdealGlmMhdMultiIonEquations)

Compute the ion-electron collision source terms for the momentum and energy equations of each ion species. We assume ``v_e = v^+``
(no effect of currents on the electron velocity).

The collision sources read as
```math
\begin{aligned}
\vec{s}_{\rho_k \vec{v}_k} =& \rho_k \bar{\nu}_{ke} (\vec{v}_{e} - \vec{v}_k),
\\
s_{E_k} =&
3 \left(
\bar{\nu}_{ke} \frac{\rho_k M_{1}}{M_k} R_1 (T_{e} - T_k)
\right)
+
\vec{v}_k \cdot \vec{s}_{\rho_k \vec{v}_k},
\end{aligned}
```
where ``T_e`` is the electron temperature computed with the function `equations.electron_temperature`,
``M_k`` is the molar mass of ion species `k` provided in `equations.molar_masses`,
``R_k`` is the specific gas constant of ion species `k` provided in `equations.gas_constants`, and
``\bar{\nu}_{kk'}`` is the collision frequency of species `k` with the electrons, which is computed as
```math
\begin{aligned}
\bar{\nu}_{ke} = \tilde{B}_{ke} \frac{e n_e}{T_e^{3/2}},
\end{aligned}
```
with the total electron charge ``e n_e`` (computed assuming quasi-neutrality), and the
ion-electron collision coefficient ``\tilde{B}_{ke}`` provided in `equations.ion_electron_collision_constants`,
which is scaled with the elementary charge (see [`IdealGlmMhdMultiIonEquations2D`](@ref)).

References:
- P. Rambo, J. Denavit, Interpenetration and ion separation in colliding plasmas, Physics of Plasmas 1 (1994) 4050–4060.
- Schunk, R. W., Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.
Cambridge university press.
"""
function source_terms_collision_ion_electron(u, x, t,
equations::AbstractIdealGlmMhdMultiIonEquations)
s = zero(MVector{nvariables(equations), eltype(u)})
@unpack gas_constants, molar_masses, ion_electron_collision_constants, electron_temperature = equations

prim = cons2prim(u, equations)
T_e = electron_temperature(u, equations)
T_e32 = T_e^(3 / 2)

v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,
equations)

# Compute total electron charge
total_electron_charge = zero(real(equations))
for k in eachcomponent(equations)
rho, _ = get_component(k, u, equations)
total_electron_charge += rho * equations.charge_to_mass[k]
end

for k in eachcomponent(equations)
rho_k, v1_k, v2_k, v3_k, p_k = get_component(k, prim, equations)
T_k = p_k / (rho_k * gas_constants[k])

# Compute effective collision frequency
v_ke = ion_electron_collision_constants[k] * total_electron_charge / T_e32

S_q1 = rho_k * v_ke * (v1_plus - v1_k)
S_q2 = rho_k * v_ke * (v2_plus - v2_k)
S_q3 = rho_k * v_ke * (v3_plus - v3_k)

S_E = 3 * molar_masses[1] * gas_constants[1] * (T_e - T_k) * v_ke * rho_k /
molar_masses[k]

S_E += (v1_k * S_q1 + v2_k * S_q2 + v3_k * S_q3)

set_component!(s, k, 0, S_q1, S_q2, S_q3, S_E, equations)
end
return SVector{nvariables(equations), real(equations)}(s)
end
end
Loading
Loading