ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
quantities.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include "loop.h"
9#include "quantities.h"
10
11#include <deal.II/base/function_parser.h>
12#include <deal.II/base/mpi.templates.h>
13
14#include <array>
15#include <fstream>
16#include <iomanip>
17#include <map>
18#include <sstream>
19
20namespace ryujin
21{
22 using namespace dealii;
23
24 namespace
25 {
31 template <typename Number>
32 void to_central_moments(Number *moments,
33 const unsigned int n_moments,
34 const unsigned int n_selected)
35 {
36 for (unsigned int c = 0; c < n_selected; ++c) {
37 const auto m = [&](const unsigned int k) -> Number & {
38 return moments[(k - 1) * n_selected + c];
39 };
40 const Number m1 = m(1);
41
42 /* Higher central moments are computed from raw moments first: */
43 if (n_moments >= 4)
44 m(4) +=
45 -4. * m1 * m(3) + 6. * m1 * m1 * m(2) - 3. * m1 * m1 * m1 * m1;
46 if (n_moments >= 3)
47 m(3) += -3. * m1 * m(2) + 2. * m1 * m1 * m1;
48 if (n_moments >= 2)
49 m(2) -= m1 * m1;
50 }
51 }
52 } // namespace
53
54
55 template <typename HyperbolicDescription, int dim, typename Number>
57 const MPIEnsemble &mpi_ensemble,
58 const OfflineData<dim, Number> &offline_data,
59 const HyperbolicSystem &hyperbolic_system,
60 const Interface::ParabolicSystem &parabolic_system,
61 const MultiComponentVector<Number> &initial_precomputed,
62 const std::string &subsection /*= "Quantities"*/)
63 : ParameterAcceptor(subsection)
64 , mpi_ensemble_(mpi_ensemble)
65 , offline_data_(&offline_data)
66 , extractor_(offline_data,
67 hyperbolic_system,
68 parabolic_system,
69 initial_precomputed)
70 , base_name_("")
71 , mesh_files_have_been_written_(false)
72 {
73 quantities_ = hyperbolic_system.primitive_names(dim);
74
75 add_parameter("quantities",
76 quantities_,
77 "List of conserved, primitive, precomputed, initial, or "
78 "parabolic quantities for which statistics are accumulated "
79 "on all manifolds.");
80
81 n_moments_ = 2;
82 add_parameter("number of moments",
83 n_moments_,
84 "Number of moments computed for every selected quantity in "
85 "the time averaged and space averaged statistics: 1 (mean), "
86 "2 (mean and variance), 3 (additionally the third central "
87 "moment), 4 (additionally the fourth central moment).");
89 add_parameter("interior manifolds",
90 interior_manifolds_,
91 "List of level set functions describing interior manifolds. "
92 "The description is used to only output point values for "
93 "vertices belonging to a certain level set. "
94 "Format: '<name> : <level set formula> : <options> , [...] "
95 "(options: time_averaged, space_averaged, instantaneous)");
96
97 add_parameter("boundary manifolds",
98 boundary_manifolds_,
99 "List of level set functions describing boundary. The "
100 "description is used to only output point values for "
101 "boundary vertices belonging to a certain level set. "
102 "Format: '<name> : <level set formula> : <options> , [...] "
103 "(options: time_averaged, space_averaged, instantaneous)");
104
105 clear_temporal_statistics_on_writeout_ = true;
106 add_parameter("clear statistics on writeout",
107 clear_temporal_statistics_on_writeout_,
108 "If set to true then all temporal statistics (for "
109 "\"time_averaged\" quantities) accumulated so far are reset "
110 "each time a writeout of quantities is performed");
111 }
112
113
114 template <typename HyperbolicDescription, int dim, typename Number>
116 const std::string &name)
117 {
118#ifdef DEBUG_OUTPUT
119 std::cout << "Quantities<dim, Number>::prepare()" << std::endl;
120#endif
121
122 base_name_ = name;
123
124 extractor_.prepare(quantities_);
125
126 AssertThrow(1 <= n_moments_ && n_moments_ <= 4,
127 dealii::ExcMessage("Invalid number of moments: \"" +
128 std::to_string(n_moments_) +
129 "\" is not in the range 1 to 4."));
130
131 const unsigned int n_owned = offline_data_->n_locally_owned();
132 const auto sparsity_simd_view =
133 offline_data_->sparsity_pattern_simd().view();
134 const auto lumped_mass_matrix_view =
135 offline_data_->lumped_mass_matrix().template view<1>();
136
137 /*
138 * Create a manifold record with the name and parsed options for every
139 * entry of the parameter lists:
140 */
141
142 const auto create_manifold = [](const auto &entry, const bool boundary) {
143 const auto &[name, expression, options] = entry;
144
145 Manifold manifold;
146 manifold.name = name;
147 manifold.boundary = boundary;
148 manifold.instantaneous =
149 options.find("instantaneous") != std::string::npos;
150 manifold.time_averaged =
151 options.find("time_averaged") != std::string::npos;
152 manifold.space_averaged =
153 options.find("space_averaged") != std::string::npos;
154
155 AssertThrow(manifold.instantaneous || manifold.time_averaged ||
156 manifold.space_averaged,
157 dealii::ExcMessage(
158 "Invalid options \"" + options + "\" for manifold \"" +
159 name +
160 "\": at least one of instantaneous, time_averaged, or "
161 "space_averaged has to be selected."));
162
163 return manifold;
164 };
165
166 /*
167 * Sort the points of a manifold lexicographically by position (and
168 * normal) so that the output does not depend on the local numbering
169 * of degrees of freedom:
170 */
171
172 const auto sort_points = [](std::vector<ManifoldPoint> &points) {
173 const auto less = [](const auto &left, const auto &right) {
174 for (unsigned int d = 0; d < dim; ++d)
175 if (left[d] != right[d])
176 return left[d] < right[d];
177 return false;
178 };
179 std::sort(
180 points.begin(), points.end(), [&](const auto &a, const auto &b) {
181 const auto &[i_a, n_a, nm_a, m_a, id_a, x_a] = a;
182 const auto &[i_b, n_b, nm_b, m_b, id_b, x_b] = b;
183 return less(x_a, x_b) || (!less(x_b, x_a) && less(n_a, n_b));
184 });
185 };
186
187 /*
188 * Sort the points and populate the mirrored index and mass arrays
189 * used in the compute loops:
190 */
191
192 const auto finalize = [&](Manifold &manifold) {
193 sort_points(manifold.points);
194
195 const auto n_points = manifold.points.size();
196 manifold.indices.reinit(n_points, TransferPolicy::implicit_transfers);
197 manifold.masses.reinit(n_points, TransferPolicy::implicit_transfers);
198
199 auto *indices = manifold.indices.view();
200 auto *masses = manifold.masses.view();
201 Number mass_sum = Number(0.);
202 for (std::size_t p = 0; p < n_points; ++p) {
203 indices[p] = std::get<0>(manifold.points[p]);
204 masses[p] = std::get<3>(manifold.points[p]);
205 mass_sum += masses[p];
206 }
207
208 manifold.mass_sum =
209 Utilities::MPI::sum(mass_sum, mpi_ensemble_.ensemble_communicator());
210
211 manifolds_.push_back(std::move(manifold));
212 };
213
214 manifolds_.clear();
215
216 /*
217 * Create interior manifolds: We have to loop over all cells and
218 * collect all degrees of freedom satisfying the level set condition.
219 */
220
221 for (const auto &entry : interior_manifolds_) {
222 auto manifold = create_manifold(entry, /*boundary*/ false);
223 FunctionParser<dim> level_set_function(std::get<1>(entry));
224
225 const auto &discretization = offline_data_->discretization();
226 const auto &dof_handler = offline_data_->dof_handler();
227
228 const auto support_points =
229 dof_handler.get_fe().get_unit_support_points();
230
231 std::vector<dealii::types::global_dof_index> local_dof_indices;
232
233 /* We use a map to deduplicate the collected points: */
234 std::map<unsigned int, ManifoldPoint> preliminary_map;
235
236 for (auto cell : dof_handler.active_cell_iterators()) {
237 if (!cell->is_locally_owned())
238 continue;
239
240 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
241 local_dof_indices.resize(dofs_per_cell);
242 cell->get_active_or_mg_dof_indices(local_dof_indices);
243
244 const auto &mapping = discretization.mapping()[cell->active_fe_index()];
245
246 for (unsigned int j = 0; j < dofs_per_cell; ++j) {
247 const Point<dim> position =
248 mapping.transform_unit_to_real_cell(cell, support_points[j]);
249
250 if (std::abs(level_set_function.value(position)) > 1.e-12)
251 continue;
252
253 const auto global_index = local_dof_indices[j];
254 const auto index =
255 offline_data_->scalar_partitioner()->global_to_local(
256 global_index);
257
258 /* Skip constrained degrees of freedom: */
259 if (sparsity_simd_view.row_length(index) == 1)
260 continue;
261
262 if (index >= n_owned)
263 continue;
264
265 const Number mass = lumped_mass_matrix_view.read_entry(index);
266 preliminary_map[index] = {index,
267 dealii::Tensor<1, dim, Number>(),
268 Number(0.),
269 mass,
270 dealii::numbers::internal_face_boundary_id,
271 position};
272 }
273 }
274
275 for (const auto &[index, point] : preliminary_map)
276 manifold.points.push_back(point);
277
278 finalize(manifold);
279 }
280
281 /*
282 * Create boundary manifolds: We loop over the boundary map and collect
283 * all degrees of freedom satisfying the level set condition.
284 */
285
286 for (const auto &entry : boundary_manifolds_) {
287 auto manifold = create_manifold(entry, /*boundary*/ true);
288 FunctionParser<dim> level_set_function(std::get<1>(entry));
289
290 for (const auto &point : offline_data_->boundary_map()) {
291 const auto &i = std::get<0>(point);
292
293 /* skip nonlocal */
294 if (i >= n_owned)
295 continue;
296
297 /* skip constrained */
298 if (offline_data_->affine_constraints().is_constrained(
299 offline_data_->scalar_partitioner()->local_to_global(i)))
300 continue;
301
302 const auto &position = std::get<5>(point);
303 if (std::abs(level_set_function.value(position)) < 1.e-12)
304 manifold.points.push_back(point);
305 }
306
307 finalize(manifold);
308 }
309
310 /* Clear statistics: */
311 clear_statistics();
312
313 /* Make sure we output new mesh files: */
314 mesh_files_have_been_written_ = false;
315 }
316
317
318 template <typename HyperbolicDescription, int dim, typename Number>
320 {
321 return n_moments_ * extractor_.n_selected();
322 }
323
324
325 template <typename HyperbolicDescription, int dim, typename Number>
326 std::string Quantities<HyperbolicDescription, dim, Number>::header(
327 const bool averaged) const
328 {
329 static const std::array<std::string, 4> labels{
330 "mean", "var", "mu_3", "mu_4"};
331
332 std::string result;
333 for (unsigned int k = 0; k < (averaged ? n_moments_ : 1); ++k)
334 for (const auto &name : quantities_)
335 result += (result.empty() ? "" : "\t") +
336 (averaged ? labels[k] + "(" + name + ")" : name);
337 return result + "\n";
338 }
339
340
341 template <typename HyperbolicDescription, int dim, typename Number>
343 const StateVector<Number> &state_vector, const Number t)
344 {
345#ifdef DEBUG_OUTPUT
346 std::cout << "Quantities<dim, Number>::accumulate()" << std::endl;
347#endif
348
349 using MemorySpace = selected_memory_space_t;
350
351 extractor_.template prepare_extraction<MemorySpace>(state_vector);
352
353 for (auto &manifold : manifolds_) {
354 /* skip if we don't average in space or time: */
355 if (!manifold.time_averaged && !manifold.space_averaged)
356 continue;
357
358 std::swap(manifold.t_old, manifold.t_new);
359 std::swap(manifold.old, manifold.current);
360
361 /* accumulate new values */
362
363 auto spatial_average = internal_accumulate(manifold);
364
365 /* Average in time with trapezoidal rule: */
366
367 if (RYUJIN_UNLIKELY(manifold.t_old == Number(0.) &&
368 manifold.t_new == Number(0.))) {
369 /* We have not accumulated any statistics yet: */
370 manifold.t_old = t - 1.;
371 manifold.t_new = t;
372
373 } else {
374
375 manifold.t_new = t;
376 const Number tau = manifold.t_new - manifold.t_old;
377 const Number weight = 0.5 * tau;
378
379 const auto *old =
380 std::as_const(manifold.old).template view<MemorySpace>();
381 const auto *current =
382 std::as_const(manifold.current).template view<MemorySpace>();
383 auto *sum = manifold.sum.template view<MemorySpace>();
384
385 const auto body = [=](auto sentinel, const unsigned int j) {
386 using T = decltype(sentinel);
387 if constexpr (std::is_same_v<T, dealii::VectorizedArray<Number>>) {
388 T s, o, c;
389 s.load(sum + j);
390 o.load(old + j);
391 c.load(current + j);
392 s += weight * (o + c);
393 s.store(sum + j);
394 } else {
395 sum[j] += weight * (old[j] + current[j]);
396 }
397 };
398
399 const auto n_entries = static_cast<unsigned int>(manifold.sum.size());
400 loop<MemorySpace, Number>(
401 "quantities_trapezoidal_rule", body, 0, n_entries, n_entries);
402
403 manifold.t_sum += tau;
404 }
405
406 /* Record average in space: */
407 to_central_moments(
408 spatial_average.data(), n_moments_, extractor_.n_selected());
409 manifold.time_series.emplace_back(t, std::move(spatial_average));
410 }
411 }
412
413
414 template <typename HyperbolicDescription, int dim, typename Number>
416 const StateVector<Number> &state_vector,
417 const Number t,
418 unsigned int cycle)
419 {
420#ifdef DEBUG_OUTPUT
421 std::cout << "Quantities<dim, Number>::write_out()" << std::endl;
422#endif
423
424 /*
425 * First, write out mesh files if this hasn't happened yet.
426 */
427 if (!mesh_files_have_been_written_) {
428 write_mesh_files(cycle);
429 mesh_files_have_been_written_ = true;
430 }
431
432 /*
433 * Manifolds that are only output instantaneously have not been
434 * evaluated in accumulate(). Prepare the extractor if we have to
435 * evaluate any of them:
436 */
437
438 if (std::any_of(manifolds_.begin(), manifolds_.end(), [](const auto &m) {
439 return m.instantaneous && !m.time_averaged && !m.space_averaged;
440 }))
441 extractor_.template prepare_extraction<selected_memory_space_t>(
442 state_vector);
443
444 /*
445 * Next write out instantaneous and time_averaged maps, and flush the
446 * space_averaged values to the corresponding log files:
447 */
448
449 for (auto &manifold : manifolds_) {
450 const auto prefix = base_name_ + "-" + manifold.name + "-R" +
451 Utilities::to_string(cycle, 4);
452
453 /*
454 * Compute and output instantaneous field:
455 */
456
457 if (manifold.instantaneous) {
458 const std::string file_name = prefix + "-instantaneous.dat";
459
460 std::stringstream time_stamp;
461 time_stamp << std::scientific << std::setprecision(14);
462 time_stamp << "# at t = " << t << std::endl;
463
464 /* We have not computed any updated statistics yet: */
465
466 if (!manifold.time_averaged && !manifold.space_averaged)
467 internal_accumulate(manifold);
468 else
469 AssertThrow(manifold.t_new == t, dealii::ExcInternalError());
470
471 internal_write_out(file_name,
472 time_stamp.str(),
473 manifold.current,
474 Number(1.),
475 /*averaged*/ false);
476 }
477
478 /*
479 * Output time averaged field:
480 */
481
482 if (manifold.time_averaged) {
483 const std::string file_name = prefix + "-time_averaged.dat";
484
485 /* Check whether we have accumulated any statistics yet: */
486 if (manifold.t_sum != Number(0.)) {
487 std::stringstream time_stamp;
488 time_stamp << std::scientific << std::setprecision(14);
489 time_stamp << "# averaged from t = "
490 << manifold.t_new - manifold.t_sum
491 << " to t = " << manifold.t_new << std::endl;
492
493 internal_write_out(file_name,
494 time_stamp.str(),
495 manifold.sum,
496 Number(1.) / manifold.t_sum,
497 /*averaged*/ true);
498 }
499 }
500
501 /*
502 * Output space averaged field:
503 */
504
505 if (manifold.space_averaged) {
506 /* Write to a new time series file after every call to prepare(): */
507 bool append = true;
508 if (!manifold.time_series_cycle.has_value()) {
509 manifold.time_series_cycle = cycle;
510 append = false;
511 }
512
513 const auto file_name =
514 base_name_ + "-" + manifold.name + "-R" +
515 Utilities::to_string(manifold.time_series_cycle.value(), 4) +
516 "-space_averaged_time_series.dat";
517
518 internal_write_out_time_series(
519 file_name, manifold.time_series, /*append*/ append);
520 manifold.time_series.clear();
521 }
522 }
523
524 if (clear_temporal_statistics_on_writeout_)
525 clear_statistics();
526 }
527
528
529 template <typename HyperbolicDescription, int dim, typename Number>
531 unsigned int cycle)
532 {
533 for (const auto &manifold : manifolds_) {
534 /* Skip outputting the point map for spatial averages. */
535 if (!manifold.instantaneous && !manifold.time_averaged)
536 continue;
537
538 /*
539 * Gather the point maps of all MPI ranks on the root rank, which
540 * then writes out the file.
541 *
542 * FIXME: This serializes the output on a single rank. Ideally, we
543 * should do MPI IO with all ranks participating.
544 */
545
546 const auto received = Utilities::MPI::gather(
547 mpi_ensemble_.ensemble_communicator(), manifold.points);
548
549 if (Utilities::MPI::this_mpi_process(
550 mpi_ensemble_.ensemble_communicator()) != 0)
551 continue;
552
553 std::ofstream output(base_name_ + "-" + manifold.name + "-R" +
554 Utilities::to_string(cycle, 4) + "-points.dat");
555
556 output << std::scientific << std::setprecision(14);
557
558 if (manifold.boundary)
559 output << "#\n# position\tnormal\tnormal mass\tboundary mass\n";
560 else
561 output << "#\n# position\tinterior mass\n";
562
563 unsigned int rank = 0;
564 for (const auto &entries : received) {
565 output << "# rank " << rank++ << "\n";
566 for (const auto &entry : entries) {
567 const auto &[index, n_i, nm_i, m_i, id, x_i] = entry;
568 if (manifold.boundary)
569 output << x_i << "\t" << n_i << "\t" << nm_i << "\t" << m_i << "\n";
570 else
571 output << x_i << "\t" << m_i << "\n";
572 } /*entry*/
573 } /*entries*/
574
575 output << std::flush;
576 }
577 }
578
579
580 template <typename HyperbolicDescription, int dim, typename Number>
581 void Quantities<HyperbolicDescription, dim, Number>::clear_statistics()
582 {
583 using MemorySpace = selected_memory_space_t;
584
585 for (auto &manifold : manifolds_) {
586 /* reinit() zero initializes the arrays on the selected memory space: */
587 const auto n_entries = manifold.points.size() * stride();
588 for (auto *values : {&manifold.old, &manifold.current, &manifold.sum})
589 values->reinit(
590 n_entries, TransferPolicy::implicit_transfers, MemorySpace{});
591
592 manifold.t_old = manifold.t_new = manifold.t_sum = 0.;
593 manifold.time_series.clear();
594 }
595 }
596
597
598 template <typename HyperbolicDescription, int dim, typename Number>
599 std::vector<Number>
600 Quantities<HyperbolicDescription, dim, Number>::internal_accumulate(
601 Manifold &manifold)
602 {
603 using MemorySpace = selected_memory_space_t;
604
605 const auto extractor_view = extractor_.template view<MemorySpace>();
606 const unsigned int n_selected = extractor_.n_selected();
607 const unsigned int n_moments = n_moments_;
608 const unsigned int stride = this->stride();
609 const auto n_points = static_cast<unsigned int>(manifold.points.size());
610
611 const auto *indices =
612 std::as_const(manifold.indices).template view<MemorySpace>();
613 const auto *masses =
614 std::as_const(manifold.masses).template view<MemorySpace>();
615 auto *current = manifold.current.template view<MemorySpace>();
616
617 /*
618 * Extract the values of all selected quantities of a point directly
619 * into the first moment block, compute all higher raw moments (i.e.,
620 * powers of the values), and return the mass weighted values as
621 * contribution to the spatial sums:
622 */
623
624 const auto body = [=](auto /*sentinel*/, const unsigned int p) {
625 auto *values = current + p * stride;
626 extractor_view.extract_element(
627 indices[p],
628 [=](const unsigned int k, const Number value) { values[k] = value; });
629
630 for (unsigned int k = 1; k < n_moments; ++k)
631 for (unsigned int c = 0; c < n_selected; ++c)
632 values[k * n_selected + c] =
633 values[(k - 1) * n_selected + c] * values[c];
634
635 const auto mass = masses[p];
636 return [=](const unsigned int j) { return mass * values[j]; };
637 };
638
639 std::vector<Number> spatial_average(stride, Number(0.));
640 reduction_loop<MemorySpace>(
641 "quantities_accumulate",
642 body,
643 ArrayReducer<Kokkos::Sum<Number>>(spatial_average),
644 0,
645 n_points);
646
647 /* Sum over all MPI ranks and take the average: */
648
649 Utilities::MPI::sum(spatial_average,
650 mpi_ensemble_.ensemble_communicator(),
651 spatial_average);
652
653 for (auto &it : spatial_average)
654 it /= manifold.mass_sum;
655
656 return spatial_average;
657 }
658
659
660 template <typename HyperbolicDescription, int dim, typename Number>
661 void Quantities<HyperbolicDescription, dim, Number>::internal_write_out(
662 const std::string &file_name,
663 const std::string &time_stamp,
664 const Mirrored<Number *> &values,
665 const Number scale,
666 const bool averaged)
667 {
668 /*
669 * Gather the values of all MPI ranks on the root rank, which then
670 * writes out the file. The (read only) host view triggers a transfer
671 * from the device if necessary.
672 *
673 * FIXME: This serializes the output on a single rank. Ideally, we
674 * should do MPI IO with all ranks participating.
675 */
676
677 const auto *data = values.view();
678 auto received =
679 Utilities::MPI::gather(mpi_ensemble_.ensemble_communicator(),
680 std::vector<Number>(data, data + values.size()));
681
682 if (Utilities::MPI::this_mpi_process(
683 mpi_ensemble_.ensemble_communicator()) != 0)
684 return;
685
686 const unsigned int n_selected = extractor_.n_selected();
687 const unsigned int stride = this->stride();
688
689 /* For instantaneous values we only output the first moment: */
690 const unsigned int n_columns = averaged ? stride : n_selected;
691
692 std::ofstream output(file_name);
693 output << std::scientific << std::setprecision(14);
694 output << time_stamp << "# " << header(averaged);
695
696 unsigned int rank = 0;
697 for (auto &entries : received) {
698 output << "# rank " << rank++ << "\n";
699 for (std::size_t j = 0; j < entries.size(); j += stride) {
700 auto *point = entries.data() + j;
701
702 if (averaged) {
703 for (unsigned int m = 0; m < stride; ++m)
704 point[m] *= scale;
705 to_central_moments(point, n_moments_, n_selected);
706 }
707
708 for (unsigned int m = 0; m < n_columns; ++m)
709 output << (m == 0 ? "" : "\t") << point[m];
710 output << "\n";
711 }
712 }
713
714 output << std::flush;
715 }
716
717
718 template <typename HyperbolicDescription, int dim, typename Number>
719 void Quantities<HyperbolicDescription, dim, Number>::
720 internal_write_out_time_series(
721 const std::string &file_name,
722 const std::vector<std::pair<Number, std::vector<Number>>> &values,
723 bool append)
724 {
725 if (Utilities::MPI::this_mpi_process(
726 mpi_ensemble_.ensemble_communicator()) != 0)
727 return;
728
729 std::ofstream output;
730 output << std::scientific << std::setprecision(14);
731
732 if (append) {
733 output.open(file_name, std::ofstream::out | std::ofstream::app);
734 } else {
735 output.open(file_name, std::ofstream::out | std::ofstream::trunc);
736 output << "# time t\t" << header(/*averaged*/ true);
737 }
738
739 for (const auto &[t, entry] : values) {
740 output << t;
741 for (const auto &value : entry)
742 output << "\t" << value;
743 output << "\n";
744 }
745
746 output << std::flush;
747 }
748
749} /* namespace ryujin */
Quantities(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const Interface::ParabolicSystem &parabolic_system, const MultiComponentVector< Number > &initial_precomputed, const std::string &subsection="/Quantities")
void accumulate(const StateVector< Number > &state_vector, const Number t)
typename HyperbolicDescription::HyperbolicSystem HyperbolicSystem
Definition quantities.h:57
void write_out(const StateVector< Number > &state_vector, const Number t, unsigned int cycle)
void prepare(const std::string &name)
std::conditional_t< have_separate_memory_spaces, dealii::MemorySpace::Default, dealii::MemorySpace::Host > selected_memory_space_t
Definition gpu.h:65
#define RYUJIN_UNLIKELY(x)