Skip to content

Commit 47b852f

Browse files
committed
Refactor reading grid variables for input expressions
1 parent 4985b3c commit 47b852f

File tree

3 files changed

+81
-46
lines changed

3 files changed

+81
-46
lines changed

manual/sphinx/user_docs/input_grids.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,19 @@ these are the only quantities specified, then the coordinates revert to
156156
Cartesian.
157157

158158
You can read additional quantities from the grid and make them available in
159-
expressions in the input file using ``input:read_Field3Ds`` and
160-
``input:read_Field2Ds``:
159+
expressions in the input file by listing them in the ``input:grid_variables``
160+
section, with the key being the name in the grid file (``mesh:file``) and the
161+
value being the type (one of ``field3d``, ``field2d``, ``boutreal``):
161162

162163
.. code-block:: cfg
163164
164-
[input]
165-
read_Field2Ds = rho, theta
165+
[input:grid_variables]
166+
rho = field2d
167+
theta = field2d
168+
scale = boutreal
166169
167170
[mesh]
168-
B = (1 / rho) * cos(theta)
171+
B = (scale / rho) * cos(theta)
169172
170173
This section describes how to generate inputs for tokamak equilibria. If
171174
you’re not interested in tokamaks then you can skip to the next section.

src/field/field_factory.cxx

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@
3131
#include <bout/output.hxx>
3232
#include <bout/traits.hxx>
3333
#include <bout/utils.hxx>
34+
#include <bout/bout_enum_class.hxx>
35+
#include <bout/bout_types.hxx>
36+
#include <bout/sys/expressionparser.hxx>
3437

3538
#include "fieldgenerators.hxx"
3639

37-
#include <algorithm>
3840
#include <cmath>
39-
#include <iterator>
4041
#include <memory>
4142
#include <string>
42-
#include <vector>
4343

4444
using bout::generator::Context;
4545

@@ -53,6 +53,8 @@ FieldGeneratorPtr generator(BoutReal* ptr) {
5353
return std::make_shared<FieldValuePtr>(ptr);
5454
}
5555

