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

Disable neutral transport #13

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a1d1e79
neutral_mixed: Add FV::Div_a_Grad_perp_limit operator
bendudson Mar 2, 2024
9c2e871
neutral_mixed: Switch perpendicular limiter to Upwind
bendudson Mar 6, 2024
bcfb164
neutral_mixed: Modify Div_a_Grad_perp_limit operator
bendudson Mar 27, 2024
1ec6be0
neutral_boundary: Fix typos in fast reflection coefficients
bendudson Mar 28, 2024
fec8232
neutral_mixed: Change parallel to Upwind
bendudson Mar 28, 2024
48a8db9
neutral_mixed: Use Div_a_Grad_perp_upwind_flows
bendudson Apr 10, 2024
7eb92e7
Add flag to suppress lax flux
mikekryjak Apr 12, 2024
b9d8fb9
neutral_mixed: improvements/diags for flooring
mikekryjak Apr 13, 2024
e73bc59
Merge branch 'master' into neutral-advection
mikekryjak Apr 18, 2024
b50e4fa
Hardcode correct use of Nnlim and Pnlim in perp neutral advection
mikekryjak May 22, 2024
b31b18e
Calculate neutral Pnfloor from Nnfloor like evolve_density
mikekryjak May 22, 2024
967ef3e
Fix typo
mikekryjak May 23, 2024
b8accfe
Change energy flow diagnostic factor from 3/2 to 5/2
mikekryjak May 28, 2024
0aa087b
Change conduction to upwind and expose fluxes
mikekryjak May 28, 2024
2027118
Add comments on flow diags and fix conduction one
mikekryjak May 28, 2024
c095e43
Fix typo
mikekryjak May 28, 2024
f5e1e98
Merge branch 'master' into neutral-advection
mikekryjak Aug 19, 2024
620c914
Revert to Div_a_Grad_perp, add flows
mikekryjak Aug 19, 2024
6eb0a04
Revert to Div_a_Grad_perp, add flows
mikekryjak Aug 19, 2024
d709eac
Merge branch 'neutral-advection' of https://github.com/bendudson/herm…
mikekryjak Aug 19, 2024
e2e19f3
Add flags to suppress par or perp neutral transport
mikekryjak Aug 23, 2024
d27b5f5
Fix silly mistakes
mikekryjak Aug 23, 2024
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
245 changes: 240 additions & 5 deletions include/div_ops.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
#define DIV_OPS_H

#include <bout/field3d.hxx>
#include <bout/vector3d.hxx>
#include <bout/fv_ops.hxx>
#include <bout/vector3d.hxx>

