Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f32587a
Ensure we do not break B for FCI
dschwoerer Aug 13, 2026
25b79ad
Do not add parallel slices to parallel slices
dschwoerer Aug 13, 2026
d1d09e8
Add normalisation for metrics
dschwoerer Aug 13, 2026
1dd1583
Fix compilation for 2D metrics
dschwoerer Aug 13, 2026
2735dcb
Suppress unused var warnings
dschwoerer Aug 13, 2026
6282f56
Switch to preprocessor #if
dschwoerer Aug 13, 2026
75d4fb9
Add dx normalisation for Tokamak geometry
dschwoerer Aug 14, 2026
993e633
Improve comments
dschwoerer Aug 14, 2026
bb4964e
Fix J normalisation
dschwoerer Aug 14, 2026
ae5e990
Merge branch 'next' of https://github.com/boutproject/BOUT-dev into n…
dschwoerer Aug 17, 2026
eddc7e1
Avoid returning void from function call
dschwoerer Aug 17, 2026
fa34beb
Add normalisation of g_22_y*
dschwoerer Aug 19, 2026
adce6ca
Handle non-FCI case in normaliseMetricFCI
dschwoerer Aug 19, 2026
cf90f25
Remove non-const version from header
dschwoerer Aug 19, 2026
50f4b02
Add API to set metric tensors and J and B
dschwoerer Aug 21, 2026
8ca53f0
Add MetricNormaliser class
dschwoerer Aug 24, 2026
cd18251
Add normalisation to MetricTensors
dschwoerer Aug 24, 2026
f9210bc
Add generic normalisation to coordinates
dschwoerer Aug 24, 2026
30e7624
Remove specific normalisation
dschwoerer Aug 24, 2026
da0c682
Add normalisations to tokamak_coordinates
dschwoerer Aug 24, 2026
d49fbec
Add missing virtual/override
dschwoerer Aug 24, 2026
a220081
Turn MetricNormaliser into a plain struct
dschwoerer Aug 24, 2026
01621b9
Use const ref
dschwoerer Aug 24, 2026
ae9b5e7
Remove redundant initialisation
dschwoerer Aug 24, 2026
ef2fda3
Be explit about it being not set
dschwoerer Aug 25, 2026
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
29 changes: 27 additions & 2 deletions include/bout/coordinates.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

class Mesh;
class YBoundary;
struct MetricNormaliser;