56+
BOUT_ENUM_CLASS(GridVariableFunction, field3d, field2d, boutreal);
57+
5658
namespace {
5759
/// Provides a placeholder whose target can be changed after creation.
5860
/// This enables recursive FieldGenerator expressions to be generated
@@ -89,41 +91,44 @@ class FieldIndirect : public FieldGenerator {
8991
FieldGeneratorPtr target;
9092
};
9193

92-
// Split a string on commas and trim whitespace from the results
93-
auto trimsplit(const std::string& str) -> std::vector<std::string> {
94-
auto split = strsplit(str, ',');
95-
std::vector<std::string> result{};
96-
result.reserve(split.size());
97-
std::transform(split.begin(), split.end(), std::back_inserter(result),
98-
[](const std::string& element) { return trim(element); });
99-
return result;
100-
}
101-
10294
// Read variables from the grid file and make them available in expressions
10395
template <class T>
104-
auto read_grid_variables(FieldFactory& factory, Mesh& mesh, Options& options,
105-
const std::string& option_name) {
106-
const auto field_variables =
107-
options["input"][option_name]
108-
.doc("Variables to read from the grid file and make available in expressions. "
109-
"Comma-separated list of variable names")
110-
.withDefault<std::string>("");
111-
112-
if (not field_variables.empty()) {
96+
auto add_grid_variable(FieldFactory& factory, Mesh& mesh, const std::string& name) {
97+
T var;
98+
mesh.get(var, name);
99+
factory.addGenerator(name, std::make_shared<GridVariable<T>>(var, name));
100+
}
101+
102+
auto read_grid_variables(FieldFactory& factory, Mesh& mesh, Options& options) {
103+
auto& field_variables = options["input"]["grid_variables"].doc(
104+
"Variables to read from the grid file and make available in expressions");
105+
106+
for (const auto& [name, value] : field_variables) {
113107
if (not mesh.isDataSourceGridFile()) {
114-
throw BoutException("A grid file ('mesh:file') is required for `input:{}`",
115-
option_name);
108+
throw BoutException(
109+
"A grid file ('mesh:file') is required for `input:grid_variables`");
116110
}
117111

118-
for (const auto& name : trimsplit(field_variables)) {
119-
if (not mesh.sourceHasVar(name)) {
120-
const auto filename = Options::root()["mesh"]["file"].as<std::string>();
121-
throw BoutException("Grid file '{}' missing `{}` specified in `input:{}`",
122-
filename, name, option_name);
123-
}
124-
T var;
112+
if (not mesh.sourceHasVar(name)) {
113+
const auto filename = Options::root()["mesh"]["file"].as<std::string>();
114+
throw BoutException(
115+
"Grid file '{}' missing `{}` specified in `input:grid_variables`", filename,
116+
name);
117+
}
118+
119+
const auto func = value.as<GridVariableFunction>();
120+
switch (func) {
121+
case GridVariableFunction::field3d:
122+
add_grid_variable<Field3D>(factory, mesh, name);
123+
break;
124+
case GridVariableFunction::field2d:
125+
add_grid_variable<Field2D>(factory, mesh, name);
126+
break;
127+
case GridVariableFunction::boutreal:
128+
BoutReal var{};
125129
mesh.get(var, name);
126-
factory.addGenerator(name, std::make_shared<GridVariable<T>>(var, name));
130+
factory.addGenerator(name, std::make_shared<FieldValue>(var));
131+
break;
127132
}
128133
}
129134
}
@@ -225,8 +230,7 @@ FieldFactory::FieldFactory(Mesh* localmesh, Options* opt)
225230
addGenerator("where", std::make_shared<FieldWhere>(nullptr, nullptr, nullptr));
226231

227232
// Variables from the grid file
228-
read_grid_variables<Field3D>(*this, *localmesh, nonconst_options, "read_Field3Ds");
229-
read_grid_variables<Field2D>(*this, *localmesh, nonconst_options, "read_Field2Ds");
233+
read_grid_variables(*this, *localmesh, nonconst_options);
230234
}
231235

232236
Field2D FieldFactory::create2D(const std::string& value, const Options* opt,

tests/unit/field/test_field_factory.cxx

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -995,8 +995,9 @@ TEST_F(FieldFactoryFieldVariableTest, CreateField3D) {
995995
}
996996

997997
{
998-
Options options{{"mesh", {{"file", filename.string()}}},
999-
{"input", {{"read_Field3Ds", "rho, theta"}}}};
998+
Options options{
999+
{"mesh", {{"file", filename.string()}}},
1000+
{"input", {{"grid_variables", {{"rho", "field3d"}, {"theta", "field3d"}}}}}};
10001001

10011002
dynamic_cast<FakeMesh*>(mesh)->setGridDataSource(new GridFile{filename});
10021003
auto factory = FieldFactory{mesh, &options};
@@ -1024,8 +1025,9 @@ TEST_F(FieldFactoryFieldVariableTest, CreateField2D) {
10241025
}
10251026

10261027
{
1027-
Options options{{"mesh", {{"file", filename.string()}}},
1028-
{"input", {{"read_Field2Ds", "rho, theta"}}}};
1028+
Options options{
1029+
{"mesh", {{"file", filename.string()}}},
1030+
{"input", {{"grid_variables", {{"rho", "field2d"}, {"theta", "field2d"}}}}}};
10291031

10301032
dynamic_cast<FakeMesh*>(mesh)->setGridDataSource(new GridFile{filename});
10311033
auto factory = FieldFactory{mesh, &options};
@@ -1036,8 +1038,33 @@ TEST_F(FieldFactoryFieldVariableTest, CreateField2D) {
10361038
}
10371039
}
10381040

1041+
TEST_F(FieldFactoryFieldVariableTest, ReadBoutReal) {
1042+
bout::testing::TempFile filename;
1043+
1044+
{
1045+
Options grid{{"rho", 4},
1046+
{"theta", 5},
1047+
{"nx", mesh->LocalNx},
1048+
{"ny", mesh->LocalNy},
1049+
{"nz", mesh->LocalNz}};
1050+
bout::OptionsIO::create(filename)->write(grid);
1051+
}
1052+
1053+
{
1054+
Options options{
1055+
{"mesh", {{"file", filename.string()}}},
1056+
{"input", {{"grid_variables", {{"rho", "boutreal"}, {"theta", "boutreal"}}}}}};
1057+
1058+
dynamic_cast<FakeMesh*>(mesh)->setGridDataSource(new GridFile{filename});
1059+
auto factory = FieldFactory{mesh, &options};
1060+
1061+
const auto output = factory.create3D("rho * theta");
1062+
EXPECT_TRUE(IsFieldEqual(output, 4 * 5));
1063+
}
1064+
}
1065+
10391066
TEST_F(FieldFactoryFieldVariableTest, NoMeshFile) {
1040-
Options options{{"input", {{"read_Field2Ds", "rho, theta"}}}};
1067+
Options options{{"input", {{"grid_variables", {{"rho", "field3d"}}}}}};
10411068

10421069
EXPECT_THROW((FieldFactory(mesh, &options)), BoutException);
10431070
}
@@ -1057,8 +1084,9 @@ TEST_F(FieldFactoryFieldVariableTest, MissingVariable) {
10571084
}
10581085

10591086
{
1060-
Options options{{"mesh", {{"file", filename.string()}}},
1061-
{"input", {{"read_Field3Ds", "rho, theta"}}}};
1087+
Options options{
1088+
{"mesh", {{"file", filename.string()}}},
1089+
{"input", {{"grid_variables", {{"rho", "field3d"}, {"theta", "field3d"}}}}}};
10621090

10631091
dynamic_cast<FakeMesh*>(mesh)->setGridDataSource(new GridFile{filename});
10641092
EXPECT_THROW((FieldFactory{mesh, &options}), BoutException);

0 commit comments

Comments
 (0)