Skip to content

Commit c42dc24

Browse files
ZedThreedschwoerer
authored andcommitted
Return std::optional from invert3x3
Allows throwing more specific error in coordinates
1 parent 3ceef07 commit c42dc24

File tree

3 files changed

+35
-50
lines changed

3 files changed

+35
-50
lines changed

src/mesh/coordinates.cxx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,9 +1264,10 @@ int Coordinates::calcCovariant(const std::string& region) {
12641264
a(1, 2) = a(2, 1) = g23[i];
12651265
a(0, 2) = a(2, 0) = g13[i];
12661266

1267-
if (!invert3x3(a)) {
1268-
output_error.write("\tERROR: metric tensor is singular at ({:d}, {:d})\n", i.x(),
1269-
i.y());
1267+
if (const auto det = bout::invert3x3(a); det.has_value()) {
1268+
output_error.write(
1269+
"\tERROR: metric tensor is singular at ({:d}, {:d}), determinant: {:d}\n",
1270+
i.x(), i.y(), det.value());
12701271
return 1;
12711272
}
12721273

@@ -1320,9 +1321,10 @@ int Coordinates::calcContravariant(const std::string& region) {
13201321
a(1, 2) = a(2, 1) = g_23[i];
13211322
a(0, 2) = a(2, 0) = g_13[i];
13221323

1323-
if (!invert3x3(a)) {
1324-
output_error.write("\tERROR: metric tensor is singular at ({:d}, {:d})\n", i.x(),
1325-
i.y());
1324+
if (const auto det = bout::invert3x3(a); det.has_value()) {
1325+
output_error.write(
1326+
"\tERROR: metric tensor is singular at ({:d}, {:d}), determinant: {:d}\n",
1327+
i.x(), i.y(), det.value());
13261328
return 1;
13271329
}
13281330

src/mesh/invert3x3.hxx

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,42 +29,38 @@
2929
#pragma once
3030

3131
#include <bout/utils.hxx>
32+
#include <optional>
3233

3334
/// Explicit inversion of a 3x3 matrix \p a
3435
///
35-
/// The input \p small determines how small the determinant must be for
36-
/// us to throw due to the matrix being singular (ill conditioned);
37-
/// If small is less than zero then instead of throwing we return false.
38-
/// This is ugly but can be used to support some use cases.
39-
template <typename T>
40-
bool invert3x3(Matrix<T>& a, T small = 1.0e-15) {
36+
/// If the matrix is singular (ill conditioned), the determinant is
37+
/// return. Otherwise, an empty `std::optional` is return
38+
namespace bout {
39+
inline std::optional<BoutReal> invert3x3(Matrix<BoutReal>& a) {
4140
TRACE("invert3x3");
4241

4342
// Calculate the first co-factors
44-
T A = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);
45-
T B = a(1, 2) * a(2, 0) - a(1, 0) * a(2, 2);
46-
T C = a(1, 0) * a(2, 1) - a(1, 1) * a(2, 0);
43+
BoutReal A = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);
44+
BoutReal B = a(1, 2) * a(2, 0) - a(1, 0) * a(2, 2);
45+
BoutReal C = a(1, 0) * a(2, 1) - a(1, 1) * a(2, 0);
4746

4847
// Calculate the determinant
49-
T det = a(0, 0) * A + a(0, 1) * B + a(0, 2) * C;
50-
48+
const BoutReal det = a(0, 0) * A + a(0, 1) * B + a(0, 2) * C;
49+
constexpr BoutReal small = 1.0e-15;
5150
if (std::abs(det) < std::abs(small)) {
52-
if (small >= 0) {
53-
throw BoutException("Determinant of matrix < {:e} --> Poorly conditioned", small);
54-
}
55-
return false;
51+
return std::optional<BoutReal>{det};
5652
}
5753

5854
// Calculate the rest of the co-factors
59-
T D = a(0, 2) * a(2, 1) - a(0, 1) * a(2, 2);
60-
T E = a(0, 0) * a(2, 2) - a(0, 2) * a(2, 0);
61-
T F = a(0, 1) * a(2, 0) - a(0, 0) * a(2, 1);
62-
T G = a(0, 1) * a(1, 2) - a(0, 2) * a(1, 1);
63-
T H = a(0, 2) * a(1, 0) - a(0, 0) * a(1, 2);
64-
T I = a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0);
55+
BoutReal D = a(0, 2) * a(2, 1) - a(0, 1) * a(2, 2);
56+
BoutReal E = a(0, 0) * a(2, 2) - a(0, 2) * a(2, 0);
57+
BoutReal F = a(0, 1) * a(2, 0) - a(0, 0) * a(2, 1);
58+
BoutReal G = a(0, 1) * a(1, 2) - a(0, 2) * a(1, 1);
59+
BoutReal H = a(0, 2) * a(1, 0) - a(0, 0) * a(1, 2);
60+
BoutReal I = a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0);
6561

6662
// Now construct the output, overwrites input
67-
T detinv = 1.0 / det;
63+
BoutReal detinv = 1.0 / det;
6864

6965
a(0, 0) = A * detinv;
7066
a(0, 1) = D * detinv;
@@ -76,5 +72,6 @@ bool invert3x3(Matrix<T>& a, T small = 1.0e-15) {
7672
a(2, 1) = F * detinv;
7773
a(2, 2) = I * detinv;
7874

79-
return true;
75+
return std::nullopt;
8076
}
77+
} // namespace bout

tests/unit/mesh/test_invert3x3.cxx

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ TEST(Invert3x3Test, Identity) {
99
input(i, i) = 1.0;
1010
}
1111
auto expected = input;
12-
invert3x3(input);
12+
bout::invert3x3(input);
1313

1414
for (int j = 0; j < 3; j++) {
1515
for (int i = 0; i < 3; i++) {
@@ -39,7 +39,7 @@ TEST(Invert3x3Test, InvertTwice) {
3939
}
4040

4141
// Invert twice to check if we get back to where we started
42-
invert3x3(input);
42+
bout::invert3x3(input);
4343

4444
for (int j = 0; j < 3; j++) {
4545
for (int i = 0; i < 3; i++) {
@@ -52,37 +52,23 @@ TEST(Invert3x3Test, InvertTwice) {
5252
TEST(Invert3x3Test, Singular) {
5353
Matrix<BoutReal> input(3, 3);
5454
input = 0;
55-
EXPECT_THROW(invert3x3(input), BoutException);
55+
auto result = bout::invert3x3(input);
56+
EXPECT_TRUE(result.has_value());
5657
}
5758

5859
TEST(Invert3x3Test, BadCondition) {
5960
Matrix<BoutReal> input(3, 3);
6061

61-
// Default small
6262
input = 0.;
6363
input(0, 0) = 1.0e-16;
6464
input(1, 1) = 1.0;
6565
input(2, 2) = 1.0;
66-
EXPECT_THROW(invert3x3(input), BoutException);
66+
EXPECT_TRUE(bout::invert3x3(input).has_value());
6767

68-
// Default small -- not quite bad enough condition
68+
// not quite bad enough condition
6969
input = 0.;
7070
input(0, 0) = 1.0e-12;
7171
input(1, 1) = 1.0;
7272
input(2, 2) = 1.0;
73-
EXPECT_NO_THROW(invert3x3(input));
74-
75-
// Non-default small
76-
input = 0.;
77-
input(0, 0) = 1.0e-12;
78-
input(1, 1) = 1.0;
79-
input(2, 2) = 1.0;
80-
EXPECT_THROW(invert3x3(input, 1.0e-10), BoutException);
81-
82-
// Non-default small
83-
input = 0.;
84-
input(0, 0) = 1.0e-12;
85-
input(1, 1) = 1.0;
86-
input(2, 2) = 1.0;
87-
EXPECT_NO_THROW(invert3x3(input, -1.0e-10));
73+
EXPECT_FALSE(bout::invert3x3(input).has_value());
8874
}

0 commit comments

Comments
 (0)