/*!
* Represents a coordinate system, and associated operators
Expand Down Expand Up @@ -154,8 +155,6 @@ public:
/// get g_22 at the cell faces;
const FieldMetric& g_22_ylow() const;
const FieldMetric& g_22_yhigh() const;
FieldMetric& g_22_ylow();
FieldMetric& g_22_yhigh();
// Cell Areas
const FieldMetric& cell_area_xlow() const {
if (_cell_area_xlow.has_value()) {
Expand Down Expand Up @@ -370,10 +369,16 @@ public:
void setMetricTensor(const ContravariantMetricTensor& contravariant_metric_tensor,
const CovariantMetricTensor& covariant_metric_tensor);

void setMetricTensorJB(const ContravariantMetricTensor& contravariant_metric_tensor,
const CovariantMetricTensor& covariant_metric_tensor,
const FieldMetric& J, const FieldMetric& Bxy);

void communicateMetricTensor();

void communicateDz();

void normaliseMetric(const MetricNormaliser& norm);

///< Coordinate system Jacobian, so volume of cell is J*dx*dy*dz
const FieldMetric& J() const;

Expand Down Expand Up @@ -543,4 +548,24 @@ namespace bout {
std::string parallelSliceFieldName(std::string_view field, int offset);
}

/// Represents a way to normalise the coordinate system
/// If a component returns nothing, no normalisation is performed.
/// Coordinate values are divided by the respective component from
/// MetricNormaliser, with the exception of the contravariant metric
/// tensor, which is multiplied by the normalisation factor.
struct MetricNormaliser {
std::optional<BoutReal> g = std::nullopt;
std::optional<BoutReal> g11 = std::nullopt;
std::optional<BoutReal> g22 = std::nullopt;
std::optional<BoutReal> g33 = std::nullopt;
std::optional<BoutReal> g12 = std::nullopt;
std::optional<BoutReal> g13 = std::nullopt;
std::optional<BoutReal> g23 = std::nullopt;
std::optional<BoutReal> dx = std::nullopt;
std::optional<BoutReal> dy = std::nullopt;
std::optional<BoutReal> dz = std::nullopt;
std::optional<BoutReal> J = std::nullopt;
std::optional<BoutReal> Bxy = std::nullopt;
};
Comment thread
dschwoerer marked this conversation as resolved.
Comment thread
dschwoerer marked this conversation as resolved.

Comment thread
dschwoerer marked this conversation as resolved.
#endif // BOUT_COORDINATES_H
8 changes: 8 additions & 0 deletions include/bout/metric_tensor.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ using FieldMetric = Field2D;
} // namespace bout

class Coordinates;
struct MetricNormaliser;

class MetricTensor {
public:
Expand Down Expand Up @@ -77,6 +78,9 @@ public:

void communicate();

template <class F>
void normaliseMetric(const MetricNormaliser& norm, const F& op);

private:
FieldMetric g11_m, g22_m, g33_m, g12_m, g13_m, g23_m;
};
Expand All @@ -90,6 +94,8 @@ public:

auto inverse(const std::string& region = "RGN_ALL", bool communicate = true)
-> ContravariantMetricTensor;

void normaliseMetric(const MetricNormaliser& norm);
};

class ContravariantMetricTensor : public MetricTensor {
Expand All @@ -98,6 +104,8 @@ public:

auto inverse(const std::string& region = "RGN_ALL", bool communicate = true)
-> CovariantMetricTensor;

void normaliseMetric(const MetricNormaliser& norm);
};

#endif //BOUT_METRIC_TENSOR_HXX
3 changes: 3 additions & 0 deletions include/bout/tokamak_coordinates.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ TokamakCoordinates set_tokamak_coordinates(Mesh& mesh, BoutReal Lbar = 1.0,
BoutReal Bbar = 1.0, bool no_shear = false,
BoutReal shear_factor = 1.0);

MetricNormaliser TokamakOrFCIMetricNormaliser(const Mesh* mesh, BoutReal Bnorm,
BoutReal rho_s0);

} // namespace bout

#endif //BOUT_TOKAMAK_COORDINATES_HXX
67 changes: 65 additions & 2 deletions src/mesh/coordinates.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ Coordinates::FieldMetric Coordinates::recalculateJacobian() const {
}

Coordinates::FieldMetric Coordinates::recalculateBxy() const {
ASSERT2(not J().isFci());
return sqrt(g_22()) / J();
}

Expand Down Expand Up @@ -1268,6 +1269,16 @@ void Coordinates::setMetricTensor(
setJ(recalculateJacobian());
setBxy(recalculateBxy());
}
void Coordinates::setMetricTensorJB(
const ContravariantMetricTensor& contravariant_metric_tensor,
const CovariantMetricTensor& covariant_metric_tensor, const FieldMetric& J,
const FieldMetric& Bxy) {
contravariantMetricTensor = contravariant_metric_tensor;
covariantMetricTensor = covariant_metric_tensor;
setJ(J);
setBxy(Bxy);
invalidateMetricCaches();
}

void Coordinates::communicateMetricTensor() {
contravariantMetricTensor.communicate();
Expand All @@ -1277,6 +1288,58 @@ void Coordinates::communicateMetricTensor() {
void Coordinates::communicateDz() { localmesh->communicate(dz_); }

void Coordinates::splitBxyParallelSlices() {
Bxy_.splitParallelSlices();
Bxy_.yup() = Bxy_.ydown() = Bxy_;
if (not Bxy_.hasParallelSlices()) {
auto copy = Bxy_;
Bxy_.splitParallelSlices();
Bxy_.yup() = Bxy_.ydown() = copy;
}
}

void Coordinates::normaliseMetric(const MetricNormaliser& norm) {
covariantMetricTensor.normaliseMetric(norm);
contravariantMetricTensor.normaliseMetric(norm);

#if BOUT_USE_METRIC_3D
using FieldMetricParallel = Field3DParallel;
#else
using FieldMetricParallel = Field2D;
#endif

if (norm.J.has_value()) {
if (J().hasParallelSlices()) {
setJ(FieldMetricParallel{J() / *norm.J});
} else {
setJ(J() / *norm.J);
}
}
if (norm.Bxy.has_value()) {
if (Bxy().hasParallelSlices()) {
setBxy(FieldMetricParallel{Bxy() / *norm.Bxy});
} else {
setBxy(Bxy() / *norm.Bxy);
}
}
if (norm.dx.has_value()) {
setDx(dx() / *norm.dx);
}
if (norm.dy.has_value()) {
setDy(dy() / *norm.dy);
}
if (norm.dz.has_value()) {
setDz(dz() / *norm.dz);
}
invalidateMetricCaches();
if (norm.g.has_value() or norm.g22.has_value()) {
if (Bxy().isFci()) {
// No we compute g_22_* - they must not be cleared. If they get
// cleared, they will be recomputed, but not normalised!
auto g22 = norm.g.has_value() ? norm.g.value() : norm.g22.value();
g_22_ylow();
g_22_yhigh();
ASSERT2(_g_22_ylow.has_value());
(*_g_22_ylow) /= g22;
ASSERT2(_g_22_yhigh.has_value());
(*_g_22_yhigh) /= g22;
}
}
}
7 changes: 5 additions & 2 deletions src/mesh/difops.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -762,10 +762,13 @@ Field3D Laplace(const Field3D& f, CELL_LOC outloc,
* Inverse of Laplacian operator in LaplaceXY solver
*******************************************************************************/

Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) {
#if BOUT_USE_METRIC_3D
Field2D Laplace_perpXY([[maybe_unused]] const Field2D& A,
[[maybe_unused]] const Field2D& f) {
throw BoutException("Coordinates::Laplace_perpXY for 3D metric not implemented");
}
#else
Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) {
const auto& coords = *f.getCoordinates();

Field2D result;
Expand Down Expand Up @@ -821,8 +824,8 @@ Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) {
}

