PRISMS-PF Manual
Loading...
Searching...
No Matches
read_binary.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: © 2026 PRISMS Center at the University of Michigan
2// SPDX-License-Identifier: GNU Lesser General Public Version 2.1
3
4#pragma once
5
6#include <deal.II/base/exceptions.h>
7#include <deal.II/base/point.h>
8#include <deal.II/lac/vector.h>
9
10#include <prismspf/core/types.h>
11
13
15
16#include <bit>
17#include <fstream>
18#include <iostream>
19#include <ranges>
20#include <string>
21#include <typeinfo>
22#include <utility>
23
25
29template <unsigned int dim, typename number>
30class ReadBinary : public ReadFieldBase<dim, number>
31{
32public:
36 ReadBinary(const InitialConditionFile &_ic_file,
37 const SpatialDiscretization<dim> &_spatial_discretization);
38
42 void
43 print_file() override;
44
48
49 static void
50 write_file(const std::vector<number> &data, const InitialConditionFile &ic_file);
51
55 number
56 get_scalar_value(const dealii::Point<dim> &point,
57 const std::string &scalar_name) override;
58
62 dealii::Vector<number>
63 get_vector_value(const dealii::Point<dim> &point,
64 const std::string &vector_name) override;
65
66private:
71 void
73
77 dealii::Vector<number>
78 get_value(const dealii::types::global_dof_index index, const unsigned int n_components);
79
89 dealii::Vector<number>
90 interpolate(const dealii::Point<dim> &point, const unsigned int n_components);
91
98 dealii::types::global_dof_index n_points = 1;
99
103 dealii::types::global_dof_index n_values = 0;
104
108 std::vector<number> data;
109};
110
111template <unsigned int dim, typename number>
113 const InitialConditionFile &_ic_file,
114 const SpatialDiscretization<dim> &_spatial_discretization)
115 : ReadFieldBase<dim, number>(_ic_file, _spatial_discretization)
116{
117 // Make sure the dataset format is correct
119 dealii::ExcMessage("Dataset format must be FlatBinary"));
120
121 // Check that only one field is being read in
122 AssertThrow(this->ic_file.file_variable_names.size() == 1 &&
123 this->ic_file.simulation_variable_names.size() == 1,
124 dealii::ExcMessage("Only one field can be read in from a binary file"));
125
126 // Make sure we have a rectangular domain
127 AssertThrow(_spatial_discretization.mesh_type == TriangulationType::Rectangular,
128 dealii::ExcMessage(
129 "Only rectangular domains are supported for binary input files"));
130
131 // Compute the total number of points in the binary file
132 for (unsigned int d : std::views::iota(0U, dim))
133 {
134 n_points *= this->ic_file.n_data_points[d];
135 }
136
137 // Check that the binary matches an expected size
139
140 // Read in the binary file
141 std::ifstream data_file(this->ic_file.file_name, std::ios::binary);
142 AssertThrow(data_file,
143 dealii::ExcMessage("Could not open binary file: " +
144 this->ic_file.file_name));
145
146 // Reserve space in the data vector
147 data.resize(n_values);
148
149 // Read in the data
150 data_file.read(reinterpret_cast<char *>(data.data()), n_values * sizeof(number));
151 data_file.close();
152}
153
154template <unsigned int dim, typename number>
155inline void
157{
158 // Grab the file size of the binary file in bytes
159 auto file_size = std::filesystem::file_size(this->ic_file.file_name);
160
161 // Compute the expected size of the binary file. This is simply the number of points
162 // multiplied by the size of each point in bytes.
163 auto expected_size_scalar = static_cast<std::uintmax_t>(n_points * sizeof(number));
164 auto expected_size_vector = static_cast<std::uintmax_t>(dim * expected_size_scalar);
165
166 // Make sure expected size is not zero
167 AssertThrow(
168 expected_size_scalar != 0 && expected_size_vector != 0,
169 dealii::ExcMessage(
170 "Expected input array size is zero, check that the number of data points "
171 "in each used direction is set correctly in the input file for your binary file. "
172 "You likely have the number of data points set to zero in all directions."));
173 // Make sure the size matches for either a scalar or vector
174 AssertThrow(file_size == expected_size_scalar || file_size == expected_size_vector,
175 dealii::ExcMessage(
176 "Expected binary file size (" + std::to_string(expected_size_scalar) +
177 " bytes for scalar or " + std::to_string(expected_size_vector) +
178 " bytes for vector) does not match actual file size (" +
179 std::to_string(file_size) + " bytes)."));
180
181 // Set the number of values
182 n_values = file_size == expected_size_scalar ? n_points : dim * n_points;
183}
184
185template <unsigned int dim, typename number>
186inline void
187ReadBinary<dim, number>::write_file(const std::vector<number> &data,
189{
190 // Try to open the file
191 std::ofstream data_file(ic_file.file_name, std::ios::binary);
192 AssertThrow(data_file,
193 dealii::ExcMessage("Could not open binary file: " + ic_file.file_name));
194
195 // Write the data
196 data_file.write(reinterpret_cast<const char *>(data.data()),
197 data.size() * sizeof(number));
198 data_file.close();
199}
200
201template <unsigned int dim, typename number>
202inline dealii::Vector<number>
203ReadBinary<dim, number>::get_value(const dealii::types::global_dof_index index,
204 const unsigned int n_components)
205{
206 // Create vector to hold the value with the correct number of components
207 dealii::Vector<number> value(n_components);
208
209 // Check that the number of components matches what we expect from the number of values
210 Assert(n_values % n_components == 0,
211 dealii::ExcMessage("The number of components requested in the get_value call "
212 "does not match the number of values in the binary file."));
213
214 // Fill the value vector
215 for (unsigned int component : std::views::iota(0U, n_components))
216 {
217 Assert(((n_components * index) + component) < data.size(),
218 dealii::ExcMessage("Index out of bounds in ReadBinary::get_vals"));
219 value[component] = data[(n_components * index) + component];
220 }
221
222 return value;
223}
224
225template <unsigned int dim, typename number>
226inline dealii::Vector<number>
227ReadBinary<dim, number>::interpolate(const dealii::Point<dim> &point,
228 const unsigned int n_components)
229{
230 Assert(n_components == 1 || n_components == dim,
231 dealii::ExcMessage(
232 "Number of components for interpolation must be 1 (scalar) or dim (vector)"));
233
234 // Create a vector to hold the interpolated value
235 dealii::Vector<number> value(n_components);
236
237 // Get the mesh
238 AssertThrow(this->spatial_discretization.mesh_type == TriangulationType::Rectangular,
239 dealii::ExcMessage(
240 "Only rectangular domains are supported for binary input files"));
241 const RectangularMesh<dim> &mesh = this->spatial_discretization.rectangular_mesh;
242
243 // Compute the spacing in each direction
244 std::array<number, dim> spacing;
245 for (unsigned int d : std::views::iota(0U, dim))
246 {
247 spacing[d] =
248 (mesh.size[d]) / static_cast<number>(this->ic_file.n_data_points[d] - 1);
249 }
250
251 // Compute the indices of the lower corner of the cell containing the point
252 std::array<dealii::types::global_dof_index, dim> lower_indices;
253 for (unsigned int d : std::views::iota(0U, dim))
254 {
255 lower_indices[d] =
256 static_cast<dealii::types::global_dof_index>(std::floor(point[d] / spacing[d]));
257 // Make sure we don't go out of bounds
258 if (lower_indices[d] >= this->ic_file.n_data_points[d] - 1)
259 {
260 lower_indices[d] = this->ic_file.n_data_points[d] - 2;
261 }
262 }
263
264 // Compute the weights for interpolation in each direction
265 std::array<number, dim> weights;
266 for (unsigned int d : std::views::iota(0U, dim))
267 {
268 weights[d] = (point[d] - lower_indices[d] * spacing[d]) / spacing[d];
269 }
270
271 // Perform multilinear interpolation based on the dimension
272 if constexpr (dim == 1)
273 {
274 // Here is the map of the nodes in 1D:
275 // 0 — 1
276
277 // Grab the index of the lower left corner of the cell
278 // cppcheck-suppress-begin containerOutOfBounds
279 auto lower_index = lower_indices[0];
280 // cppcheck-suppress-end containerOutOfBounds
281
282 // Get the indices of the two points in 1D
283 auto node_index_0 = lower_index;
284 auto node_index_1 = lower_index + 1;
285
286 // Get the values at these points
287 auto value_0 = get_value(node_index_0, n_components);
288 auto value_1 = get_value(node_index_1, n_components);
289
290 // Interpolate
291 for (unsigned int c : std::views::iota(0U, n_components))
292 {
293 // cppcheck-suppress-begin containerOutOfBounds
294 value[c] = (1.0 - weights[0]) * value_0[c] + weights[0] * value_1[c];
295 // cppcheck-suppress-end containerOutOfBounds
296 }
297 }
298 else if constexpr (dim == 2)
299 {
300 // Here is the map of the nodes in 2D:
301 // 01 — 11
302 // | |
303 // 00 — 10
304
305 // Grab the row length in the x-direction (0th direction)
306 auto row_length_0 = this->ic_file.n_data_points[0];
307
308 // Grab the index of the lower left corner of the cell
309 // cppcheck-suppress-begin containerOutOfBounds
310 auto lower_index = lower_indices[0] + (lower_indices[1] * row_length_0);
311 // cppcheck-suppress-end containerOutOfBounds
312
313 // Get the indices of the four points in 2D
314 auto node_index_00 = lower_index;
315 auto node_index_10 = lower_index + 1;
316
317 auto node_index_01 = lower_index + row_length_0;
318 auto node_index_11 = lower_index + row_length_0 + 1;
319
320 // Get the values at these points
321 auto value_00 = get_value(node_index_00, n_components);
322 auto value_10 = get_value(node_index_10, n_components);
323
324 auto value_01 = get_value(node_index_01, n_components);
325 auto value_11 = get_value(node_index_11, n_components);
326
327 // Interpolate
328 for (unsigned int c : std::views::iota(0U, n_components))
329 {
330 // cppcheck-suppress-begin containerOutOfBounds
331 value[c] = (1.0 - weights[0]) * (1.0 - weights[1]) * value_00[c] +
332 weights[0] * (1.0 - weights[1]) * value_10[c] +
333 (1.0 - weights[0]) * weights[1] * value_01[c] +
334 weights[0] * weights[1] * value_11[c];
335 // cppcheck-suppress-end containerOutOfBounds
336 }
337 }
338 else if constexpr (dim == 3)
339 {
340 // Here is the map of the nodes in 3D:
341 //
342 // 011 ———— 111
343 // / | / |
344 // / 001 / |
345 // 010 ——— 110 101
346 // | / | /
347 // | / | /
348 // 000 ———— 100
349
350 // Grab the row length in the x-direction (0th direction)
351 auto row_length_0 = this->ic_file.n_data_points[0];
352 // Grab the row length in the y-direction (1st direction)
353 auto row_length_1 = this->ic_file.n_data_points[1];
354
355 // Grab the index of the lower left corner of the cell
356 // cppcheck-suppress-begin containerOutOfBounds
357 auto lower_index = lower_indices[0] + (lower_indices[1] * row_length_0) +
358 (lower_indices[2] * row_length_0 * row_length_1);
359 // cppcheck-suppress-end containerOutOfBounds
360
361 // Get the indices of the eight points in 3D
362 auto node_index_000 = lower_index;
363 auto node_index_100 = lower_index + 1;
364
365 auto node_index_010 = lower_index + row_length_0;
366 auto node_index_110 = lower_index + row_length_0 + 1;
367
368 auto node_index_001 = lower_index + (row_length_0 * row_length_1);
369 auto node_index_101 = lower_index + (row_length_0 * row_length_1) + 1;
370
371 auto node_index_011 = lower_index + (row_length_0 * row_length_1) + row_length_0;
372 auto node_index_111 =
373 lower_index + (row_length_0 * row_length_1) + row_length_0 + 1;
374
375 // Get the values at these points
376 auto value_000 = get_value(node_index_000, n_components);
377 auto value_100 = get_value(node_index_100, n_components);
378
379 auto value_010 = get_value(node_index_010, n_components);
380 auto value_110 = get_value(node_index_110, n_components);
381
382 auto value_001 = get_value(node_index_001, n_components);
383 auto value_101 = get_value(node_index_101, n_components);
384
385 auto value_011 = get_value(node_index_011, n_components);
386 auto value_111 = get_value(node_index_111, n_components);
387
388 // Interpolate
389 for (unsigned int c : std::views::iota(0U, n_components))
390 {
391 // cppcheck-suppress-begin containerOutOfBounds
392 value[c] =
393 (1.0 - weights[0]) * (1.0 - weights[1]) * (1.0 - weights[2]) * value_000[c] +
394 weights[0] * (1.0 - weights[1]) * (1.0 - weights[2]) * value_100[c] +
395 (1.0 - weights[0]) * weights[1] * (1.0 - weights[2]) * value_010[c] +
396 weights[0] * weights[1] * (1.0 - weights[2]) * value_110[c] +
397 (1.0 - weights[0]) * (1.0 - weights[1]) * weights[2] * value_001[c] +
398 weights[0] * (1.0 - weights[1]) * weights[2] * value_101[c] +
399 (1.0 - weights[0]) * weights[1] * weights[2] * value_011[c] +
400 weights[0] * weights[1] * weights[2] * value_111[c];
401 // cppcheck-suppress-end containerOutOfBounds
402 }
403 }
404
405 return value;
406}
407
408template <unsigned int dim, typename number>
409inline number
410ReadBinary<dim, number>::get_scalar_value(const dealii::Point<dim> &point,
411 [[maybe_unused]] const std::string &scalar_name)
412{
413 Assert(n_values == n_points,
414 dealii::ExcMessage("The number of points should match the number of values in a "
415 "binary file for a scalar field. Make sure the file size is "
416 "correct and you are trying to access a scalar field."));
417 return interpolate(point, 1)[0];
418}
419
420template <unsigned int dim, typename number>
421inline dealii::Vector<number>
422ReadBinary<dim, number>::get_vector_value(const dealii::Point<dim> &point,
423 [[maybe_unused]] const std::string &vector_name)
424{
425 Assert((n_values / dim) == n_points,
426 dealii::ExcMessage("The number of points should match the number of values "
427 "divided by the dimension in a binary file for a vector "
428 "field. Make sure the file size is correct and you are "
429 "trying to access a vector field."));
430
431 return interpolate(point, dim);
432}
433
434template <unsigned int dim, typename number>
435inline void
437{
438 for (dealii::types::global_dof_index i : std::views::iota(0U, n_values))
439 {
440 Assert(i < data.size(),
441 dealii::ExcMessage("Index out of bounds in ReadBinary::print_file"));
442 ConditionalOStreams::pout_summary() << this->data.at(i) << "\n";
443 }
444 ConditionalOStreams::pout_summary() << std::flush;
445}
446
447PRISMS_PF_END_NAMESPACE
static dealii::ConditionalOStream & pout_summary()
Log output stream for writing a summary.log file.
Definition conditional_ostreams.cc:35
dealii::Vector< number > get_value(const dealii::types::global_dof_index index, const unsigned int n_components)
Get vector value for a given index.
Definition read_binary.h:203
std::vector< number > data
Data array to hold the read in values.
Definition read_binary.h:108
ReadBinary(const InitialConditionFile &_ic_file, const SpatialDiscretization< dim > &_spatial_discretization)
Constructor.
Definition read_binary.h:112
number get_scalar_value(const dealii::Point< dim > &point, const std::string &scalar_name) override
Get scalar value for a given point.
Definition read_binary.h:410
void check_file_size()
Check the size of the binary file and make sure it matches the expected size (in bytes).
Definition read_binary.h:156
dealii::Vector< number > interpolate(const dealii::Point< dim > &point, const unsigned int n_components)
Get vector value for a given point.
Definition read_binary.h:227
dealii::types::global_dof_index n_values
Number of values (n_points * n_components).
Definition read_binary.h:103
dealii::types::global_dof_index n_points
Number of grid points.
Definition read_binary.h:98
static void write_file(const std::vector< number > &data, const InitialConditionFile &ic_file)
Write a binary file for testing.
Definition read_binary.h:187
void print_file() override
Print the binary file to text for debugging.
Definition read_binary.h:436
dealii::Vector< number > get_vector_value(const dealii::Point< dim > &point, const std::string &vector_name) override
Get vector value for a given point.
Definition read_binary.h:422
const InitialConditionFile & ic_file
Initial condition file object.
Definition read_field_base.h:85
ReadFieldBase(const InitialConditionFile &_ic_file, const SpatialDiscretization< dim > &_spatial_discretization)
Constructor.
Definition read_field_base.h:89
const SpatialDiscretization< dim > & spatial_discretization
Spatial discretization object.
Definition read_field_base.h:80
Definition conditional_ostreams.cc:20
@ ReadBinary
Definition read_field_factory.h:31
@ Rectangular
Definition spatial_discretization.h:36
Initial condition file.
Definition io_parameters.h:320
@ FlatBinary
Definition io_parameters.h:326
Class for rectangular mesh parameters.
Definition spatial_discretization.h:143
dealii::Tensor< 1, dim, double > size
Upper bound point.
Definition spatial_discretization.h:221
Struct that holds spatial discretization parameters.
Definition spatial_discretization.h:308
TriangulationType mesh_type
Definition spatial_discretization.h:383