Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ d1cfb8abd6aa5c76e6c1a4d7ab20929c65f8afc2
ec69e8838be2dde140a915e50506f8e1ce3cb534
f2bc0488a298f136294c523bc5ab4086d090059b
1b4707187a3a85126338303dc766280b8fb2dc56
b2e2f3575e68f771be2a4341af5fd14e2870469e
64e4285ec38569f66d31e589a4610cefd16d8076
6 changes: 6 additions & 0 deletions include/bout/array.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ public:
ptr = get(new_size);
}

/*!
* Change shape of the container.
* Invalidates contents.
*/
void reshape(std::tuple<size_type> new_shape) { reallocate(std::get<0>(new_shape)); }

/*!
* Holds a static variable which controls whether
* memory blocks (dataBlock) are put into a store
Expand Down
18 changes: 17 additions & 1 deletion include/bout/utils.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* simple but common calculations
*
**************************************************************************
* Copyright 2010 - 2025 BOUT++ contributors
* Copyright 2010 - 2026 BOUT++ contributors
*
* Contact: Ben Dudson, dudson2@llnl.gov
*
Expand Down Expand Up @@ -231,6 +231,14 @@ public:
data.reallocate(new_size_1 * new_size_2);
}

/*!
* Change shape of the container.
* Invalidates contents.
*/
void reshape(std::tuple<size_type, size_type> new_shape) {
reallocate(std::get<0>(new_shape), std::get<1>(new_shape));
}

Matrix& operator=(const Matrix& other) {
n1 = other.n1;
n2 = other.n2;
Expand Down Expand Up @@ -331,6 +339,14 @@ public:
data.reallocate(new_size_1 * new_size_2 * new_size_3);
}

/*!
* Change shape of the container.
* Invalidates contents.
*/
void reshape(std::tuple<size_type, size_type, size_type> new_shape) {
reallocate(std::get<0>(new_shape), std::get<1>(new_shape), std::get<2>(new_shape));
}

Tensor& operator=(const Tensor& other) {
n1 = other.n1;
n2 = other.n2;
Expand Down
29 changes: 24 additions & 5 deletions src/sys/options.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -744,27 +744,46 @@ FieldPerp Options::as<FieldPerp>(const FieldPerp& similar_to) const {
}

namespace {
/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the
/// appropriate container
/// Primary declaration of ConvertContainer, for specialization below.
/// No definition needed unless it is used.
template <class Container>
struct ConvertContainer {
struct ConvertContainer;

/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the
/// appropriate container. Templated on both the container class C
/// and scalar type Scalar.
template <template <class> class C, class Scalar>
struct ConvertContainer<C<Scalar>> {
using Container = C<Scalar>;
ConvertContainer(std::string error, Container similar_to_)
: error_message(std::move(error)), similar_to(std::move(similar_to_)) {}

Container operator()(int value) {
Container result(similar_to);
std::fill(std::begin(result), std::end(result), value);
std::fill(std::begin(result), std::end(result), static_cast<Scalar>(value));
return result;
}

Container operator()(BoutReal value) {
Container result(similar_to);
std::fill(std::begin(result), std::end(result), value);
std::fill(std::begin(result), std::end(result), static_cast<Scalar>(value));
return result;
}

Container operator()(const Container& value) { return value; }

// Convert between scalar types: C<OtherScalar> -> C<Scalar>
// The size of the returned result will be the same as the input value
template <class OtherScalar>
Container operator()(const C<OtherScalar>& value) {
Container result(similar_to);
result.reshape(value.shape()); // Resize to shape of input

std::transform(std::begin(value), std::end(value), std::begin(result),
[](const OtherScalar& x) { return static_cast<Scalar>(x); });
return result;
}

template <class Other>
Container operator()([[maybe_unused]] const Other& value) {
throw BoutException(error_message);
Expand Down
6 changes: 6 additions & 0 deletions src/sys/options/options_netcdf.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ void readGroup(const std::string& filename, const NcGroup& group, Options& resul
var.getVar(value.begin());
result[var_name] = value;
}
} else if (var_type == ncInt) {
Tensor<int> value(static_cast<int>(dims[0].getSize()),
static_cast<int>(dims[1].getSize()),
static_cast<int>(dims[2].getSize()));
var.getVar(value.begin());
result[var_name] = value;
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/sys/test_options.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1470,3 +1470,36 @@ TEST_F(OptionsTest, MatrixInt) {
const auto matrix_out = options["int_matrix"].as<Matrix<int>>();
ASSERT_EQ(matrix_out(nx - 1, ny - 1), matrix_in(nx - 1, ny - 1));
}

TEST_F(OptionsTest, TensorIntToTensorBoutReal) {
constexpr int nx = 2;
constexpr int ny = 3;
constexpr int nz = 4;

Tensor<int> t_int(nx, ny, nz);
int count = 0;
for (int i = 0; i < nx; ++i) {
for (int j = 0; j < ny; ++j) {
for (int k = 0; k < nz; ++k) {
t_int(i, j, k) = ++count;
}
}
}

Options option = t_int;

// Convert to Tensor<BoutReal>
Tensor<BoutReal> t_boutreal = option.as<Tensor<BoutReal>>();

std::tuple<int, int, int> expected_shape{nx, ny, nz};
ASSERT_EQ(expected_shape, t_boutreal.shape());

count = 0;
for (int i = 0; i < nx; ++i) {
for (int j = 0; j < ny; ++j) {
for (int k = 0; k < nz; ++k) {
ASSERT_FLOAT_EQ(t_boutreal(i, j, k), static_cast<BoutReal>(++count));
}
}
}
}
Loading