return result;
#endif
}
#endif

/*******************************************************************************
* b0xGrad_dot_Grad
Expand Down
42 changes: 42 additions & 0 deletions src/mesh/metric_tensor.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,48 @@ auto ContravariantMetricTensor::inverse(const std::string& region, bool communic
return result;
}

template <class F>
void MetricTensor::normaliseMetric(const MetricNormaliser& norm, const F& op) {
if (norm.g.has_value()) {
op(g11_m, norm.g);
op(g22_m, norm.g);
op(g33_m, norm.g);
op(g12_m, norm.g);
op(g13_m, norm.g);
op(g23_m, norm.g);
} else {
op(g11_m, norm.g11);
op(g22_m, norm.g22);
op(g33_m, norm.g33);
op(g12_m, norm.g12);
op(g13_m, norm.g13);
op(g23_m, norm.g23);
}
}

void ContravariantMetricTensor::normaliseMetric(const MetricNormaliser& norm) {
MetricTensor::normaliseMetric(norm, [](FieldMetric& f, auto fac) {
if (fac.has_value()) {
if (f.hasParallelSlices()) {
f.asField3DParallel() *= fac.value();
} else {
f *= fac.value();
}
}
});
}
void CovariantMetricTensor::normaliseMetric(const MetricNormaliser& norm) {
MetricTensor::normaliseMetric(norm, [](FieldMetric& f, auto fac) {
if (fac.has_value()) {
if (f.hasParallelSlices()) {
f.asField3DParallel() /= fac.value();
} else {
f /= fac.value();
}
}
});
}

void MetricTensor::communicate() {
g11_m.getMesh()->communicate_no_slices(g11_m, g22_m, g33_m, g12_m, g13_m, g23_m);
}
17 changes: 17 additions & 0 deletions src/mesh/tokamak_coordinates.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,21 @@ TokamakCoordinates set_tokamak_coordinates(Mesh& mesh, BoutReal Lbar, BoutReal B

return {Rxy, Zxy, Bpxy, Btxy, Bxy, hthe, I, I_unnormalised};
}

MetricNormaliser TokamakOrFCIMetricNormaliser(const Mesh* mesh, BoutReal Bnorm,
BoutReal rho_s0) {
if (mesh->isFci()) {
return {.g{SQ(rho_s0)}, .J{rho_s0 * rho_s0 * rho_s0}, .Bxy{Bnorm}};
Comment thread
ZedThree marked this conversation as resolved.
}
return {.g11{1 / SQ(Bnorm * rho_s0)},
.g22{SQ(rho_s0)},
.g33{SQ(rho_s0)},
.g12{1 / Bnorm},
.g13{1 / Bnorm},
.g23{SQ(rho_s0)},
.dx{rho_s0 * rho_s0 * Bnorm},
.J{rho_s0 / Bnorm},
.Bxy{Bnorm}};
Comment thread
dschwoerer marked this conversation as resolved.
}

} // namespace bout
Loading