/*!
* Diffusion in index space
Expand Down Expand Up @@ -61,9 +61,16 @@ const Field3D D4DZ4_Index(const Field3D& f);
const Field2D Laplace_FV(const Field2D& k, const Field2D& f);

/// Perpendicular diffusion including X and Y directions
/// Takes Div_a_Grad_perp from BOUT++ and adds flows
const Field3D Div_a_Grad_perp_flows(const Field3D& a, const Field3D& f,
Field3D& flux_xlow, Field3D& flux_ylow);
/// Same but with upwinding
/// WARNING: Causes checkerboarding in neutral_mixed integrated test
const Field3D Div_a_Grad_perp_upwind(const Field3D& a, const Field3D& f);
/// Version of function that returns flows
const Field3D Div_a_Grad_perp_upwind_flows(const Field3D& a, const Field3D& f, Field3D& flux_xlow, Field3D& flux_ylow);
/// Same but with upwinding and flows
/// WARNING: Causes checkerboarding in neutral_mixed integrated test
const Field3D Div_a_Grad_perp_upwind_flows(const Field3D& a, const Field3D& f,
Field3D& flux_xlow, Field3D& flux_ylow);

namespace FV {

Expand Down Expand Up @@ -105,8 +112,7 @@ struct Superbee {
BoutReal sign = SIGN(gL);
gL = fabs(gL);
gR = fabs(gR);
BoutReal half_slope = sign * BOUTMAX(BOUTMIN(gL, 0.5*gR),
BOUTMIN(gR, 0.5*gL));
BoutReal half_slope = sign * BOUTMAX(BOUTMIN(gL, 0.5 * gR), BOUTMIN(gR, 0.5 * gL));
n.L = n.c - half_slope;
n.R = n.c + half_slope;
}
Expand Down Expand Up @@ -459,6 +465,235 @@ const Field3D Div_par_mod(const Field3D& f_in, const Field3D& v_in,
return are_unaligned ? fromFieldAligned(result, "RGN_NOBNDRY") : result;
}

/// Div ( a g Grad_perp(f) ) -- Perpendicular gradient-driven advection
///
/// This version uses a slope limiter to calculate cell edge values of g in X,
/// the advects the upwind cell edge.
///
/// 1st order upwinding is used in Y.
template <typename CellEdges = MC>
const Field3D Div_a_Grad_perp_limit(const Field3D& a, const Field3D& g, const Field3D& f) {
ASSERT2(a.getLocation() == f.getLocation());

Mesh* mesh = a.getMesh();

// Requires at least 2 communication guard cells in X, 1 in Y
ASSERT1(mesh->xstart >= 2);
ASSERT1(mesh->ystart >= 1);

CellEdges cellboundary;

Field3D result{zeroFrom(f)};

Coordinates* coord = f.getCoordinates();

// Flux in x

for (int i = mesh->xstart - 1; i <= mesh->xend; i++) {
for (int j = mesh->ystart; j <= mesh->yend; j++) {
for (int k = 0; k < mesh->LocalNz; k++) {
// Calculate flux from i to i+1

const BoutReal gradient = f(i + 1, j, k) - f(i, j, k);

// Mid-point average boundary value of 'a'
const BoutReal aedge = 0.5 * (a(i + 1, j, k) + a(i, j, k));
BoutReal gedge;
if (((i == mesh->xstart - 1) and mesh->firstX()) or
((i == mesh->xend) and mesh->lastX())) {
// Mid-point average boundary value of 'g'
gedge = 0.5 * (g(i + 1, j, k) + g(i, j, k));
} else if (gradient > 0) {
// Flux is from (i+1) to (i)
// Reconstruct `g` at left of (i+1, j, k)

Stencil1D sg;
sg.m = g(i, j, k);
sg.c = g(i + 1, j, k);
sg.p = g(i + 2, j, k);
cellboundary(sg); // Calculate sg.R and sg.L

gedge = sg.L;
} else {
// Flux is from (i) to (i+1)
// Reconstruct `g` at right of (i, j, k)

Stencil1D sg;
sg.m = g(i - 1, j, k);
sg.c = g(i, j, k);
sg.p = g(i + 1, j, k);
cellboundary(sg); // Calculate sg.R and sg.L

gedge = sg.R;
}

// Flux across cell edge
const BoutReal fout = gradient * aedge * gedge
* (coord->J(i, j) * coord->g11(i, j)
+ coord->J(i + 1, j) * coord->g11(i + 1, j))
/ (coord->dx(i, j) + coord->dx(i + 1, j));

result(i, j, k) += fout / (coord->dx(i, j) * coord->J(i, j));
result(i + 1, j, k) -= fout / (coord->dx(i + 1, j) * coord->J(i + 1, j));
}
}
}

// Y and Z fluxes require Y derivatives

// Fields containing values along the magnetic field
Field3D fup(mesh), fdown(mesh);
Field3D aup(mesh), adown(mesh);
Field3D gup(mesh), gdown(mesh);

// Values on this y slice (centre).
// This is needed because toFieldAligned may modify the field
Field3D ac = a;
Field3D gc = g;
Field3D fc = f;

// Result of the Y and Z fluxes
Field3D yzresult(mesh);
yzresult.allocate();

if (f.hasParallelSlices() && a.hasParallelSlices() && g.hasParallelSlices()) {
// All inputs have yup and ydown

fup = f.yup();
fdown = f.ydown();

aup = a.yup();
adown = a.ydown();

gup = g.yup();
gdown = g.ydown();
} else {
// At least one input doesn't have yup/ydown fields.
// Need to shift to/from field aligned coordinates

fup = fdown = fc = toFieldAligned(f);
aup = adown = ac = toFieldAligned(a);
gup = gdown = gc = toFieldAligned(g);
yzresult.setDirectionY(YDirectionType::Aligned);
}

// Y flux

for (int i = mesh->xstart; i <= mesh->xend; i++) {
for (int j = mesh->ystart; j <= mesh->yend; j++) {

BoutReal coef_u =
0.5
* (coord->g_23(i, j) / SQ(coord->J(i, j) * coord->Bxy(i, j))
+ coord->g_23(i, j + 1) / SQ(coord->J(i, j + 1) * coord->Bxy(i, j + 1)));

BoutReal coef_d =
0.5
* (coord->g_23(i, j) / SQ(coord->J(i, j) * coord->Bxy(i, j))
+ coord->g_23(i, j - 1) / SQ(coord->J(i, j - 1) * coord->Bxy(i, j - 1)));

for (int k = 0; k < mesh->LocalNz; k++) {
// Calculate flux between j and j+1
int kp = (k + 1) % mesh->LocalNz;
int km = (k - 1 + mesh->LocalNz) % mesh->LocalNz;

// Calculate Z derivative at y boundary
BoutReal dfdz =
0.25 * (fc(i, j, kp) - fc(i, j, km) + fup(i, j + 1, kp) - fup(i, j + 1, km))
/ coord->dz(i, j);

// Y derivative
BoutReal dfdy = 2. * (fup(i, j + 1, k) - fc(i, j, k))
/ (coord->dy(i, j + 1) + coord->dy(i, j));

BoutReal aedge = 0.5 * (ac(i, j, k) + aup(i, j + 1, k));
BoutReal gedge;
if ((j == mesh->yend) and mesh->lastY(i)) {
// Midpoint boundary value
gedge = 0.5 * (gc(i, j, k) + gup(i, j + 1, k));
} else if (dfdy > 0) {
// Flux from (j+1) to (j)
gedge = gup(i, j + 1, k);
} else {
// Flux from (j) to (j+1)
gedge = gc(i, j, k);
}

BoutReal fout = aedge * gedge * 0.5
* (coord->J(i, j) * coord->g23(i, j)
+ coord->J(i, j + 1) * coord->g23(i, j + 1))
* (dfdz - coef_u * dfdy);

yzresult(i, j, k) = fout / (coord->dy(i, j) * coord->J(i, j));

// Calculate flux between j and j-1
dfdz = 0.25
* (fc(i, j, kp) - fc(i, j, km) + fdown(i, j - 1, kp) - fdown(i, j - 1, km))
/ coord->dz(i, j);

dfdy = 2. * (fc(i, j, k) - fdown(i, j - 1, k))
/ (coord->dy(i, j) + coord->dy(i, j - 1));

aedge = 0.5 * (ac(i, j, k) + adown(i, j - 1, k));
if ((j == mesh->ystart) and mesh->firstY(i)) {
gedge = 0.5 * (gc(i, j, k) + gdown(i, j - 1, k));
} else if (dfdy > 0) {
gedge = gc(i, j, k);
} else {
gedge = gdown(i, j - 1, k);
}

fout = aedge * gedge * 0.5
* (coord->J(i, j) * coord->g23(i, j)
+ coord->J(i, j - 1) * coord->g23(i, j - 1))
* (dfdz - coef_d * dfdy);

yzresult(i, j, k) -= fout / (coord->dy(i, j) * coord->J(i, j));
}
}
}

// Z flux
// Easier since all metrics constant in Z

for (int i = mesh->xstart; i <= mesh->xend; i++) {
for (int j = mesh->ystart; j <= mesh->yend; j++) {
// Coefficient in front of df/dy term
BoutReal coef = coord->g_23(i, j)
/ (coord->dy(i, j + 1) + 2. * coord->dy(i, j) + coord->dy(i, j - 1))
/ SQ(coord->J(i, j) * coord->Bxy(i, j));

for (int k = 0; k < mesh->LocalNz; k++) {
// Calculate flux between k and k+1
int kp = (k + 1) % mesh->LocalNz;

BoutReal gradient =
// df/dz
(fc(i, j, kp) - fc(i, j, k)) / coord->dz(i, j)

// - g_yz * df/dy / SQ(J*B)
- coef
* (fup(i, j + 1, k) + fup(i, j + 1, kp) - fdown(i, j - 1, k)
- fdown(i, j - 1, kp));

BoutReal fout = gradient * 0.5*(ac(i,j,kp) + ac(i,j,k)) *
((gradient > 0) ? gc(i, j, kp) : gc(i, j, k));

yzresult(i, j, k) += fout / coord->dz(i, j);
yzresult(i, j, kp) -= fout / coord->dz(i, j);
}
}
}
// Check if we need to transform back
if (f.hasParallelSlices() && a.hasParallelSlices()) {
result += yzresult;
} else {
result += fromFieldAligned(yzresult);
}

return result;
}

} // namespace FV

#endif // DIV_OPS_H
16 changes: 15 additions & 1 deletion include/neutral_mixed.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,40 @@ private:
BoutReal AA; ///< Atomic mass (proton = 1)

Field3D Dnn; ///< Diffusion coefficient
Field3D DnnNn, DnnPn, DnnTn, DnnNVn; ///< Used for operators
Field3D DnnNn, DnnPn, DnnNVn;

bool sheath_ydown, sheath_yup;

BoutReal nn_floor; ///< Minimum Nn used when dividing NVn by Nn to get Vn.
BoutReal pn_floor; ///< Minimum Pn used when dividing Pn by Nn to get Tn.

BoutReal flux_limit; ///< Diffusive flux limit
BoutReal diffusion_limit; ///< Maximum diffusion coefficient

bool neutral_viscosity; ///< include viscosity?
bool neutral_conduction; ///< Include heat conduction?
bool evolve_momentum; ///< Evolve parallel momentum?

bool parallel_transport, perpendicular_transport; ///< Include transport in a particular direction?

bool precondition {true}; ///< Enable preconditioner?
bool lax_flux; ///< Use Lax flux for advection terms
std::unique_ptr<Laplacian> inv; ///< Laplacian inversion used for preconditioning

Field3D density_source, pressure_source; ///< External input source
Field3D Sn, Sp, Snv; ///< Particle, pressure and momentum source
Field3D sound_speed; ///< Sound speed for use with Lax flux
Field3D perp_nn_adv_src; ///< Source due to perpendicular advection operator
Field3D par_nn_adv_src; ///< Source due to parallel advection operator

bool output_ddt; ///< Save time derivatives?
bool diagnose; ///< Save additional diagnostics?

// Flow diagnostics
Field3D particle_flow_xlow, particle_flow_ylow;
Field3D momentum_flow_xlow, momentum_flow_ylow;
Field3D energy_flow_xlow, energy_flow_ylow;
Field3D conduction_flow_xlow, conduction_flow_ylow;
};

namespace {
Expand Down
Loading
Loading