Skip to content

Commit 19cdbb7

Browse files
committed
Merge branch 'turbulence-cases' into paper-inputs
2 parents 646cc90 + 7bff699 commit 19cdbb7

15 files changed

+269
-22
lines changed

include/diamagnetic_drift.hxx

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct DiamagneticDrift : public Component {
2222
private:
2323
Vector2D Curlb_B;
2424
bool bndry_flux;
25+
Field2D diamag_form;
2526
};
2627

2728
namespace {

include/evolve_density.hxx

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct EvolveDensity : public Component {
2727
/// - N<name> e.g. "Ne", "Nd+"
2828
/// - source Source of particles [/m^3/s]
2929
/// NOTE: This overrides mesh input N<name>_src
30+
/// - source_only_in_core Zero the source outside the closed field-line region?
31+
/// - neumann_boundary_average_z Apply Neumann boundaries with Z average?
3032
///
3133
EvolveDensity(std::string name, Options &options, Solver *solver);
3234

@@ -65,6 +67,7 @@ private:
6567

6668
bool bndry_flux; ///< Allow flows through boundaries?
6769
bool poloidal_flows; ///< Include ExB flow in Y direction?
70+
bool neumann_boundary_average_z; ///< Apply neumann boundary with Z average?
6871

6972
BoutReal density_floor;
7073
bool low_n_diffuse; ///< Parallel diffusion at low density

include/evolve_momentum.hxx

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ private:
4848
BoutReal hyper_z; ///< Hyper-diffusion
4949

5050
bool diagnose; ///< Output additional diagnostics?
51+
bool fix_momentum_boundary_flux; ///< Fix momentum flux to boundary condition?
5152
};
5253

5354
namespace {

include/evolve_pressure.hxx

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ struct EvolvePressure : public Component {
3333
/// - P<name> e.g. "Pe", "Pd+"
3434
/// - source Source of pressure [Pa / s].
3535
/// NOTE: This overrides mesh input P<name>_src
36+
/// - source_only_in_core Zero the source outside the closed field-line region?
37+
/// - neumann_boundary_average_z Apply Neumann boundaries with Z average?
3638
///
3739
EvolvePressure(std::string name, Options& options, Solver* solver);
3840

@@ -74,6 +76,7 @@ private:
7476
Field3D T, N; ///< Temperature, density
7577

7678
bool bndry_flux;
79+
bool neumann_boundary_average_z; ///< Apply neumann boundary with Z average?
7780
bool poloidal_flows;
7881
bool thermal_conduction; ///< Include thermal conduction?
7982
BoutReal kappa_coefficient; ///< Leading numerical coefficient in parallel heat flux calculation

include/sheath_boundary.hxx

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct SheathBoundary : public Component {
2525
/// - lower_y Boundary on lower y?
2626
/// - upper_y Boundary on upper y?
2727
/// - wall_potential Voltage of the wall [Volts]
28+
/// - floor_potential Apply floor to sheath potential?
2829
/// - secondary_electron_coef Effective secondary electron emission coefficient
2930
/// - sin_alpha Sine of the angle between magnetic field line and wall surface (0 to 1)
3031
/// - always_set_phi Always set phi field? Default is to only modify if already set
@@ -84,6 +85,8 @@ private:
8485
bool always_set_phi; ///< Set phi field?
8586

8687
Field3D wall_potential; ///< Voltage at the wall. Normalised units.
88+
89+
bool floor_potential; ///< Apply floor to sheath potential?
8790
};
8891

8992
namespace {

include/sound_speed.hxx

+24
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "component.hxx"
66

7+
#include <bout/constants.hxx>
8+
79
/// Calculate the system sound speed
810
///
911
/// This uses the sum of all species pressures and mass densities
@@ -14,6 +16,25 @@ struct SoundSpeed : public Component {
1416
electron_dynamics = options["electron_dynamics"]
1517
.doc("Include electron sound speed?")
1618
.withDefault<bool>(true);
19+
20+
alfven_wave = options["alfven_wave"]
21+
.doc("Include Alfven wave speed?")
22+
.withDefault<bool>(false);
23+
if (alfven_wave) {
24+
// Calculate normalisation factor
25+
const auto& units = alloptions["units"];
26+
const auto Bnorm = get<BoutReal>(units["Tesla"]);
27+
const auto Nnorm = get<BoutReal>(units["inv_meters_cubed"]);
28+
const auto Cs0 = get<BoutReal>(units["meters"]) / get<BoutReal>(units["seconds"]);
29+
beta_norm = Bnorm / sqrt(SI::mu0 * Nnorm * SI::Mp) / Cs0;
30+
}
31+
32+
temperature_floor = options["temperature_floor"]
33+
.doc("Minimum temperature when calculating sound speeds [eV]")
34+
.withDefault(0.0);
35+
if (temperature_floor > 0.0) {
36+
temperature_floor /= get<BoutReal>(alloptions["units"]["eV"]);
37+
}
1738
}
1839

1940
/// This sets in the state
@@ -31,6 +52,9 @@ struct SoundSpeed : public Component {
3152

3253
private:
3354
bool electron_dynamics; ///< Include electron sound speed?
55+
bool alfven_wave; ///< Include Alfven wave speed?
56+
BoutReal beta_norm{0.0}; ///< Normalisation factor for Alfven speed
57+
BoutReal temperature_floor; ///< Minimum temperature when calculating speed
3458
};
3559

3660
namespace {

include/vorticity.hxx

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ private:
9191

9292
bool vort_dissipation; ///< Parallel dissipation of vorticity
9393
bool phi_dissipation; ///< Parallel dissipation of potential
94+
bool phi_sheath_dissipation; ///< Dissipation at the sheath if phi < 0
9495

9596
bool phi_boundary_relax; ///< Relax boundary to zero-gradient
9697
BoutReal phi_boundary_timescale; ///< Relaxation timescale [normalised]
@@ -102,6 +103,7 @@ private:
102103
Field2D Bsq; // SQ(coord->Bxy)
103104
Vector2D Curlb_B; // Curvature vector Curl(b/B)
104105
BoutReal hyper_z; ///< Hyper-viscosity in Z
106+
Field2D viscosity; ///< Kinematic viscosity
105107

106108
// Diagnostic outputs
107109
Field3D DivJdia, DivJcol; // Divergence of diamagnetic and collisional current

include/zero_current.hxx

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ struct ZeroCurrent : public Component {
3737
///
3838
void transform(Options &state) override;
3939

40+
void finally(const Options &state) override {
41+
// Get the velocity with boundary condition applied.
42+
// This is for output only
43+
velocity = get<Field3D>(state["species"][name]["velocity"]);
44+
}
45+
4046
void outputVars(Options &state) override;
4147
private:
4248
std::string name; ///< Name of this species

src/diamagnetic_drift.cxx

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <bout/fv_ops.hxx>
2+
#include <vecops.hxx>
23

34
#include "../include/diamagnetic_drift.hxx"
45

@@ -13,6 +14,10 @@ DiamagneticDrift::DiamagneticDrift(std::string name, Options& alloptions,
1314
bndry_flux =
1415
options["bndry_flux"].doc("Allow fluxes through boundary?").withDefault<bool>(true);
1516

17+
diamag_form = options["diamag_form"]
18+
.doc("Form of diamagnetic drift: 0 = gradient; 1 = divergence")
19+
.withDefault(Field2D(1.0));
20+
1621
// Read curvature vector
1722
Curlb_B.covariant = false; // Contravariant
1823
if (mesh->get(Curlb_B, "bxcv")) {
@@ -41,6 +46,15 @@ DiamagneticDrift::DiamagneticDrift(std::string name, Options& alloptions,
4146
Curlb_B.z *= SQ(Lnorm);
4247

4348
Curlb_B *= 2. / mesh->getCoordinates()->Bxy;
49+
50+
// Set drift to zero through sheath boundaries.
51+
// Flux through those cell faces should be set by sheath.
52+
for (RangeIterator r = mesh->iterateBndryLowerY(); !r.isDone(); r++) {
53+
Curlb_B.y(r.ind, mesh->ystart - 1) = -Curlb_B.y(r.ind, mesh->ystart);
54+
}
55+
for (RangeIterator r = mesh->iterateBndryUpperY(); !r.isDone(); r++) {
56+
Curlb_B.y(r.ind, mesh->yend + 1) = -Curlb_B.y(r.ind, mesh->yend);
57+
}
4458
}
4559

4660
void DiamagneticDrift::transform(Options& state) {
@@ -62,17 +76,28 @@ void DiamagneticDrift::transform(Options& state) {
6276

6377
if (IS_SET(species["density"])) {
6478
auto N = GET_VALUE(Field3D, species["density"]);
65-
subtract(species["density_source"], FV::Div_f_v(N, vD, bndry_flux));
79+
80+
// Divergence form: Div(n v_D)
81+
Field3D div_form = FV::Div_f_v(N, vD, bndry_flux);
82+
// Gradient form: Curlb_B dot Grad(N T / q)
83+
Field3D grad_form = Curlb_B * Grad(N * T / q);
84+
85+
subtract(species["density_source"], diamag_form * div_form + (1. - diamag_form) * grad_form);
6686
}
6787

6888
if (IS_SET(species["pressure"])) {
6989
auto P = get<Field3D>(species["pressure"]);
70-
subtract(species["energy_source"], (5. / 2) * FV::Div_f_v(P, vD, bndry_flux));
90+
91+
Field3D div_form = FV::Div_f_v(P, vD, bndry_flux);
92+
Field3D grad_form = Curlb_B * Grad(P * T / q);
93+
subtract(species["energy_source"], (5. / 2) * (diamag_form * div_form + (1. - diamag_form) * grad_form));
7194
}
7295

7396
if (IS_SET(species["momentum"])) {
7497
auto NV = get<Field3D>(species["momentum"]);
75-
subtract(species["momentum_source"], FV::Div_f_v(NV, vD, bndry_flux));
98+
Field3D div_form = FV::Div_f_v(NV, vD, bndry_flux);
99+
Field3D grad_form = Curlb_B * Grad(NV * T / q);
100+
subtract(species["momentum_source"], diamag_form * div_form + (1. - diamag_form) * grad_form);
76101
}
77102
}
78103
}

src/evolve_density.cxx

+53
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,25 @@ EvolveDensity::EvolveDensity(std::string name, Options& alloptions, Solver* solv
8181
.doc("Source term in ddt(N" + name + std::string("). Units [m^-3/s]"))
8282
.withDefault(source)
8383
/ (Nnorm * Omega_ci);
84+
85+
if (alloptions[std::string("N") + name]["source_only_in_core"]
86+
.doc("Zero the source outside the closed field-line region?")
87+
.withDefault<bool>(false)) {
88+
for (int x = mesh->xstart; x <= mesh->xend; x++) {
89+
if (!mesh->periodicY(x)) {
90+
// Not periodic, so not in core
91+
for (int y = mesh->ystart; y <= mesh->yend; y++) {
92+
for (int z = mesh->zstart; z <= mesh->zend; z++) {
93+
source(x, y, z) = 0.0;
94+
}
95+
}
96+
}
97+
}
98+
}
99+
100+
neumann_boundary_average_z = alloptions[std::string("N") + name]["neumann_boundary_average_z"]
101+
.doc("Apply neumann boundary with Z average?")
102+
.withDefault<bool>(false);
84103
}
85104

86105
void EvolveDensity::transform(Options& state) {
@@ -93,6 +112,40 @@ void EvolveDensity::transform(Options& state) {
93112

94113
mesh->communicate(N);
95114

115+
if (neumann_boundary_average_z) {
116+
// Take Z (usually toroidal) average and apply as X (radial) boundary condition
117+
if (mesh->firstX()) {
118+
for (int j = mesh->ystart; j <= mesh->yend; j++) {
119+
BoutReal Navg = 0.0; // Average N in Z
120+
for (int k = 0; k < mesh->LocalNz; k++) {
121+
Navg += N(mesh->xstart, j, k);
122+
}
123+
Navg /= mesh->LocalNz;
124+
125+
// Apply boundary condition
126+
for (int k = 0; k < mesh->LocalNz; k++) {
127+
N(mesh->xstart - 1, j, k) = 2. * Navg - N(mesh->xstart, j, k);
128+
N(mesh->xstart - 2, j, k) = N(mesh->xstart - 1, j, k);
129+
}
130+
}
131+
}
132+
133+
if (mesh->lastX()) {
134+
for (int j = mesh->ystart; j <= mesh->yend; j++) {
135+
BoutReal Navg = 0.0; // Average N in Z
136+
for (int k = 0; k < mesh->LocalNz; k++) {
137+
Navg += N(mesh->xend, j, k);
138+
}
139+
Navg /= mesh->LocalNz;
140+
141+
for (int k = 0; k < mesh->LocalNz; k++) {
142+
N(mesh->xend + 1, j, k) = 2. * Navg - N(mesh->xend, j, k);
143+
N(mesh->xend + 2, j, k) = N(mesh->xend + 1, j, k);
144+
}
145+
}
146+
}
147+
}
148+
96149
auto& species = state["species"][name];
97150
set(species["density"], floor(N, 0.0)); // Density in state always >= 0
98151
set(species["AA"], AA); // Atomic mass

src/evolve_momentum.cxx

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ EvolveMomentum::EvolveMomentum(std::string name, Options &alloptions, Solver *so
4343
diagnose = options["diagnose"]
4444
.doc("Output additional diagnostics?")
4545
.withDefault<bool>(false);
46+
47+
fix_momentum_boundary_flux = options["fix_momentum_boundary_flux"]
48+
.doc("Fix Y boundary momentum flux to boundary midpoint value?")
49+
.withDefault<bool>(false);
4650
}
4751

4852
void EvolveMomentum::transform(Options &state) {
@@ -115,7 +119,7 @@ void EvolveMomentum::finally(const Options &state) {
115119

116120
// Note: Density floor should be consistent with calculation of V
117121
// otherwise energy conservation is affected
118-
ddt(NV) -= AA * FV::Div_par_fvv(Nlim, V, fastest_wave);
122+
ddt(NV) -= AA * FV::Div_par_fvv(Nlim, V, fastest_wave, fix_momentum_boundary_flux);
119123

120124
// Parallel pressure gradient
121125
if (species.isSet("pressure")) {

src/evolve_pressure.cxx

+52
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,25 @@ EvolvePressure::EvolvePressure(std::string name, Options& alloptions, Solver* so
9090
+ std::string("). Units [N/m^2/s]"))
9191
.withDefault(source)
9292
/ (SI::qe * Nnorm * Tnorm * Omega_ci);
93+
94+
if (alloptions[std::string("P") + name]["source_only_in_core"]
95+
.doc("Zero the source outside the closed field-line region?")
96+
.withDefault<bool>(false)) {
97+
for (int x = mesh->xstart; x <= mesh->xend; x++) {
98+
if (!mesh->periodicY(x)) {
99+
// Not periodic, so not in core
100+
for (int y = mesh->ystart; y <= mesh->yend; y++) {
101+
for (int z = mesh->zstart; z <= mesh->zend; z++) {
102+
source(x, y, z) = 0.0;
103+
}
104+
}
105+
}
106+
}
107+
}
108+
109+
neumann_boundary_average_z = alloptions[std::string("P") + name]["neumann_boundary_average_z"]
110+
.doc("Apply neumann boundary with Z average?")
111+
.withDefault<bool>(false);
93112
}
94113

95114
void EvolvePressure::transform(Options& state) {
@@ -102,6 +121,39 @@ void EvolvePressure::transform(Options& state) {
102121

103122
mesh->communicate(P);
104123

124+
if (neumann_boundary_average_z) {
125+
// Take Z (usually toroidal) average and apply as X (radial) boundary condition
126+
if (mesh->firstX()) {
127+
for (int j = mesh->ystart; j <= mesh->yend; j++) {
128+
BoutReal Pavg = 0.0; // Average P in Z
129+
for (int k = 0; k < mesh->LocalNz; k++) {
130+
Pavg += P(mesh->xstart, j, k);
131+
}
132+
Pavg /= mesh->LocalNz;
133+
134+
// Apply boundary condition
135+
for (int k = 0; k < mesh->LocalNz; k++) {
136+
P(mesh->xstart - 1, j, k) = 2. * Pavg - P(mesh->xstart, j, k);
137+
P(mesh->xstart - 2, j, k) = P(mesh->xstart - 1, j, k);
138+
}
139+
}
140+
}
141+
142+
if (mesh->lastX()) {
143+
for (int j = mesh->ystart; j <= mesh->yend; j++) {
144+
BoutReal Pavg = 0.0; // Average P in Z
145+
for (int k = 0; k < mesh->LocalNz; k++) {
146+
Pavg += P(mesh->xend, j, k);
147+
}
148+
Pavg /= mesh->LocalNz;
149+
150+
for (int k = 0; k < mesh->LocalNz; k++) {
151+
P(mesh->xend + 1, j, k) = 2. * Pavg - P(mesh->xend, j, k);
152+
P(mesh->xend + 2, j, k) = P(mesh->xend + 1, j, k);
153+
}
154+
}
155+
}
156+
}
105157

106158
auto& species = state["species"][name];
107159

src/sheath_boundary.cxx

+11-5
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ SheathBoundary::SheathBoundary(std::string name, Options &alloptions, Solver *)
100100
// not the value at the boundary half-way between cells. This is due
101101
// to how twist-shift boundary conditions and non-aligned inputs are
102102
// treated; using the cell boundary gives incorrect results.
103+
104+
floor_potential = options["floor_potential"]
105+
.doc("Apply a floor to wall potential when calculating Ve?")
106+
.withDefault<bool>(true);
103107
}
104108

105109
void SheathBoundary::transform(Options &state) {
@@ -323,11 +327,12 @@ void SheathBoundary::transform(Options &state) {
323327
const BoutReal tesheath = 0.5 * (Te[im] + Te[i]); // electron temperature
324328
const BoutReal phi_wall = wall_potential[i];
325329

326-
const BoutReal phisheath = floor(
327-
0.5 * (phi[im] + phi[i]), phi_wall); // Electron saturation at phi = phi_wall
330+
const BoutReal phisheath = floor_potential ? floor(
331+
0.5 * (phi[im] + phi[i]), phi_wall) // Electron saturation at phi = phi_wall
332+
: 0.5 * (phi[im] + phi[i]);
328333

329334
// Electron sheath heat transmission
330-
const BoutReal gamma_e = 2 / (1. - Ge) + (phisheath - phi_wall) / floor(tesheath, 1e-5);
335+
const BoutReal gamma_e = floor(2 / (1. - Ge) + (phisheath - phi_wall) / floor(tesheath, 1e-5), 0.0);
331336

332337
// Electron velocity into sheath (< 0)
333338
const BoutReal vesheath = (tesheath < 1e-10) ?
@@ -385,10 +390,11 @@ void SheathBoundary::transform(Options &state) {
385390
const BoutReal nesheath = 0.5 * (Ne[ip] + Ne[i]);
386391
const BoutReal tesheath = 0.5 * (Te[ip] + Te[i]); // electron temperature
387392
const BoutReal phi_wall = wall_potential[i];
388-
const BoutReal phisheath = floor(0.5 * (phi[ip] + phi[i]), phi_wall); // Electron saturation at phi = phi_wall
393+
const BoutReal phisheath = floor_potential ? floor(0.5 * (phi[ip] + phi[i]), phi_wall) // Electron saturation at phi = phi_wall
394+
: 0.5 * (phi[ip] + phi[i]);
389395

390396
// Electron sheath heat transmission
391-
const BoutReal gamma_e = 2 / (1. - Ge) + (phisheath - phi_wall) / floor(tesheath, 1e-5);
397+
const BoutReal gamma_e = floor(2 / (1. - Ge) + (phisheath - phi_wall) / floor(tesheath, 1e-5), 0.0);
392398

393399
// Electron velocity into sheath (> 0)
394400
const BoutReal vesheath = (tesheath < 1e-10) ?

0 commit comments

Comments
 (0)