Skip to content

Commit 0d30096

Browse files
committed
improve FieldContainer and lightly refactor fill_field_vals
1 parent 4faeb8c commit 0d30096

File tree

3 files changed

+182
-27
lines changed

3 files changed

+182
-27
lines changed

tests/grtestutils/field_container.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "./field_container.hpp"
1515
#include "./field_info_detail.hpp"
1616
#include "./status.hpp"
17+
#include "./utils.hpp"
1718

1819
#include "grackle.h"
1920
#include "status_reporting.h"
@@ -63,7 +64,7 @@ namespace field_detail {
6364
/// The returned where the keys are the names of every active Grackle field
6465
/// and the associated values are all nullptr
6566
static MapType make_nullptr_map_(const GrackleCtxPack& ctx_pack,
66-
bool exclude_metal) {
67+
const std::set<std::string>& exclude_fields) {
6768
MapType m;
6869
// fill up m with (field, nullptr) pairs for each field enabled by ctx_pack
6970
auto fn = [&m](const char* name, const FieldInfo& info) {
@@ -73,8 +74,8 @@ static MapType make_nullptr_map_(const GrackleCtxPack& ctx_pack,
7374
};
7475
for_each_named_field(ctx_pack, fn);
7576

76-
if (exclude_metal) {
77-
auto search = m.find("metal_density");
77+
for (const std::string& name : exclude_fields) {
78+
auto search = m.find(name);
7879
if (search != m.end()) {
7980
m.erase(search);
8081
}
@@ -141,14 +142,15 @@ std::pair<CorePack, Status> CorePack::setup_1d(MapType&& premade_map,
141142
} // namespace field_detail
142143

143144
std::pair<FieldContainer, Status> FieldContainer::create_1d(
144-
const GrackleCtxPack& ctx_pack, int buf_size, bool disable_metal) {
145+
const GrackleCtxPack& ctx_pack, int buf_size,
146+
const std::set<std::string>& exclude_fields) {
145147
if (!ctx_pack.is_initialized()) {
146148
return {FieldContainer{}, error::Adhoc("ctx_pack isn't initialized")};
147149
}
148150

149151
// construct a map for each relevant field (the values are all nullptr)
150152
field_detail::MapType m =
151-
field_detail::make_nullptr_map_(ctx_pack, disable_metal);
153+
field_detail::make_nullptr_map_(ctx_pack, exclude_fields);
152154
std::pair<field_detail::CorePack, Status> tmp =
153155
field_detail::CorePack::setup_1d(std::move(m), buf_size);
154156
if (tmp.second.is_err()) {
@@ -162,14 +164,14 @@ std::pair<FieldContainer, Status> FieldContainer::create_1d(
162164

163165
std::pair<FieldContainer, Status> FieldContainer::create(
164166
const GrackleCtxPack& ctx_pack, const GridLayout& layout,
165-
bool disable_metal) {
167+
const std::set<std::string>& exclude_fields) {
166168
int total_count = layout.n_elements();
167169

168170
std::pair<FieldContainer, Status> tmp =
169-
FieldContainer::create_1d(ctx_pack, total_count, disable_metal);
171+
FieldContainer::create_1d(ctx_pack, total_count, exclude_fields);
170172

171173
if (tmp.second.is_ok()) {
172-
(*tmp.first.data_.layout) = layout;
174+
tmp.first.data_.override_layout(layout);
173175
}
174176
return tmp;
175177
}
@@ -181,13 +183,13 @@ FieldContainer FieldContainer::clone() const {
181183
field_detail::MapType m = this->data_.map;
182184
std::pair<field_detail::CorePack, Status> tmp =
183185
field_detail::CorePack::setup_1d(std::move(m),
184-
this->elements_per_field());
186+
this->grid_layout().n_elements());
185187
// it shouldn't be possible for tmp.second.is_err() to return true
186188
FieldContainer out;
187189
out.data_ = std::move(tmp.first);
188190

189191
// copy over layout properties
190-
(*out.data_.layout) = *this->data_.layout;
192+
out.data_.override_layout(*this->data_.layout);
191193

192194
// now copy over field values
193195
copy_into_helper_(out);
@@ -204,7 +206,7 @@ void FieldContainer::copy_into_helper_(FieldContainer& other) const {
204206

205207
const gr_float* src = this->data_.data_buf.get();
206208
gr_float* dst = other.data_.data_buf.get();
207-
int length = this->elements_per_field() * this->n_fields();
209+
int length = this->grid_layout().n_elements() * this->n_fields();
208210
std::memcpy(dst, src, length * sizeof(gr_float));
209211
}
210212

@@ -222,4 +224,20 @@ bool FieldContainer::same_fields(const FieldContainer& other) const {
222224
return true;
223225
}
224226

227+
void PrintTo(const FieldContainer& fc, std::ostream* os) {
228+
const GridLayout& layout = fc.grid_layout();
229+
*os << "FieldContainer{\n"
230+
<< " " << layout.to_string() << ",\n"
231+
<< " grid_dx = " << fc.data_.my_fields->grid_dx << ",\n"
232+
<< " fields = {\n";
233+
std::size_t n_elements = layout.n_elements();
234+
for (const auto& it : fc) {
235+
const std::string& field_name = it.first;
236+
const gr_float* ptr = it.second;
237+
*os << " " << field_name << " =\n";
238+
*os << " " << ptr_to_string(ptr, n_elements) << '\n';
239+
}
240+
*os << " }\n}\n";
241+
}
242+
225243
} // namespace grtest

tests/grtestutils/field_container.hpp

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <map>
2424
#include <memory> // std::map
2525
#include <optional>
26+
#include <set>
2627
#include <string>
2728
#include <string_view>
2829
#include <utility> // std::pair
@@ -136,6 +137,17 @@ class GridLayout {
136137
}
137138

138139
public:
140+
/// factory method
141+
///
142+
/// This is for the common case where we want a 1D layout with a hardcoded
143+
/// number of entries (and we know at compile-time that it can't fail)
144+
template <int N>
145+
static GridLayout create_1d() noexcept {
146+
static_assert(N >= 1, "N must be positive");
147+
int dim[3] = {N, 0, 0};
148+
return GridLayout::create_(1, dim, nullptr, nullptr).first;
149+
}
150+
139151
/// factory method
140152
static std::pair<GridLayout, Status> try_from_dim(int rank, const int* dim) {
141153
return GridLayout::create_(rank, dim, nullptr, nullptr);
@@ -245,6 +257,19 @@ struct CorePack {
245257
/// maps field names to pointers
246258
MapType map;
247259

260+
/// prefer this method over directly modifying layout
261+
///
262+
/// @note This obviously requires that this->layout and this->my_fields are
263+
/// not nullptr.
264+
void override_layout(const GridLayout& new_layout) noexcept {
265+
// overriding this->layout implicitly updates the members of `my_fields`,
266+
// `my_fields->grid_(dimension|start|end)`, because these members all point
267+
// to statically sized C array members of GridLayout
268+
*this->layout = new_layout;
269+
// make sure grid_rank remains up to date
270+
this->my_fields->grid_rank = this->layout->rank();
271+
}
272+
248273
/// factory method that consumes a @p premade_map
249274
///
250275
/// @param premade_map A string to pointer mapping. The keys of this argument
@@ -285,13 +310,24 @@ class FieldContainer {
285310
~FieldContainer() = default;
286311

287312
/// A factory method to make a simple 1d container
313+
///
314+
/// @param ctx_pack The Grackle Configuration used for initialization
315+
/// @param buf_size The positive number of elements in the container
316+
/// @param exclude_fields Names of fields that should be excluded
317+
///
318+
/// @note This is provided as a convenience. Do we really need it?
288319
static std::pair<FieldContainer, Status> create_1d(
289-
const GrackleCtxPack& ctx_pack, int buf_size, bool disable_metal = false);
320+
const GrackleCtxPack& ctx_pack, int buf_size,
321+
const std::set<std::string>& exclude_fields = {});
290322

291323
/// A factory method to make a container
324+
///
325+
/// @param ctx_pack The Grackle Configuration used for initialization
326+
/// @param layout The Grid layout to use
327+
/// @param exclude_fields Names of fields that should be excluded
292328
static std::pair<FieldContainer, Status> create(
293329
const GrackleCtxPack& ctx_pack, const GridLayout& layout,
294-
bool disable_metal = false);
330+
const std::set<std::string>& exclude_fields = {});
295331

296332
/// Create a clone of FieldContainer
297333
FieldContainer clone() const;
@@ -302,7 +338,8 @@ class FieldContainer {
302338
///
303339
/// @warning
304340
/// Setting bypass_check to `true` is risky. It primarily exists for the
305-
/// case where you call this method in a loop
341+
/// case where you call this method in a loop and we already know that 2
342+
/// containers are compatible
306343
Status copy_into(FieldContainer& dest, bool bypass_check = false) const {
307344
if (!(bypass_check || this->same_grid_props(dest))) {
308345
return error::Adhoc("grid properties are incompatible");
@@ -321,9 +358,20 @@ class FieldContainer {
321358
/// returns whether `this` and @p other contains the same set of fields
322359
bool same_fields(const FieldContainer& other) const;
323360

361+
/**@{*/
362+
/// get the pointer to the wrapped @ref grackle_field_data instance
363+
///
364+
/// @note
365+
/// This primarily exists to support Grackle API function. Avoid mutating
366+
/// the returned pointer
324367
const grackle_field_data* get_ptr() const { return data_.my_fields.get(); }
368+
369+
// NOLINTNEXTLINE(readability-make-member-function-const)
325370
grackle_field_data* get_ptr() { return data_.my_fields.get(); }
371+
/**@}*/
326372

373+
/**@{*/
374+
/// finds the field data pointer for the field with the specified name
327375
std::optional<gr_float*> find(std::string_view key) {
328376
auto s = data_.map.find(key);
329377
return (s != data_.map.end()) ? std::optional{s->second} : std::nullopt;
@@ -333,18 +381,40 @@ class FieldContainer {
333381
auto s = data_.map.find(key);
334382
return (s != data_.map.end()) ? std::optional{s->second} : std::nullopt;
335383
}
384+
/**@}*/
385+
386+
/**@{*/
387+
/// finds the field data pointer or aborts the program
388+
gr_float* get_or_abort(std::string_view key) {
389+
std::optional<gr_float*> tmp = find(key);
390+
if (!tmp.has_value()) {
391+
std::string key_copy{key}; // required b/c key isn't '\0' terminated
392+
GR_INTERNAL_ERROR("\"%s\" field wasn't found", key_copy.c_str());
393+
}
394+
return *tmp;
395+
}
336396

337-
int n_fields() const { return static_cast<int>(data_.map.size()); }
397+
const gr_float* get_or_abort(std::string_view key) const {
398+
std::optional<const gr_float*> tmp = find(key);
399+
if (!tmp.has_value()) {
400+
std::string key_copy{key}; // required b/c key isn't '\0' terminated
401+
GR_INTERNAL_ERROR("\"%s\" field wasn't found", key_copy.c_str());
402+
}
403+
return *tmp;
404+
}
405+
/**@}*/
338406

339-
int rank() const { return data_.my_fields->grid_rank; }
407+
const GridLayout& grid_layout() const { return *data_.layout; }
340408

341-
/// the number of elements per field (unaffected by ghost zones)
342-
int elements_per_field() const { return data_.layout->n_elements(); }
409+
int n_fields() const { return static_cast<int>(data_.map.size()); }
343410

344411
// we define both of the following methods to support the writing of
345412
// range-based for-loops to iterate over key-buffer pairs
346413
MapType::const_iterator begin() const { return data_.map.begin(); }
347414
MapType::const_iterator end() const { return data_.map.end(); }
415+
416+
// teach googletest how to print this type
417+
friend void PrintTo(const FieldContainer& fc, std::ostream* os);
348418
};
349419
} // namespace grtest
350420

tests/grtestutils/fill_field_vals.hpp

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,55 @@
2525

2626
namespace grtest {
2727

28+
/// @defgroup fillfieldsgrp Logic for filling field values
29+
///
30+
/// This group of entities defines the logic for initializing values in a
31+
/// @ref FluidContainer.
32+
///
33+
/// Since our testing tools may be used to test (or benchmark) scenarios with
34+
/// an arbitrarily large number of elements, we adopt a general-purpose scheme
35+
/// where we essentially repeat a "tile" of field values 1 or more times.
36+
///
37+
/// In general, a "tile" refers to an instance of a generic type that implements
38+
/// the methods illustrated in the following snippet.
39+
///
40+
/// @code{C++}
41+
/// struct MySampleTileType {
42+
/// /// number of elements in the tile
43+
/// int get_n_vals() const noexcept;
44+
///
45+
/// // iF field_name is known, fill buf with associated values associated
46+
/// // and return true. Otherwise, return false.
47+
/// //
48+
/// // buf should have a length given by get_n_vals()
49+
/// bool fill_buf(gr_float* buf, std::string_view field_name) const noexcept;
50+
/// };
51+
/// @endcode
52+
///
53+
/// This approach was picked to maximize flexibility.
54+
/// - it can work for initializing a single value or an arbitrary number of
55+
/// values. This reduces the maintenance burden (at the cost of some
56+
/// performance)
57+
/// - it should be easy enough to implement a new tile type
58+
/// - it is conceivable that we could reuse this machinery for initializing
59+
/// a field from serialized values (maybe from a json or toml or hdf5 file)
60+
/**@{*/ // open the doxygen group
61+
2862
/// Fill in the values of @p fc by repeating the values from @p field_tile
2963
///
30-
/// @param[in, out] fc The field container that will be filled
3164
/// @param[in] field_tile Specifies the values to use
65+
/// @param[in, out] fc The field container that will be filled
3266
template <class Tile>
33-
Status fill_field_vals(FieldContainer& fc, const Tile& field_tile) {
67+
Status fill_field_vals(const Tile& field_tile, FieldContainer& fc) {
3468
// access field grid index properties
35-
int rank = fc.rank();
36-
int ix_start = fc.get_ptr()->grid_start[0];
37-
int ix_stop = fc.get_ptr()->grid_end[0] + 1;
38-
int iy_start = (rank >= 2) ? fc.get_ptr()->grid_start[1] : 0;
39-
int iy_stop = (rank >= 2) ? fc.get_ptr()->grid_end[1] + 1 : 1;
40-
int iz_start = (rank == 3) ? fc.get_ptr()->grid_start[2] : 0;
41-
int iz_stop = (rank == 3) ? fc.get_ptr()->grid_end[2] + 1 : 1;
69+
const GridLayout& layout = fc.grid_layout();
70+
int rank = layout.rank();
71+
int ix_start = layout.start()[0];
72+
int ix_stop = layout.stop()[0];
73+
int iy_start = (rank >= 2) ? layout.start()[1] : 0;
74+
int iy_stop = (rank >= 2) ? layout.stop()[1] : 1;
75+
int iz_start = (rank == 3) ? layout.start()[2] : 0;
76+
int iz_stop = (rank == 3) ? layout.stop()[2] : 1;
4277

4378
int mx = fc.get_ptr()->grid_dimension[0];
4479
int my = (rank >= 2) ? fc.get_ptr()->grid_dimension[1] : 1;
@@ -78,6 +113,30 @@ Status fill_field_vals(FieldContainer& fc, const Tile& field_tile) {
78113
return OkStatus();
79114
}
80115

116+
/// Create a @ref FieldContainer and fill in values by repeating the values from
117+
/// @p field_tile
118+
///
119+
/// @param field_tile Specifies the values to use
120+
/// @param ctx_pack The Grackle Configuration used for initialization
121+
/// @param layout The Grid layout to use
122+
/// @param exclude_fields Field names that should be excluded during creation
123+
///
124+
/// This function is simple: it just calls @ref FieldContainer::create and
125+
/// @ref fill_field_vals. But because this sequence of events is relatively
126+
/// common, the aggregation of @ref Status instance is quite convenient.
127+
template <class Tile>
128+
std::pair<FieldContainer, Status> create_and_fill_FieldContainer(
129+
const Tile& field_tile, const GrackleCtxPack& ctx_pack,
130+
const GridLayout& layout,
131+
const std::set<std::string>& exclude_fields = {}) {
132+
std::pair<grtest::FieldContainer, grtest::Status> out =
133+
grtest::FieldContainer::create(ctx_pack, layout, exclude_fields);
134+
if (out.second.is_ok()) {
135+
out.second = fill_field_vals(field_tile, out.first);
136+
}
137+
return out;
138+
}
139+
81140
/// Represents a very simplistic set of conditions
82141
struct SimpleFieldTile {
83142
double mfrac_metal;
@@ -89,8 +148,14 @@ struct SimpleFieldTile {
89148
double common_density;
90149
double common_eint;
91150

151+
/// number of values specified by the tile
92152
int get_n_vals() const noexcept { return 1; }
93153

154+
/// fill @p buf with values associated with the specified @p field_name
155+
///
156+
/// @param[out] buf This is filled. The length is given by @ref get_n_vals()
157+
/// @param[in] field_name Name of the field
158+
/// @returns `true` if the field is known. Otherwise, returns `false`
94159
bool fill_buf(gr_float* buf, std::string_view field_name) const noexcept {
95160
if (field_name == "density") {
96161
buf[0] = common_density;
@@ -145,6 +210,8 @@ inline SimpleFieldTile make_simple_tile(const GrackleCtxPack& ctx_pack,
145210
/* common_eint = */ common_eint};
146211
}
147212

213+
/**@}*/ // close the doxygen group
214+
148215
} // namespace grtest
149216

150217
#endif // GRTESTUTILS_SET_FIELD_CONDITIONS_HPP

0 commit comments

Comments
 (0)