11#include <deal.II/base/function_parser.h>
12#include <deal.II/base/mpi.templates.h>
13#include <deal.II/base/work_stream.h>
14#include <deal.II/dofs/dof_tools.h>
19template <
int rank,
int dim,
typename Number>
20bool operator<(
const Tensor<rank, dim, Number> &left,
21 const Tensor<rank, dim, Number> &right)
23 return std::lexicographical_compare(
24 left.begin_raw(), left.end_raw(), right.begin_raw(), right.end_raw());
26DEAL_II_NAMESPACE_CLOSE
30 using namespace dealii;
35 const std::string &get_options_from_name(
const T &manifolds,
36 const std::string &name)
39 std::find_if(manifolds.begin(),
41 [&, name = std::cref(name)](
const auto &element) {
42 return std::get<0>(element) == name.get();
44 Assert(it != manifolds.end(), dealii::ExcInternalError());
45 return std::get<2>(*it);
50 template <
typename Description,
int dim,
typename Number>
56 const std::string &subsection )
57 : ParameterAcceptor(subsection)
58 , mpi_ensemble_(mpi_ensemble)
59 , offline_data_(&offline_data)
60 , hyperbolic_system_(&hyperbolic_system)
61 , parabolic_system_(¶bolic_system)
63 , mesh_files_have_been_written_(false)
65 add_parameter(
"interior manifolds",
67 "List of level set functions describing interior manifolds. "
68 "The description is used to only output point values for "
69 "vertices belonging to a certain level set. "
70 "Format: '<name> : <level set formula> : <options> , [...] "
71 "(options: time_averaged, space_averaged, instantaneous)");
73 add_parameter(
"boundary manifolds",
75 "List of level set functions describing boundary. The "
76 "description is used to only output point values for "
77 "boundary vertices belonging to a certain level set. "
78 "Format: '<name> : <level set formula> : <options> , [...] "
79 "(options: time_averaged, space_averaged, instantaneous)");
81 clear_temporal_statistics_on_writeout_ =
true;
82 add_parameter(
"clear statistics on writeout",
83 clear_temporal_statistics_on_writeout_,
84 "If set to true then all temporal statistics (for "
85 "\"time_averaged\" quantities) accumulated so far are reset "
86 "each time a writeout of quantities is performed");
90 template <
typename Description,
int dim,
typename Number>
94 std::cout <<
"Quantities<dim, Number>::prepare()" << std::endl;
100 time_series_cycle_.reset();
102 const unsigned int n_owned = offline_data_->n_locally_owned();
103 const auto &sparsity_simd = offline_data_->sparsity_pattern_simd();
111 interior_maps_.clear();
113 interior_manifolds_.begin(),
114 interior_manifolds_.end(),
115 std::inserter(interior_maps_, interior_maps_.end()),
116 [
this, n_owned, &sparsity_simd](
auto it) {
117 const auto &[name, expression, option] = it;
118 FunctionParser<dim> level_set_function(expression);
120 std::vector<interior_point> map;
121 std::map<int, interior_point> preliminary_map;
123 const auto &discretization = offline_data_->discretization();
124 const auto &dof_handler = offline_data_->dof_handler();
126 const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
128 const auto support_points =
129 dof_handler.get_fe().get_unit_support_points();
131 std::vector<dealii::types::global_dof_index> local_dof_indices(
135 for (auto cell : dof_handler.active_cell_iterators()) {
138 if (!cell->is_locally_owned())
141 cell->get_active_or_mg_dof_indices(local_dof_indices);
143 for (unsigned int j = 0; j < dofs_per_cell; ++j) {
145 Point<dim> position =
146 discretization.mapping().transform_unit_to_real_cell(
147 cell, support_points[j]);
154 if (std::abs(level_set_function.value(position)) > 1.e-12)
157 const auto global_index = local_dof_indices[j];
159 offline_data_->scalar_partitioner()->global_to_local(
163 const unsigned int row_length = sparsity_simd.row_length(index);
167 if (index >= n_owned)
170 const Number interior_mass =
171 offline_data_->lumped_mass_matrix().local_element(index);
173 preliminary_map[index] = {index, interior_mass, position};
181 for (
const auto &[index, tuple] : preliminary_map) {
182 map.push_back(tuple);
185 return std::make_pair(name, map);
197 boundary_maps_.clear();
199 boundary_manifolds_.begin(),
200 boundary_manifolds_.end(),
201 std::inserter(boundary_maps_, boundary_maps_.end()),
202 [
this, n_owned](
auto it) {
203 const auto &[name, expression, option] = it;
204 FunctionParser<dim> level_set_function(expression);
206 std::vector<boundary_point> map;
208 for (const auto &entry : offline_data_->boundary_map()) {
210 const auto &i = std::get<0>(entry);
217 if (offline_data_->affine_constraints().is_constrained(
218 offline_data_->scalar_partitioner()->local_to_global(i)))
221 const auto &position = std::get<5>(entry);
222 if (std::abs(level_set_function.value(position)) < 1.e-12)
223 map.push_back(entry);
225 return std::make_pair(name, map);
232 mesh_files_have_been_written_ =
false;
235 const auto &names = View::primitive_component_names;
236 header_ = std::accumulate(
240 [](
const std::string &description,
const std::string &name) {
241 return description.empty()
242 ? (std::string(
"primitive state (") + name)
243 : (description +
", " + name);
245 ")\t and 2nd moments\n";
249 template <
typename Description,
int dim,
typename Number>
251 Quantities<Description, dim, Number>::write_mesh_files(
unsigned int cycle)
257 for (
const auto &[name, interior_map] : interior_maps_) {
259 const auto &options = get_options_from_name(interior_manifolds_, name);
260 if (options.find(
"instantaneous") == std::string::npos &&
261 options.find(
"time_averaged") == std::string::npos)
270 const auto received = Utilities::MPI::gather(
271 mpi_ensemble_.ensemble_communicator(), interior_map);
273 if (Utilities::MPI::this_mpi_process(
274 mpi_ensemble_.ensemble_communicator()) == 0) {
276 std::ofstream output(base_name_ +
"-" + name +
"-R" +
277 Utilities::to_string(cycle, 4) +
"-points.dat");
279 output << std::scientific << std::setprecision(14);
281 output <<
"#\n# position\tinterior mass\n";
283 unsigned int rank = 0;
284 for (
const auto &entries : received) {
285 output <<
"# rank " << rank++ <<
"\n";
286 for (
const auto &entry : entries) {
287 const auto &[index, mass_i, x_i] = entry;
288 output << x_i <<
"\t" << mass_i <<
"\n";
292 output << std::flush;
300 for (
const auto &[name, boundary_map] : boundary_maps_) {
302 const auto &options = get_options_from_name(boundary_manifolds_, name);
303 if (options.find(
"instantaneous") == std::string::npos &&
304 options.find(
"time_averaged") == std::string::npos)
313 const auto received = Utilities::MPI::gather(
314 mpi_ensemble_.ensemble_communicator(), boundary_map);
316 if (Utilities::MPI::this_mpi_process(
317 mpi_ensemble_.ensemble_communicator()) == 0) {
319 std::ofstream output(base_name_ +
"-" + name +
"-R" +
320 Utilities::to_string(cycle, 4) +
"-points.dat");
322 output << std::scientific << std::setprecision(14);
324 output <<
"#\n# position\tnormal\tnormal mass\tboundary mass\n";
326 unsigned int rank = 0;
327 for (
const auto &entries : received) {
328 output <<
"# rank " << rank++ <<
"\n";
329 for (
const auto &entry : entries) {
330 const auto &[index, n_i, nm_i, bm_i, id, x_i] = entry;
331 output << x_i <<
"\t" << n_i <<
"\t" << nm_i <<
"\t" << bm_i
336 output << std::flush;
342 template <
typename Description,
int dim,
typename Number>
343 void Quantities<Description, dim, Number>::clear_statistics()
345 const auto reset = [](
const auto &manifold_map,
auto &statistics_map) {
346 for (
const auto &[name, data_map] : manifold_map) {
347 const auto n_entries = data_map.size();
348 auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =
349 statistics_map[name];
350 val_old.resize(n_entries);
351 val_new.resize(n_entries);
352 val_sum.resize(n_entries);
353 t_old = t_new = t_sum = 0.;
359 interior_statistics_.clear();
360 reset(interior_maps_, interior_statistics_);
361 interior_time_series_.clear();
363 boundary_statistics_.clear();
364 reset(boundary_maps_, boundary_statistics_);
365 boundary_time_series_.clear();
369 template <
typename Description,
int dim,
typename Number>
370 template <
typename po
int_type,
typename value_type>
371 value_type Quantities<Description, dim, Number>::internal_accumulate(
373 const std::vector<point_type> &points_vector,
374 std::vector<value_type> &val_new)
376 const auto &U = std::get<0>(state_vector);
378 value_type spatial_average;
379 Number mass_sum = Number(0.);
382 points_vector.begin(),
385 [&](
auto point) -> value_type {
386 const auto i = std::get<0>(point);
391 constexpr auto index =
392 std::is_same_v<point_type, interior_point> ? 1 : 3;
393 const auto mass_i = std::get<index>(point);
395 const auto U_i = U.get_tensor(i);
396 const auto view = hyperbolic_system_->template view<dim, Number>();
397 const auto primitive_state = view.to_primitive_state(U_i);
400 std::get<0>(result) = primitive_state;
402 std::get<1>(result) = schur_product(primitive_state, primitive_state);
405 std::get<0>(spatial_average) += mass_i * std::get<0>(result);
406 std::get<1>(spatial_average) += mass_i * std::get<1>(result);
414 Utilities::MPI::sum(mass_sum, mpi_ensemble_.ensemble_communicator());
416 std::get<0>(spatial_average) = Utilities::MPI::sum(
417 std::get<0>(spatial_average), mpi_ensemble_.ensemble_communicator());
418 std::get<1>(spatial_average) = Utilities::MPI::sum(
419 std::get<1>(spatial_average), mpi_ensemble_.ensemble_communicator());
423 std::get<0>(spatial_average) /= mass_sum;
424 std::get<1>(spatial_average) /= mass_sum;
426 return spatial_average;
430 template <
typename Description,
int dim,
typename Number>
431 template <
typename value_type>
432 void Quantities<Description, dim, Number>::internal_write_out(
433 const std::string &file_name,
434 const std::string &time_stamp,
435 const std::vector<value_type> &values,
444 const auto received =
445 Utilities::MPI::gather(mpi_ensemble_.ensemble_communicator(), values);
447 if (Utilities::MPI::this_mpi_process(
448 mpi_ensemble_.ensemble_communicator()) == 0) {
450 std::ofstream output(file_name);
451 output << std::scientific << std::setprecision(14);
452 output << time_stamp <<
"# " << header_;
454 unsigned int rank = 0;
455 for (
const auto &entries : received) {
456 output <<
"# rank " << rank++ <<
"\n";
457 for (
const auto &entry : entries) {
458 const auto &[state, state_square] = entry;
459 output << scale * state <<
"\t" << scale * state_square <<
"\n";
463 output << std::flush;
468 template <
typename Description,
int dim,
typename Number>
469 template <
typename value_type>
470 void Quantities<Description, dim, Number>::internal_write_out_time_series(
471 const std::string &file_name,
472 const std::vector<std::tuple<Number, value_type>> &values,
475 if (Utilities::MPI::this_mpi_process(
476 mpi_ensemble_.ensemble_communicator()) == 0) {
477 std::ofstream output;
478 output << std::scientific << std::setprecision(14);
481 output.open(file_name, std::ofstream::out | std::ofstream::app);
483 output.open(file_name, std::ofstream::out | std::ofstream::trunc);
484 output <<
"# time t\t" << header_;
487 for (
const auto &entry : values) {
488 const auto t = std::get<0>(entry);
489 const auto &[state, state_square] = std::get<1>(entry);
491 output << t <<
"\t" << state <<
"\t" << state_square <<
"\n";
494 output << std::flush;
500 template <
typename Description,
int dim,
typename Number>
505 std::cout <<
"Quantities<dim, Number>::accumulate()" << std::endl;
508 const auto accumulate = [&](
const auto &point_maps,
509 const auto &manifolds,
512 for (
const auto &[name, point_map] : point_maps) {
515 const auto &options = get_options_from_name(manifolds, name);
518 if (options.find(
"time_averaged") == std::string::npos &&
519 options.find(
"space_averaged") == std::string::npos)
522 auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =
525 std::swap(t_old, t_new);
526 std::swap(val_old, val_new);
530 const auto spatial_average =
531 internal_accumulate(state_vector, point_map, val_new);
543 const Number tau = t_new - t_old;
545 for (std::size_t i = 0; i < val_sum.size(); ++i) {
546 std::get<0>(val_sum[i]) += 0.5 * tau * std::get<0>(val_old[i]);
547 std::get<0>(val_sum[i]) += 0.5 * tau * std::get<0>(val_new[i]);
548 std::get<1>(val_sum[i]) += 0.5 * tau * std::get<1>(val_old[i]);
549 std::get<1>(val_sum[i]) += 0.5 * tau * std::get<1>(val_new[i]);
555 time_series[name].push_back({t, spatial_average});
559 accumulate(interior_maps_,
561 interior_statistics_,
562 interior_time_series_);
564 accumulate(boundary_maps_,
566 boundary_statistics_,
567 boundary_time_series_);
571 template <
typename Description,
int dim,
typename Number>
573 const StateVector &state_vector,
const Number t,
unsigned int cycle)
576 std::cout <<
"Quantities<dim, Number>::write_out()" << std::endl;
582 if (!mesh_files_have_been_written_) {
583 write_mesh_files(cycle);
584 mesh_files_have_been_written_ =
true;
592 const auto write_out = [&](
const auto &point_maps,
593 const auto &manifolds,
596 for (
const auto &[name, point_map] : point_maps) {
599 const auto &options = get_options_from_name(manifolds, name);
602 base_name_ +
"-" + name +
"-R" + Utilities::to_string(cycle, 4);
608 if (options.find(
"instantaneous") != std::string::npos) {
610 const std::string file_name = prefix +
"-instantaneous.dat";
612 auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =
615 std::stringstream time_stamp;
616 time_stamp << std::scientific << std::setprecision(14);
617 time_stamp <<
"# at t = " << t << std::endl;
621 if (options.find(
"time_averaged") == std::string::npos &&
622 options.find(
"space_averaged") == std::string::npos)
623 internal_accumulate(state_vector, point_map, val_new);
625 AssertThrow(t_new == t, dealii::ExcInternalError());
627 internal_write_out(file_name, time_stamp.str(), val_new, Number(1.));
634 if (options.find(
"time_averaged") != std::string::npos) {
636 const std::string file_name = prefix +
"-time_averaged.dat";
638 auto &[val_old, val_new, val_sum, t_old, t_new, t_sum] =
642 if (t_sum != Number(0.)) {
643 std::stringstream time_stamp;
644 time_stamp << std::scientific << std::setprecision(14);
645 time_stamp <<
"# averaged from t = " << t_new - t_sum
646 <<
" to t = " << t_new << std::endl;
649 file_name, time_stamp.str(), val_sum, Number(1.) / t_sum);
657 if (options.find(
"space_averaged") != std::string::npos) {
659 if (!time_series_cycle_.has_value()) {
660 time_series_cycle_ = cycle;
664 const auto file_name =
665 base_name_ +
"-" + name +
"-R" +
666 Utilities::to_string(time_series_cycle_.value(), 4) +
667 "-space_averaged_time_series.dat";
669 auto &series = time_series[name];
670 internal_write_out_time_series(file_name, series, append);
676 write_out(interior_maps_,
678 interior_statistics_,
679 interior_time_series_);
681 write_out(boundary_maps_,
683 boundary_statistics_,
684 boundary_time_series_);
686 if (clear_temporal_statistics_on_writeout_)
typename Description::HyperbolicSystem HyperbolicSystem
typename Description::ParabolicSystem ParabolicSystem
typename View::StateVector StateVector
Quantities(const MPIEnsemble &mpi_ensemble, const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const ParabolicSystem ¶bolic_system, const std::string &subsection="/Quantities")
#define RYUJIN_UNLIKELY(x)
std::tuple< MultiComponentVector< Number, problem_dim >, MultiComponentVector< Number, prec_dim >, BlockVector< Number > > StateVector
DEAL_II_NAMESPACE_OPEN bool operator<(const Tensor< rank, dim, Number > &left, const Tensor< rank, dim, Number > &right)