Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/ideal_glm_mhd_multiion.jl
5586 views
1
# This file includes functions that are common for all AbstractIdealGlmMhdMultiIonEquations
2
3
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
4
# Since these FMAs can increase the performance of many numerical algorithms,
5
# we need to opt-in explicitly.
6
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
7
@muladd begin
8
#! format: noindent
9
10
have_nonconservative_terms(::AbstractIdealGlmMhdMultiIonEquations) = True()
11
12
# Variable names for the multi-ion GLM-MHD equation
13
# ATTENTION: the variable order for AbstractIdealGlmMhdMultiIonEquations is different than in the reference
14
# - A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
15
# of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
16
# [DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
17
# The first three entries of the state vector `cons[1:3]` are the magnetic field components. After that, we have chunks
18
# of 5 entries for the hydrodynamic quantities of each ion species. Finally, the last entry `cons[end]` is the divergence
19
# cleaning field.
20
function varnames(::typeof(cons2cons), equations::AbstractIdealGlmMhdMultiIonEquations)
21
cons = ("B1", "B2", "B3")
22
for i in eachcomponent(equations)
23
cons = (cons...,
24
tuple("rho_" * string(i), "rho_v1_" * string(i), "rho_v2_" * string(i),
25
"rho_v3_" * string(i), "rho_e_total_" * string(i))...)
26
end
27
cons = (cons..., "psi")
28
29
return cons
30
end
31
32
function varnames(::typeof(cons2prim), equations::AbstractIdealGlmMhdMultiIonEquations)
33
prim = ("B1", "B2", "B3")
34
for i in eachcomponent(equations)
35
prim = (prim...,
36
tuple("rho_" * string(i), "v1_" * string(i), "v2_" * string(i),
37
"v3_" * string(i), "p_" * string(i))...)
38
end
39
prim = (prim..., "psi")
40
41
return prim
42
end
43
44
function default_analysis_integrals(::AbstractIdealGlmMhdMultiIonEquations)
45
return (entropy_timederivative, Val(:l2_divb), Val(:linf_divb))
46
end
47
48
"""
49
source_terms_lorentz(u, x, t, equations::AbstractIdealGlmMhdMultiIonEquations)
50
51
Source terms due to the Lorentz' force for plasmas with more than one ion species. These source
52
terms are a fundamental, inseparable part of the multi-ion GLM-MHD equations, and vanish for
53
a single-species plasma. In particular, they have to be used for every
54
simulation of [`IdealGlmMhdMultiIonEquations2D`](@ref) and [`IdealGlmMhdMultiIonEquations3D`](@ref).
55
"""
56
function source_terms_lorentz(u, x, t, equations::AbstractIdealGlmMhdMultiIonEquations)
57
@unpack charge_to_mass = equations
58
B1, B2, B3 = magnetic_field(u, equations)
59
v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,
60
equations)
61
62
s = zero(MVector{nvariables(equations), eltype(u)})
63
64
for k in eachcomponent(equations)
65
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
66
rho_inv = 1 / rho
67
v1 = rho_v1 * rho_inv
68
v2 = rho_v2 * rho_inv
69
v3 = rho_v3 * rho_inv
70
v1_diff = v1_plus - v1
71
v2_diff = v2_plus - v2
72
v3_diff = v3_plus - v3
73
r_rho = charge_to_mass[k] * rho
74
s2 = r_rho * (v2_diff * B3 - v3_diff * B2)
75
s3 = r_rho * (v3_diff * B1 - v1_diff * B3)
76
s4 = r_rho * (v1_diff * B2 - v2_diff * B1)
77
s5 = v1 * s2 + v2 * s3 + v3 * s4
78
79
set_component!(s, k, 0, s2, s3, s4, s5, equations)
80
end
81
82
return SVector(s)
83
end
84
85
"""
86
electron_pressure_zero(u, equations::AbstractIdealGlmMhdMultiIonEquations)
87
88
Returns the value of zero for the electron pressure. Needed for consistency with the
89
single-fluid MHD equations in the limit of one ion species.
90
"""
91
function electron_pressure_zero(u, equations::AbstractIdealGlmMhdMultiIonEquations)
92
return zero(u[1])
93
end
94
95
"""
96
v1, v2, v3, vk1, vk2, vk3 = charge_averaged_velocities(u,
97
equations::AbstractIdealGlmMhdMultiIonEquations)
98
99
100
Compute the charge-averaged velocities (`v1`, `v2`, and `v3`) and each ion species' contribution
101
to the charge-averaged velocities (`vk1`, `vk2`, and `vk3`). The output variables `vk1`, `vk2`, and `vk3`
102
are `SVectors` of size `ncomponents(equations)`.
103
104
!!! warning "Experimental implementation"
105
This is an experimental feature and may change in future releases.
106
"""
107
@inline function charge_averaged_velocities(u,
108
equations::AbstractIdealGlmMhdMultiIonEquations)
109
total_electron_charge = zero(real(equations))
110
111
vk1_plus = zero(MVector{ncomponents(equations), eltype(u)})
112
vk2_plus = zero(MVector{ncomponents(equations), eltype(u)})
113
vk3_plus = zero(MVector{ncomponents(equations), eltype(u)})
114
115
for k in eachcomponent(equations)
116
rho, rho_v1, rho_v2, rho_v3, _ = get_component(k, u, equations)
117
118
total_electron_charge += rho * equations.charge_to_mass[k]
119
vk1_plus[k] = rho_v1 * equations.charge_to_mass[k]
120
vk2_plus[k] = rho_v2 * equations.charge_to_mass[k]
121
vk3_plus[k] = rho_v3 * equations.charge_to_mass[k]
122
end
123
vk1_plus ./= total_electron_charge
124
vk2_plus ./= total_electron_charge
125
vk3_plus ./= total_electron_charge
126
v1_plus = sum(vk1_plus)
127
v2_plus = sum(vk2_plus)
128
v3_plus = sum(vk3_plus)
129
130
return v1_plus, v2_plus, v3_plus, SVector(vk1_plus), SVector(vk2_plus),
131
SVector(vk3_plus)
132
end
133
134
"""
135
get_component(k, u, equations::AbstractIdealGlmMhdMultiIonEquations)
136
137
Get the hydrodynamic variables of component (ion species) `k`.
138
139
!!! warning "Experimental implementation"
140
This is an experimental feature and may change in future releases.
141
"""
142
@inline function get_component(k, u, equations::AbstractIdealGlmMhdMultiIonEquations)
143
return SVector(u[3 + (k - 1) * 5 + 1],
144
u[3 + (k - 1) * 5 + 2],
145
u[3 + (k - 1) * 5 + 3],
146
u[3 + (k - 1) * 5 + 4],
147
u[3 + (k - 1) * 5 + 5])
148
end
149
150
"""
151
set_component!(u, k, u1, u2, u3, u4, u5,
152
equations::AbstractIdealGlmMhdMultiIonEquations)
153
154
Set the hydrodynamic variables (`u1` to `u5`) of component (ion species) `k`.
155
156
!!! warning "Experimental implementation"
157
This is an experimental feature and may change in future releases.
158
"""
159
@inline function set_component!(u, k, u1, u2, u3, u4, u5,
160
equations::AbstractIdealGlmMhdMultiIonEquations)
161
u[3 + (k - 1) * 5 + 1] = u1
162
u[3 + (k - 1) * 5 + 2] = u2
163
u[3 + (k - 1) * 5 + 3] = u3
164
u[3 + (k - 1) * 5 + 4] = u4
165
u[3 + (k - 1) * 5 + 5] = u5
166
167
return u
168
end
169
170
# Extract magnetic field from solution vector
171
magnetic_field(u, equations::AbstractIdealGlmMhdMultiIonEquations) = SVector(u[1], u[2],
172
u[3])
173
174
# Extract GLM divergence-cleaning field from solution vector
175
divergence_cleaning_field(u, equations::AbstractIdealGlmMhdMultiIonEquations) = u[end]
176
177
@doc raw"""
178
density(u, equations::AbstractIdealGlmMhdMultiIonEquations)
179
180
Computes the total density ``\rho = \sum_{i=1}^n \rho_i`` from the conserved variables `u`,
181
where ``i`` is the index of **ion** species.
182
"""
183
@inline function density(u, equations::AbstractIdealGlmMhdMultiIonEquations)
184
rho = zero(real(equations))
185
for k in eachcomponent(equations)
186
rho += u[3 + (k - 1) * 5 + 1]
187
end
188
return rho
189
end
190
191
@doc raw"""
192
pressure(u, equations::AbstractIdealGlmMhdMultiIonEquations)
193
194
Computes the pressure of every component ``k`` analogouos to
195
[`pressure(u, equations::IdealGlmMhdEquations1D)`](@ref).
196
"""
197
@inline function pressure(u, equations::AbstractIdealGlmMhdMultiIonEquations)
198
B1, B2, B3, _ = u
199
p = zero(MVector{ncomponents(equations), real(equations)})
200
for k in eachcomponent(equations)
201
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
202
v1 = rho_v1 / rho
203
v2 = rho_v2 / rho
204
v3 = rho_v3 / rho
205
gamma = equations.gammas[k]
206
p[k] = (gamma - 1) * (rho_e_total - 0.5f0 *
207
(rho * (v1^2 + v2^2 + v3^2) + B1^2 + B2^2 + B3^2))
208
end
209
return SVector{ncomponents(equations), real(equations)}(p)
210
end
211
212
#Convert conservative variables to primitive
213
function cons2prim(u, equations::AbstractIdealGlmMhdMultiIonEquations)
214
@unpack gammas = equations
215
B1, B2, B3 = magnetic_field(u, equations)
216
psi = divergence_cleaning_field(u, equations)
217
218
prim = zero(MVector{nvariables(equations), eltype(u)})
219
prim[1] = B1
220
prim[2] = B2
221
prim[3] = B3
222
for k in eachcomponent(equations)
223
rho, rho_v1, rho_v2, rho_v3, rho_e_total = get_component(k, u, equations)
224
rho_inv = 1 / rho
225
v1 = rho_inv * rho_v1
226
v2 = rho_inv * rho_v2
227
v3 = rho_inv * rho_v3
228
229
p = (gammas[k] - 1) * (rho_e_total -
230
0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3
231
+ B1 * B1 + B2 * B2 + B3 * B3
232
+ psi * psi))
233
234
set_component!(prim, k, rho, v1, v2, v3, p, equations)
235
end
236
prim[end] = psi
237
238
return SVector(prim)
239
end
240
241
#Convert conservative variables to entropy variables
242
@inline function cons2entropy(u, equations::AbstractIdealGlmMhdMultiIonEquations)
243
@unpack gammas = equations
244
B1, B2, B3 = magnetic_field(u, equations)
245
psi = divergence_cleaning_field(u, equations)
246
247
prim = cons2prim(u, equations)
248
entropy = zero(MVector{nvariables(equations), eltype(u)})
249
rho_p_plus = zero(real(equations))
250
for k in eachcomponent(equations)
251
rho, v1, v2, v3, p = get_component(k, prim, equations)
252
s = log(p) - gammas[k] * log(rho)
253
rho_p = rho / p
254
w1 = (gammas[k] - s) / (gammas[k] - 1) - 0.5f0 * rho_p * (v1^2 + v2^2 + v3^2)
255
w2 = rho_p * v1
256
w3 = rho_p * v2
257
w4 = rho_p * v3
258
w5 = -rho_p
259
rho_p_plus += rho_p
260
261
set_component!(entropy, k, w1, w2, w3, w4, w5, equations)
262
end
263
264
# Additional non-conservative variables
265
entropy[1] = rho_p_plus * B1
266
entropy[2] = rho_p_plus * B2
267
entropy[3] = rho_p_plus * B3
268
entropy[end] = rho_p_plus * psi
269
270
return SVector(entropy)
271
end
272
273
# Convert primitive to conservative variables
274
@inline function prim2cons(prim, equations::AbstractIdealGlmMhdMultiIonEquations)
275
@unpack gammas = equations
276
B1, B2, B3 = magnetic_field(prim, equations)
277
psi = divergence_cleaning_field(prim, equations)
278
279
cons = zero(MVector{nvariables(equations), eltype(prim)})
280
cons[1] = B1
281
cons[2] = B2
282
cons[3] = B3
283
for k in eachcomponent(equations)
284
rho, v1, v2, v3, p = get_component(k, prim, equations)
285
rho_v1 = rho * v1
286
rho_v2 = rho * v2
287
rho_v3 = rho * v3
288
289
rho_e_total = p / (gammas[k] - 1) +
290
0.5f0 * (rho_v1 * v1 + rho_v2 * v2 + rho_v3 * v3) +
291
0.5f0 * (B1^2 + B2^2 + B3^2) +
292
0.5f0 * psi^2
293
294
set_component!(cons, k, rho, rho_v1, rho_v2, rho_v3, rho_e_total, equations)
295
end
296
cons[end] = psi
297
298
return SVector(cons)
299
end
300
301
# Specialization of [`DissipationLaxFriedrichsEntropyVariables`](@ref) for the multi-ion GLM-MHD equations
302
# For details on the multi-ion entropy Jacobian ``H`` see
303
# - A. Rueda-Ramírez, A. Sikstel, G. Gassner, An Entropy-Stable Discontinuous Galerkin Discretization
304
# of the Ideal Multi-Ion Magnetohydrodynamics System (2024). Journal of Computational Physics.
305
# [DOI: 10.1016/j.jcp.2024.113655](https://doi.org/10.1016/j.jcp.2024.113655).
306
# Since the entropy Jacobian is a sparse matrix, we do not construct it but directly compute the
307
# action of its product with the jump in the entropy variables.
308
#
309
# ATTENTION: the variable order for AbstractIdealGlmMhdMultiIonEquations is different than in the reference above.
310
# The first three entries of the state vector `u[1:3]` are the magnetic field components. After that, we have chunks
311
# of 5 entries for the hydrodynamic quantities of each ion species. Finally, the last entry `u[end]` is the divergence
312
# cleaning field.
313
@inline function (dissipation::DissipationLaxFriedrichsEntropyVariables)(u_ll, u_rr,
314
orientation_or_normal_direction,
315
equations::AbstractIdealGlmMhdMultiIonEquations)
316
@unpack gammas = equations
317
λ = dissipation.max_abs_speed(u_ll, u_rr, orientation_or_normal_direction,
318
equations)
319
320
w_ll = cons2entropy(u_ll, equations)
321
w_rr = cons2entropy(u_rr, equations)
322
prim_ll = cons2prim(u_ll, equations)
323
prim_rr = cons2prim(u_rr, equations)
324
B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations)
325
B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations)
326
psi_ll = divergence_cleaning_field(u_ll, equations)
327
psi_rr = divergence_cleaning_field(u_rr, equations)
328
329
# Some global averages
330
B1_avg = 0.5f0 * (B1_ll + B1_rr)
331
B2_avg = 0.5f0 * (B2_ll + B2_rr)
332
B3_avg = 0.5f0 * (B3_ll + B3_rr)
333
psi_avg = 0.5f0 * (psi_ll + psi_rr)
334
335
dissipation = zero(MVector{nvariables(equations), eltype(u_ll)})
336
337
beta_plus_ll = 0
338
beta_plus_rr = 0
339
340
# Compute the dissipation for the hydrodynamic quantities of each ion species `k`
341
#################################################################################
342
343
# The for loop below fills the entries of `dissipation` that depend on the entries of the diagonal
344
# blocks ``A_k`` of the entropy Jacobian ``H`` in the given reference (see equations (80)-(82)),
345
# but the terms that depend on the magnetic field ``B`` and divergence cleaning field ``psi`` are
346
# excluded here and considered below. In other words, these are the dissipation values that depend
347
# on the entries of the entropy Jacobian that are marked in blue in Figure 1 of the reference given above.
348
for k in eachcomponent(equations)
349
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = get_component(k, prim_ll, equations)
350
rho_rr, v1_rr, v2_rr, v3_rr, p_rr = get_component(k, prim_rr, equations)
351
352
w1_ll, w2_ll, w3_ll, w4_ll, w5_ll = get_component(k, w_ll, equations)
353
w1_rr, w2_rr, w3_rr, w4_rr, w5_rr = get_component(k, w_rr, equations)
354
355
# Auxiliary variables
356
beta_ll = 0.5f0 * rho_ll / p_ll
357
beta_rr = 0.5f0 * rho_rr / p_rr
358
vel_norm_ll = v1_ll^2 + v2_ll^2 + v3_ll^2
359
vel_norm_rr = v1_rr^2 + v2_rr^2 + v3_rr^2
360
361
# Mean variables
362
rho_ln = ln_mean(rho_ll, rho_rr)
363
beta_ln = ln_mean(beta_ll, beta_rr)
364
rho_avg = 0.5f0 * (rho_ll + rho_rr)
365
v1_avg = 0.5f0 * (v1_ll + v1_rr)
366
v2_avg = 0.5f0 * (v2_ll + v2_rr)
367
v3_avg = 0.5f0 * (v3_ll + v3_rr)
368
beta_avg = 0.5f0 * (beta_ll + beta_rr)
369
tau = 1 / (beta_ll + beta_rr)
370
p_mean = 0.5f0 * rho_avg / beta_avg
371
p_star = 0.5f0 * rho_ln / beta_ln
372
vel_norm_avg = 0.5f0 * (vel_norm_ll + vel_norm_rr)
373
vel_avg_norm = v1_avg^2 + v2_avg^2 + v3_avg^2
374
E_bar = p_star / (gammas[k] - 1) +
375
0.5f0 * rho_ln * (2 * vel_avg_norm - vel_norm_avg)
376
377
h11 = rho_ln
378
h12 = rho_ln * v1_avg
379
h13 = rho_ln * v2_avg
380
h14 = rho_ln * v3_avg
381
h15 = E_bar
382
d1 = -0.5f0 * λ *
383
(h11 * (w1_rr - w1_ll) +
384
h12 * (w2_rr - w2_ll) +
385
h13 * (w3_rr - w3_ll) +
386
h14 * (w4_rr - w4_ll) +
387
h15 * (w5_rr - w5_ll))
388
389
h21 = h12
390
h22 = rho_ln * v1_avg^2 + p_mean
391
h23 = h21 * v2_avg
392
h24 = h21 * v3_avg
393
h25 = (E_bar + p_mean) * v1_avg
394
d2 = -0.5f0 * λ *
395
(h21 * (w1_rr - w1_ll) +
396
h22 * (w2_rr - w2_ll) +
397
h23 * (w3_rr - w3_ll) +
398
h24 * (w4_rr - w4_ll) +
399
h25 * (w5_rr - w5_ll))
400
401
h31 = h13
402
h32 = h23
403
h33 = rho_ln * v2_avg^2 + p_mean
404
h34 = h31 * v3_avg
405
h35 = (E_bar + p_mean) * v2_avg
406
d3 = -0.5f0 * λ *
407
(h31 * (w1_rr - w1_ll) +
408
h32 * (w2_rr - w2_ll) +
409
h33 * (w3_rr - w3_ll) +
410
h34 * (w4_rr - w4_ll) +
411
h35 * (w5_rr - w5_ll))
412
413
h41 = h14
414
h42 = h24
415
h43 = h34
416
h44 = rho_ln * v3_avg^2 + p_mean
417
h45 = (E_bar + p_mean) * v3_avg
418
d4 = -0.5f0 * λ *
419
(h41 * (w1_rr - w1_ll) +
420
h42 * (w2_rr - w2_ll) +
421
h43 * (w3_rr - w3_ll) +
422
h44 * (w4_rr - w4_ll) +
423
h45 * (w5_rr - w5_ll))
424
425
h51 = h15
426
h52 = h25
427
h53 = h35
428
h54 = h45
429
h55 = ((p_star^2 / (gammas[k] - 1) + E_bar * E_bar) / rho_ln
430
+
431
vel_avg_norm * p_mean)
432
d5 = -0.5f0 * λ *
433
(h51 * (w1_rr - w1_ll) +
434
h52 * (w2_rr - w2_ll) +
435
h53 * (w3_rr - w3_ll) +
436
h54 * (w4_rr - w4_ll) +
437
h55 * (w5_rr - w5_ll))
438
439
beta_plus_ll += beta_ll
440
beta_plus_rr += beta_rr
441
442
set_component!(dissipation, k, d1, d2, d3, d4, d5, equations)
443
end
444
445
# Compute the dissipation related to the magnetic and divergence-cleaning fields
446
################################################################################
447
448
h_B_psi = 1 / (beta_plus_ll + beta_plus_rr)
449
450
# Dissipation for the magnetic field components due to the diagonal entries of the
451
# dissipation matrix ``H``. These are the dissipation values that depend on the diagonal
452
# entries of the entropy Jacobian that are marked in cyan in Figure 1 of the reference given above.
453
dissipation[1] = -0.5f0 * λ * h_B_psi * (w_rr[1] - w_ll[1])
454
dissipation[2] = -0.5f0 * λ * h_B_psi * (w_rr[2] - w_ll[2])
455
dissipation[3] = -0.5f0 * λ * h_B_psi * (w_rr[3] - w_ll[3])
456
457
# Dissipation for the divergence-cleaning field due to the diagonal entry of the
458
# dissipation matrix ``H``. This dissipation value depends on the single diagonal
459
# entry of the entropy Jacobian that is marked in red in Figure 1 of the reference given above.
460
dissipation[end] = -0.5f0 * λ * h_B_psi * (w_rr[end] - w_ll[end])
461
462
# Dissipation due to the off-diagonal blocks (``B_{off}``) of the dissipation matrix ``H`` and to the entries
463
# of the block ``A`` that depend on the magnetic field ``B`` and the divergence cleaning field ``psi``.
464
# See equations (80)-(82) of the given reference.
465
for k in eachcomponent(equations)
466
_, _, _, _, w5_ll = get_component(k, w_ll, equations)
467
_, _, _, _, w5_rr = get_component(k, w_rr, equations)
468
469
# Dissipation for the magnetic field components and divergence cleaning field due to the off-diagonal
470
# entries of the dissipation matrix ``H`` (block ``B^T`` in equation (80) and Figure 1 of the reference
471
# given above).
472
dissipation[1] -= 0.5f0 * λ * h_B_psi * B1_avg * (w5_rr - w5_ll)
473
dissipation[2] -= 0.5f0 * λ * h_B_psi * B2_avg * (w5_rr - w5_ll)
474
dissipation[3] -= 0.5f0 * λ * h_B_psi * B3_avg * (w5_rr - w5_ll)
475
dissipation[end] -= 0.5f0 * λ * h_B_psi * psi_avg * (w5_rr - w5_ll)
476
477
# Dissipation for the energy equation of species `k` depending on `w_1`, `w_2`, `w_3` and `w_end`. These are the
478
# values of the dissipation that depend on the off-diagonal block ``B`` of the dissipation matrix ``H`` (see equation (80)
479
# and Figure 1 of the reference given above.
480
ind_E = 3 + 5 * k # simplified version of 3 + (k - 1) * 5 + 5
481
dissipation[ind_E] -= 0.5f0 * λ * h_B_psi * B1_avg * (w_rr[1] - w_ll[1])
482
dissipation[ind_E] -= 0.5f0 * λ * h_B_psi * B2_avg * (w_rr[2] - w_ll[2])
483
dissipation[ind_E] -= 0.5f0 * λ * h_B_psi * B3_avg * (w_rr[3] - w_ll[3])
484
dissipation[ind_E] -= 0.5f0 * λ * h_B_psi * psi_avg * (w_rr[end] - w_ll[end])
485
486
# Dissipation for the energy equation of all ion species depending on `w_5`. These are the values of the dissipation
487
# vector that depend on the magnetic and divergence-cleaning field terms of the entries marked with a red cross in
488
# Figure 1 of the reference given above.
489
for kk in eachcomponent(equations)
490
ind_E = 3 + 5 * kk # simplified version of 3 + (kk - 1) * 5 + 5
491
dissipation[ind_E] -= 0.5f0 * λ *
492
(h_B_psi *
493
(B1_avg^2 + B2_avg^2 + B3_avg^2 + psi_avg^2)) *
494
(w5_rr - w5_ll)
495
end
496
end
497
498
return dissipation
499
end
500
501
@doc raw"""
502
source_terms_collision_ion_ion(u, x, t,
503
equations::AbstractIdealGlmMhdMultiIonEquations)
504
505
Compute the ion-ion collision source terms for the momentum and energy equations of each ion species as
506
```math
507
\begin{aligned}
508
\vec{s}_{\rho_k \vec{v}_k} =& \rho_k\sum_{l}\bar{\nu}_{kl}(\vec{v}_{l} - \vec{v}_k),\\
509
s_{E_k} =&
510
3 \sum_{l} \left(
511
\bar{\nu}_{kl} \frac{\rho_k M_1}{M_{l} + M_k} R_1 (T_{l} - T_k)
512
\right) +
513
\sum_{l} \left(
514
\bar{\nu}_{kl} \rho_k \frac{M_{l}}{M_{l} + M_k} \|\vec{v}_{l} - \vec{v}_k\|^2
515
\right)
516
+
517
\vec{v}_k \cdot \vec{s}_{\rho_k \vec{v}_k},
518
\end{aligned}
519
```
520
where ``M_k`` is the molar mass of ion species `k` provided in `equations.molar_masses`,
521
``R_k`` is the specific gas constant of ion species `k` provided in `equations.gas_constants`, and
522
``\bar{\nu}_{kl}`` is the effective collision frequency of species `k` with species `l`, which is computed as
523
```math
524
\begin{aligned}
525
\bar{\nu}_{kl} = \bar{\nu}^1_{kl} \tilde{B}_{kl} \frac{\rho_{l}}{T_{k l}^{3/2}},
526
\end{aligned}
527
```
528
with the so-called reduced temperature ``T_{k l}`` and the ion-ion collision constants ``\tilde{B}_{kl}`` provided
529
in `equations.ion_electron_collision_constants` (see [`IdealGlmMhdMultiIonEquations2D`](@ref)).
530
531
The additional coefficient ``\bar{\nu}^1_{kl}`` is a non-dimensional drift correction factor proposed by Rambo and Denavit.
532
533
References:
534
- P. Rambo, J. Denavit, Interpenetration and ion separation in colliding plasmas, Physics of Plasmas 1 (1994) 4050–4060.
535
[DOI: 10.1063/1.870875](https://doi.org/10.1063/1.870875).
536
- Schunk, R. W., Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.
537
Cambridge university press. [DOI: 10.1017/CBO9780511635342](https://doi.org/10.1017/CBO9780511635342).
538
"""
539
function source_terms_collision_ion_ion(u, x, t,
540
equations::AbstractIdealGlmMhdMultiIonEquations)
541
s = zero(MVector{nvariables(equations), eltype(u)})
542
@unpack gas_constants, molar_masses, ion_ion_collision_constants = equations
543
544
prim = cons2prim(u, equations)
545
546
for k in eachcomponent(equations)
547
rho_k, v1_k, v2_k, v3_k, p_k = get_component(k, prim, equations)
548
T_k = p_k / (rho_k * gas_constants[k])
549
550
S_q1 = zero(eltype(u))
551
S_q2 = zero(eltype(u))
552
S_q3 = zero(eltype(u))
553
S_E = zero(eltype(u))
554
for l in eachcomponent(equations)
555
# Do not compute collisions of an ion species with itself
556
k == l && continue
557
558
rho_l, v1_l, v2_l, v3_l, p_l = get_component(l, prim, equations)
559
T_l = p_l / (rho_l * gas_constants[l])
560
561
# Reduced temperature
562
T_kl = (molar_masses[l] * T_k + molar_masses[k] * T_l) /
563
(molar_masses[k] + molar_masses[l])
564
565
delta_v2 = (v1_l - v1_k)^2 + (v2_l - v2_k)^2 + (v3_l - v3_k)^2
566
567
# Compute collision frequency without drifting correction
568
v_kl = ion_ion_collision_constants[k, l] * rho_l / T_kl^(3 / 2)
569
570
# Correct the collision frequency with the drifting effect
571
z2 = delta_v2 / (p_l / rho_l + p_k / rho_k)
572
v_kl /= (1 + (2 / (9 * pi))^(1 / 3) * z2)^(3 / 2)
573
574
S_q1 += rho_k * v_kl * (v1_l - v1_k)
575
S_q2 += rho_k * v_kl * (v2_l - v2_k)
576
S_q3 += rho_k * v_kl * (v3_l - v3_k)
577
578
S_E += (3 * molar_masses[1] * gas_constants[1] * (T_l - T_k)
579
+
580
molar_masses[l] * delta_v2) * v_kl * rho_k /
581
(molar_masses[k] + molar_masses[l])
582
end
583
584
S_E += (v1_k * S_q1 + v2_k * S_q2 + v3_k * S_q3)
585
586
set_component!(s, k, 0, S_q1, S_q2, S_q3, S_E, equations)
587
end
588
return SVector{nvariables(equations), real(equations)}(s)
589
end
590
591
@doc raw"""
592
source_terms_collision_ion_electron(u, x, t,
593
equations::AbstractIdealGlmMhdMultiIonEquations)
594
595
Compute the ion-electron collision source terms for the momentum and energy equations of each ion species. We assume ``v_e = v^+``
596
(no effect of currents on the electron velocity).
597
598
The collision sources read as
599
```math
600
\begin{aligned}
601
\vec{s}_{\rho_k \vec{v}_k} =& \rho_k \bar{\nu}_{ke} (\vec{v}_{e} - \vec{v}_k),
602
\\
603
s_{E_k} =&
604
3 \left(
605
\bar{\nu}_{ke} \frac{\rho_k M_{1}}{M_k} R_1 (T_{e} - T_k)
606
\right)
607
+
608
\vec{v}_k \cdot \vec{s}_{\rho_k \vec{v}_k},
609
\end{aligned}
610
```
611
where ``T_e`` is the electron temperature computed with the function `equations.electron_temperature`,
612
``M_k`` is the molar mass of ion species `k` provided in `equations.molar_masses`,
613
``R_k`` is the specific gas constant of ion species `k` provided in `equations.gas_constants`, and
614
``\bar{\nu}_{ke}`` is the collision frequency of species `k` with the electrons, which is computed as
615
```math
616
\begin{aligned}
617
\bar{\nu}_{ke} = \tilde{B}_{ke} \frac{e n_e}{T_e^{3/2}},
618
\end{aligned}
619
```
620
with the total electron charge ``e n_e`` (computed assuming quasi-neutrality), and the
621
ion-electron collision coefficient ``\tilde{B}_{ke}`` provided in `equations.ion_electron_collision_constants`,
622
which is scaled with the elementary charge (see [`IdealGlmMhdMultiIonEquations2D`](@ref)).
623
624
References:
625
- P. Rambo, J. Denavit, Interpenetration and ion separation in colliding plasmas, Physics of Plasmas 1 (1994) 4050–4060.
626
[DOI: 10.1063/1.870875](https://doi.org/10.1063/1.870875).
627
- Schunk, R. W., Nagy, A. F. (2000). Ionospheres: Physics, plasma physics, and chemistry.
628
Cambridge university press. [DOI: 10.1017/CBO9780511635342](https://doi.org/10.1017/CBO9780511635342).
629
"""
630
function source_terms_collision_ion_electron(u, x, t,
631
equations::AbstractIdealGlmMhdMultiIonEquations)
632
s = zero(MVector{nvariables(equations), eltype(u)})
633
@unpack gas_constants, molar_masses, ion_electron_collision_constants, electron_temperature = equations
634
635
prim = cons2prim(u, equations)
636
T_e = electron_temperature(u, equations)
637
T_e_power32 = T_e^(3 / 2)
638
639
v1_plus, v2_plus, v3_plus, vk1_plus, vk2_plus, vk3_plus = charge_averaged_velocities(u,
640
equations)
641
642
# Compute total electron charge
643
total_electron_charge = zero(real(equations))
644
for k in eachcomponent(equations)
645
rho, _ = get_component(k, u, equations)
646
total_electron_charge += rho * equations.charge_to_mass[k]
647
end
648
649
for k in eachcomponent(equations)
650
rho_k, v1_k, v2_k, v3_k, p_k = get_component(k, prim, equations)
651
T_k = p_k / (rho_k * gas_constants[k])
652
653
# Compute effective collision frequency
654
v_ke = ion_electron_collision_constants[k] * total_electron_charge / T_e_power32
655
656
S_q1 = rho_k * v_ke * (v1_plus - v1_k)
657
S_q2 = rho_k * v_ke * (v2_plus - v2_k)
658
S_q3 = rho_k * v_ke * (v3_plus - v3_k)
659
660
S_E = 3 * molar_masses[1] * gas_constants[1] * (T_e - T_k) * v_ke * rho_k /
661
molar_masses[k]
662
663
S_E += (v1_k * S_q1 + v2_k * S_q2 + v3_k * S_q3)
664
665
set_component!(s, k, 0, S_q1, S_q2, S_q3, S_E, equations)
666
end
667
return SVector{nvariables(equations), real(equations)}(s)
668
end
669
end
670
671