SemiDiscreteOT 1.0
Semi-Discrete Optimal Transport Library
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1#ifndef UTILS_H
2#define UTILS_H
3
4#include <vector>
5#include <string>
6#include <fstream>
7#include <iostream>
8#include <filesystem>
9#include <iomanip>
10#include <limits>
11#include <deal.II/grid/grid_out.h>
12#include <deal.II/numerics/data_out.h>
13#include <deal.II/grid/grid_in.h>
14#include <deal.II/grid/grid_generator.h>
15#include <deal.II/grid/grid_tools.h>
16#include <deal.II/distributed/fully_distributed_tria.h>
17#include <deal.II/dofs/dof_handler.h>
18#include <deal.II/base/utilities.h>
19#include <deal.II/base/conditional_ostream.h>
20#include <deal.II/base/tensor.h>
21#include <deal.II/fe/fe_values.h>
22#include <deal.II/fe/fe_simplex_p.h>
23#include <deal.II/fe/fe_q.h>
24#include <deal.II/fe/mapping_fe.h>
25#include <deal.II/fe/mapping_q1.h>
26#include <deal.II/lac/vector.h>
27#include <deal.II/lac/la_parallel_vector.h>
29
34namespace Utils {
35
42inline std::string to_scientific_string(double value, int precision = 6) {
43 std::ostringstream oss;
44 oss << std::scientific << std::setprecision(precision) << value;
45 return oss.str();
46}
47
55template<typename VectorContainer>
56void write_vector(const VectorContainer& points,
57 const std::string& filepath,
58 const std::string& fileMode = "txt") {
59 std::ofstream file;
60
61 // Create directories if they don't exist
62 size_t pos = filepath.find_last_of("/\\");
63 if (pos != std::string::npos) {
64 std::string dir = filepath.substr(0, pos);
65 #ifdef _WIN32
66 system(("mkdir " + dir + " 2>nul").c_str());
67 #else
68 system(("mkdir -p " + dir).c_str());
69 #endif
70 }
71
72 if (fileMode == "bin") {
73 std::cout << "Writing: " << filepath << ".bin" << std::endl;
74 file.open(filepath + ".bin", std::ios::binary);
75 if (!file.is_open()) {
76 std::cerr << "Error: Unable to open file for writing in binary mode." << std::endl;
77 return;
78 }
79 for (const auto& point : points) {
80 file.write(reinterpret_cast<const char*>(&point), sizeof(point));
81 }
82 } else if (fileMode == "txt") {
83 std::cout << "Writing: " << filepath << ".txt" << std::endl;
84 file.open(filepath + ".txt");
85 if (!file.is_open()) {
86 std::cerr << "Error: Unable to open file for writing in text mode." << std::endl;
87 return;
88 }
89 file << std::setprecision(std::numeric_limits<double>::max_digits10);
90 for (const auto& point : points) {
91 file << point << std::endl;
92 }
93 } else {
94 std::cerr << "Error: Invalid file mode specified." << std::endl;
95 }
96 file.close();
97}
98
107template<typename VectorContainer>
108bool read_vector(VectorContainer& points,
109 const std::string& filepath,
110 const std::string& fileMode = "") {
111 std::string actualFilepath = filepath;
112 std::string actualFileMode = fileMode;
113
114 // If fileMode is not provided, infer it from the filepath extension
115 if (actualFileMode.empty()) {
116 size_t dotPos = filepath.find_last_of('.');
117 if (dotPos != std::string::npos) {
118 std::string extension = filepath.substr(dotPos + 1);
119 if (extension == "txt" || extension == "bin") {
120 actualFileMode = extension;
121 // Use the filepath as is since it already has the extension
122 actualFilepath = filepath;
123 } else {
124 // Unknown extension, default to txt
125 actualFileMode = "txt";
126 actualFilepath = filepath + ".txt";
127 }
128 } else {
129 // No extension found, default to txt
130 actualFileMode = "txt";
131 actualFilepath = filepath + ".txt";
132 }
133 } else {
134 // File mode is specified, append extension if not already present
135 if (filepath.find('.' + actualFileMode) != filepath.length() - actualFileMode.length() - 1) {
136 actualFilepath = filepath + '.' + actualFileMode;
137 }
138 }
139
140 std::ifstream file;
141
142 if (actualFileMode == "bin") {
143 file.open(actualFilepath, std::ios::binary);
144 if (!file.is_open()) {
145 std::cerr << "Error: Unable to open file for reading: " << actualFilepath << std::endl;
146 return false;
147 }
148
149 file.seekg(0, std::ios::end);
150 std::streamsize size = file.tellg();
151 file.seekg(0, std::ios::beg);
152 using value_type = typename VectorContainer::value_type;
153 points.resize(size / sizeof(value_type));
154 file.read(reinterpret_cast<char*>(points.data()), size);
155
156 } else if (actualFileMode == "txt") {
157 file.open(actualFilepath);
158 if (!file.is_open()) {
159 std::cerr << "Error: Unable to open file for reading: " << actualFilepath << std::endl;
160 return false;
161 }
162
163 points.clear();
164 typename VectorContainer::value_type value;
165 while (file >> value) {
166 points.push_back(value);
167 }
168
169 } else {
170 std::cerr << "Error: Invalid file mode specified: " << actualFileMode << std::endl;
171 return false;
172 }
173
174 file.close();
175 return true;
176}
177
184inline std::vector<std::string> select_folder(const std::string& base_dir = "output", bool allow_all = true) {
185 std::vector<std::string> epsilon_folders;
186
187 // List all epsilon folders and exact_sot
188 for (const auto& entry : std::filesystem::directory_iterator(base_dir)) {
189 if (entry.is_directory()) {
190 std::string folder_name = entry.path().filename().string();
191 if (folder_name.find("epsilon_") == 0 || folder_name == "exact_sot") {
192 epsilon_folders.push_back(folder_name);
193 }
194 }
195 }
196
197 if (epsilon_folders.empty()) {
198 throw std::runtime_error("No epsilon or exact_sot folders found in " + base_dir);
199 }
200
201 // Sort folders to ensure consistent ordering
202 std::sort(epsilon_folders.begin(), epsilon_folders.end());
203
204 // Print available options
205 std::cout << "\nAvailable folders:\n";
206 for (size_t i = 0; i < epsilon_folders.size(); ++i) {
207 std::cout << "[" << i + 1 << "] " << epsilon_folders[i] << "\n";
208 }
209 if (allow_all) {
210 std::cout << "[" << epsilon_folders.size() + 1 << "] ALL FOLDERS\n";
211 }
212
213 // Get user selection
214 size_t selection;
215 while (true) {
216 std::cout << "\nSelect a folder (1-" << (allow_all ? epsilon_folders.size() + 1 : epsilon_folders.size()) << "): ";
217 if (std::cin >> selection && selection >= 1 &&
218 selection <= (allow_all ? epsilon_folders.size() + 1 : epsilon_folders.size())) {
219 break;
220 }
221 std::cout << "Invalid selection. Please try again.\n";
222 std::cin.clear();
223 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
224 }
225
226 // Return all folders if "ALL" was selected, otherwise return a vector with just the selected folder
227 if (allow_all && selection == epsilon_folders.size() + 1) {
228 return epsilon_folders;
229 }
230 return {epsilon_folders[selection - 1]};
231}
232
238inline std::vector<std::pair<std::string, std::string>> get_target_hierarchy_files(const std::string& dir)
239{
240 std::vector<std::pair<std::string, std::string>> files;
241
242 // Check if directory exists
243 if (!std::filesystem::exists(dir)) {
244 throw std::runtime_error("Target point cloud hierarchy directory does not exist: " + dir);
245 }
246
247 // Collect all level point files
248 for (const auto& entry : std::filesystem::directory_iterator(dir)) {
249 if (entry.path().filename().string().find("level_") == 0 &&
250 entry.path().filename().string().find("_points.txt") != std::string::npos) {
251 std::string points_file = entry.path().string();
252 points_file = points_file.substr(0, points_file.length() - 4);
253
254 std::string level_num = points_file.substr(
255 points_file.find("level_") + 6,
256 points_file.find("_points") - points_file.find("level_") - 6
257 );
258 std::string density_file = dir + "/level_" + level_num + "_density";
259
260 if (std::filesystem::exists(density_file + ".txt")) {
261 files.push_back({points_file, density_file});
262 }
263 }
264 }
265
266 // Sort in reverse order (coarsest to finest)
267 std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) {
268 int level_a = std::stoi(a.first.substr(a.first.find("level_") + 6, 1));
269 int level_b = std::stoi(b.first.substr(b.first.find("level_") + 6, 1));
270 return level_a > level_b;
271 });
272
273
274 return files;
275}
276
286template <int dim, int spacedim>
287bool load_hierarchy_data(const std::string& hierarchy_dir,
288 std::vector<std::vector<std::vector<size_t>>>& child_indices,
289 int specific_level,
290 const MPI_Comm& mpi_communicator,
291 dealii::ConditionalOStream& pcout)
292{
293 // Only rank 0 checks directory and counts levels
294 int num_levels = 0;
295 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {
296 while (true) {
297 std::string points_file = hierarchy_dir + "/level_" + std::to_string(num_levels) + "_points.txt";
298 if (!std::filesystem::exists(points_file)) {
299 break;
300 }
301 num_levels++;
302 }
303 }
304
305 num_levels = dealii::Utilities::MPI::broadcast(mpi_communicator, num_levels, 0);
306
307 if (num_levels == 0) {
308 pcout << "No hierarchy data found in " << hierarchy_dir << std::endl;
309 return false;
310 }
311
312 // If loading all levels, just resize and return
313 if (specific_level == -1) {
314 child_indices.resize(num_levels - 1);
315 return true;
316 }
317
318 if (specific_level >= num_levels - 1) {
319 return true; // No parent-child relationships for the last level
320 }
321
322 if (static_cast<int>(child_indices.size()) <= specific_level) {
323 child_indices.resize(num_levels - 1);
324 }
325
326 // Process 0 loads data
327 std::vector<std::vector<size_t>> level_data;
328 bool level_load_success = true;
329
330 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {
331 std::string children_file = hierarchy_dir + "/level_" + std::to_string(specific_level+1) + "_children.txt";
332 std::ifstream children_in(children_file);
333
334 if (!children_in) {
335 level_load_success = false;
336 } else {
337 std::string line;
338
339 while (std::getline(children_in, line)) {
340 std::istringstream iss(line);
341 int num_children;
342 iss >> num_children;
343
344 std::vector<size_t> children(num_children);
345
346 size_t child_idx;
347 int idx = 0;
348
349 while (idx < num_children && iss >> child_idx) {
350 children[idx] = child_idx;
351 ++idx;
352 }
353
354 level_data.push_back(children);
355 }
356 }
357 }
358
359 // Broadcast success status
360 level_load_success = dealii::Utilities::MPI::broadcast(mpi_communicator, level_load_success, 0);
361
362 if (!level_load_success) {
363 pcout << "Error loading hierarchy data at level " << specific_level << std::endl;
364 return false;
365 }
366
367 // First broadcast the size of the level data
368 unsigned int n_parents = 0;
369 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {
370 n_parents = level_data.size();
371 }
372 n_parents = dealii::Utilities::MPI::broadcast(mpi_communicator, n_parents, 0);
373
374 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) != 0) {
375 level_data.resize(n_parents);
376 }
377
378 for (unsigned int i = 0; i < n_parents; ++i) {
379 unsigned int n_children = 0;
380 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {
381 n_children = level_data[i].size();
382 }
383 n_children = dealii::Utilities::MPI::broadcast(mpi_communicator, n_children, 0);
384
385 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) != 0) {
386 level_data[i].resize(n_children);
387 }
388
389 // mpi broadcast level data[i]
390 MPI_Bcast(&level_data[i][0], n_children, MPI_UNSIGNED_LONG, 0, mpi_communicator);
391
392 }
393
394 child_indices[specific_level] = level_data;
395 return true;
396}
397
409template<int dim, int spacedim = dim>
410bool write_mesh(const dealii::Triangulation<dim, spacedim>& mesh,
411 const std::string& filepath,
412 const std::vector<std::string>& formats,
413 const std::vector<double>* cell_data = nullptr,
414 const std::string& data_name = "cell_data")
415{
416 try {
417 std::filesystem::path path(filepath);
418 std::filesystem::create_directories(path.parent_path());
419
420 dealii::GridOut grid_out;
421
422 for (const auto& format : formats) {
423 if (format == "vtk") {
424 std::ofstream out_vtk(filepath + ".vtk");
425 if (!out_vtk.is_open()) {
426 std::cerr << "Error: Unable to open file for writing: "
427 << filepath + ".vtk" << std::endl;
428 return false;
429 }
430 grid_out.write_vtk(mesh, out_vtk);
431 std::cout << "Mesh saved to: " << filepath + ".vtk" << std::endl;
432 }
433 else if (format == "msh") {
434 // First write in default format
435 std::ofstream out_msh(filepath + ".msh");
436 if (!out_msh.is_open()) {
437 std::cerr << "Error: Unable to open file for writing: "
438 << filepath + ".msh" << std::endl;
439 return false;
440 }
441 grid_out.write_msh(mesh, out_msh);
442 out_msh.close();
443
444 // Convert to MSH2 format using gmsh
445 std::string cmd = "gmsh " + filepath + ".msh -format msh2 -save_all -3 -o " +
446 filepath + "_msh2.msh && mv " + filepath + "_msh2.msh " +
447 filepath + ".msh";
448 int ret = system(cmd.c_str());
449 if (ret != 0) {
450 std::cerr << "Error: Failed to convert mesh to MSH2 format" << std::endl;
451 return false;
452 }
453 std::cout << "Mesh saved and converted to MSH2 format: " << filepath + ".msh" << std::endl;
454 }
455 else if (format == "vtu") {
456 dealii::DataOut<dim, spacedim> data_out;
457 data_out.attach_triangulation(mesh);
458
459 // Add cell data if provided
460 if (cell_data != nullptr) {
461 Assert(cell_data->size() == mesh.n_active_cells(),
462 dealii::ExcDimensionMismatch(cell_data->size(),
463 mesh.n_active_cells()));
464
465 dealii::Vector<double> cell_data_vector(cell_data->begin(), cell_data->end());
466 data_out.add_data_vector(cell_data_vector, data_name);
467 }
468
469 data_out.build_patches();
470
471 std::ofstream out_vtu(filepath + ".vtu");
472 if (!out_vtu.is_open()) {
473 std::cerr << "Error: Unable to open file for writing: "
474 << filepath + ".vtu" << std::endl;
475 return false;
476 }
477 data_out.write_vtu(out_vtu);
478 std::cout << "Mesh saved to: " << filepath + ".vtu" << std::endl;
479 }
480 else {
481 std::cerr << "Warning: Unsupported format '" << format
482 << "' requested" << std::endl;
483 }
484 }
485
486 return true;
487 }
488 catch (const std::exception& e) {
489 std::cerr << "Error writing mesh: " << e.what() << std::endl;
490 return false;
491 }
492}
493
503template <int dim, int spacedim>
505 const dealii::DoFHandler<dim, spacedim> &source_dh,
506 const dealii::Vector<double> &source_field,
507 const dealii::DoFHandler<dim, spacedim> &target_dh,
508 dealii::LinearAlgebra::distributed::Vector<double> &target_field)
509{
510 using namespace dealii;
511
512 Assert(source_field.size() == source_dh.n_dofs(),
513 ExcDimensionMismatch(source_field.size(), source_dh.n_dofs()));
514 Assert(target_field.size() == target_dh.n_dofs(),
515 ExcDimensionMismatch(target_field.size(), target_dh.n_dofs()));
516
517 const auto &source_fe = source_dh.get_fe();
518 const auto &target_fe = target_dh.get_fe();
519
520 std::unique_ptr<Mapping<dim, spacedim>> source_mapping;
521 if (source_fe.reference_cell() == ReferenceCells::get_hypercube<dim>())
522 source_mapping = std::make_unique<MappingQ1<dim, spacedim>>();
523 else
524 source_mapping = std::make_unique<MappingFE<dim, spacedim>>(FE_SimplexP<dim, spacedim>(1));
525
526 std::unique_ptr<Mapping<dim, spacedim>> target_mapping;
527 if (target_fe.reference_cell() == ReferenceCells::get_hypercube<dim>())
528 target_mapping = std::make_unique<MappingQ1<dim, spacedim>>();
529 else
530 target_mapping = std::make_unique<MappingFE<dim, spacedim>>(FE_SimplexP<dim, spacedim>(1));
531
532 const Quadrature<dim> target_quadrature(target_fe.get_generalized_support_points());
533 FEValues<dim, spacedim> target_fe_values(*target_mapping,
534 target_fe,
535 target_quadrature,
536 update_quadrature_points);
537
538 std::vector<types::global_dof_index> source_dof_indices(source_fe.n_dofs_per_cell());
539 std::vector<types::global_dof_index> target_dof_indices(target_fe.n_dofs_per_cell());
540 Vector<double> source_cell_values(source_fe.n_dofs_per_cell());
541
542 Vector<double> target_values(target_field.size());
543 Vector<double> weights(target_field.size());
544
545 std::vector<std::pair<Point<spacedim>, typename DoFHandler<dim, spacedim>::active_cell_iterator>> cell_centers;
546 for (const auto &cell : source_dh.active_cell_iterators())
547 {
548 if (!cell->is_locally_owned())
549 continue;
550 cell_centers.emplace_back(cell->center(), cell);
551 }
552
553 std::vector<std::pair<Point<spacedim>, unsigned int>> indexed_points;
554 indexed_points.reserve(cell_centers.size());
555 for (unsigned int i = 0; i < cell_centers.size(); ++i)
556 indexed_points.emplace_back(cell_centers[i].first, i);
557
558 using RTreeParams = boost::geometry::index::rstar<8>;
559 boost::geometry::index::rtree<std::pair<Point<spacedim>, unsigned int>, RTreeParams>
560 rtree(indexed_points.begin(), indexed_points.end());
561
562 for (const auto &target_cell : target_dh.active_cell_iterators())
563 {
564 if (!target_cell->is_locally_owned())
565 continue;
566
567 target_fe_values.reinit(target_cell);
568 const std::vector<Point<spacedim>> &target_points = target_fe_values.get_quadrature_points();
569 target_cell->get_dof_indices(target_dof_indices);
570
571 for (unsigned int q = 0; q < target_points.size(); ++q)
572 {
573 const Point<spacedim> &target_point = target_points[q];
574
575 std::vector<std::pair<Point<spacedim>, unsigned int>> nearest;
576 rtree.query(boost::geometry::index::nearest(target_point, 1), std::back_inserter(nearest));
577
578 if (nearest.empty())
579 continue;
580
581 const unsigned int nearest_index = nearest.front().second;
582 auto chosen_source_cell = cell_centers[nearest_index].second;
583
584 Point<dim> p_unit;
585 try
586 {
587 p_unit = source_mapping->transform_real_to_unit_cell(chosen_source_cell, target_point);
588 }
589 catch (...)
590 {
591 continue;
592 }
593
594 chosen_source_cell->get_dof_indices(source_dof_indices);
595 for (unsigned int i = 0; i < source_dof_indices.size(); ++i)
596 source_cell_values[i] = source_field[source_dof_indices[i]];
597
598 double source_value = 0.0;
599 for (unsigned int i = 0; i < source_fe.n_dofs_per_cell(); ++i)
600 source_value += source_cell_values[i] * source_fe.shape_value(i, p_unit);
601
602 const unsigned int target_dof = target_dof_indices[q];
603 target_values[target_dof] += source_value;
604 weights[target_dof] += 1.0;
605 }
606 }
607
608 for (types::global_dof_index i = 0; i < target_field.size(); ++i)
609 if (weights[i] > 0)
610 target_field[i] = target_values[i] / weights[i];
611
612 target_field.compress(VectorOperation::insert);
613}
614
624template <int dim, int spacedim>
625void interpolate_non_conforming(const dealii::DoFHandler<dim, spacedim> &source_dh,
626 const dealii::Vector<double> &source_field,
627 const dealii::DoFHandler<dim, spacedim> &target_dh,
628 dealii::LinearAlgebra::distributed::Vector<double> &target_field)
629{
630 using namespace dealii;
631
632 Assert(source_field.size() == source_dh.n_dofs(),
633 ExcDimensionMismatch(source_field.size(), source_dh.n_dofs()));
634 Assert(target_field.size() == target_dh.n_dofs(),
635 ExcDimensionMismatch(target_field.size(), target_dh.n_dofs()));
636
637 const auto &source_fe = source_dh.get_fe();
638 const auto &target_fe = target_dh.get_fe();
639
640 std::unique_ptr<Mapping<dim, spacedim>> source_mapping;
641 if (source_dh.get_fe().reference_cell() == ReferenceCells::get_hypercube<dim>())
642 source_mapping = std::make_unique<MappingQ1<dim, spacedim>>();
643 else
644 source_mapping = std::make_unique<MappingFE<dim, spacedim>>(FE_SimplexP<dim, spacedim>(1));
645
646 std::unique_ptr<Mapping<dim, spacedim>> target_mapping;
647 if (target_dh.get_fe().reference_cell() == ReferenceCells::get_hypercube<dim>())
648 target_mapping = std::make_unique<MappingQ1<dim, spacedim>>();
649 else
650 target_mapping = std::make_unique<MappingFE<dim, spacedim>>(FE_SimplexP<dim, spacedim>(1));
651
652 std::vector<std::pair<Point<spacedim>, typename DoFHandler<dim, spacedim>::active_cell_iterator>>
653 cell_centers;
654
655 for (const auto &cell : source_dh.active_cell_iterators())
656 {
657 if (!cell->is_locally_owned())
658 continue;
659 cell_centers.emplace_back(cell->center(), cell);
660 }
661
662 std::vector<std::pair<Point<spacedim>, unsigned int>> indexed_points;
663 indexed_points.reserve(cell_centers.size());
664 for (unsigned int i = 0; i < cell_centers.size(); ++i)
665 indexed_points.emplace_back(cell_centers[i].first, i);
666
667 using RTreeParams = boost::geometry::index::rstar<8>;
668 boost::geometry::index::rtree<std::pair<Point<spacedim>, unsigned int>, RTreeParams>
669 rtree(indexed_points.begin(), indexed_points.end());
670
671 const Quadrature<dim> quadrature(target_fe.get_unit_support_points());
672
673 FEValues<dim> source_fe_values(*source_mapping,
674 source_fe,
675 quadrature,
676 update_values | update_quadrature_points);
677
678 FEValues<dim> target_fe_values(*target_mapping,
679 target_fe,
680 quadrature,
681 update_quadrature_points);
682
683 std::vector<types::global_dof_index> source_dof_indices(source_fe.n_dofs_per_cell());
684 std::vector<types::global_dof_index> target_dof_indices(target_fe.n_dofs_per_cell());
685 Vector<double> source_cell_values(source_fe.n_dofs_per_cell());
686
687 Vector<double> target_values(target_dh.n_dofs());
688 Vector<double> weights(target_dh.n_dofs());
689
690 for (const auto &target_cell : target_dh.active_cell_iterators())
691 {
692 if (!target_cell->is_locally_owned())
693 continue;
694
695 target_fe_values.reinit(target_cell);
696 const std::vector<Point<spacedim>> &target_points = target_fe_values.get_quadrature_points();
697 target_cell->get_dof_indices(target_dof_indices);
698
699 for (unsigned int q = 0; q < target_points.size(); ++q)
700 {
701 const Point<spacedim> &target_point = target_points[q];
702
703 std::vector<std::pair<Point<spacedim>, unsigned int>> nearest;
704 rtree.query(boost::geometry::index::nearest(target_point, 1),
705 std::back_inserter(nearest));
706
707 Assert(!nearest.empty(), ExcInternalError("RTree nearest neighbor search failed"));
708
709 const auto &source_cell = cell_centers[nearest[0].second].second;
710
711 source_fe_values.reinit(source_cell);
712 source_cell->get_dof_indices(source_dof_indices);
713
714 for (unsigned int i = 0; i < source_dof_indices.size(); ++i)
715 source_cell_values[i] = source_field[source_dof_indices[i]];
716
717 double source_value = 0;
718 for (unsigned int i = 0; i < source_fe.n_dofs_per_cell(); ++i)
719 source_value += source_cell_values[i] * source_fe_values.shape_value(i, q);
720
721 const unsigned int target_dof = target_dof_indices[q];
722 target_values[target_dof] += source_value;
723 weights[target_dof] += 1.0;
724 }
725 }
726
727 for (types::global_dof_index i = 0; i < target_dh.n_dofs(); ++i)
728 if (weights[i] > 0)
729 target_field[i] = target_values[i] / weights[i];
730
731 target_field.compress(VectorOperation::insert);
732}
733
742template <int dim, int spacedim = dim>
743std::unique_ptr<dealii::FiniteElement<dim, spacedim>> create_fe_for_mesh(
744 const dealii::Triangulation<dim, spacedim>& triangulation,
745 const unsigned int degree = 1)
746{
747 bool has_quads_or_hexes = false;
748 bool has_triangles_or_tets = false;
749
750 for (const auto& cell : triangulation.active_cell_iterators()) {
751 if (cell->reference_cell() == dealii::ReferenceCells::get_hypercube<dim>())
752 has_quads_or_hexes = true;
753 if (cell->reference_cell() == dealii::ReferenceCells::get_simplex<dim>())
754 has_triangles_or_tets = true;
755 }
756
757 if (has_triangles_or_tets && !has_quads_or_hexes) {
758 return std::make_unique<dealii::FE_SimplexP<dim, spacedim>>(degree);
759 } else if (has_quads_or_hexes && !has_triangles_or_tets) {
760 return std::make_unique<dealii::FE_Q<dim, spacedim>>(degree);
761 } else {
762 throw std::runtime_error("Mixed cell types or no cells found in triangulation");
763 }
764}
765
775template<int dim, int spacedim>
776std::pair<std::unique_ptr<dealii::FiniteElement<dim, spacedim>>,
777 std::unique_ptr<dealii::Mapping<dim, spacedim>>>
779 const dealii::Triangulation<dim, spacedim>& triangulation,
780 const unsigned int fe_degree = 1,
781 const unsigned int mapping_degree = 1)
782{
783 bool has_quads_or_hexes = false;
784 bool has_triangles_or_tets = false;
785
786 for (const auto& cell : triangulation.active_cell_iterators()) {
787 if (cell->reference_cell() == dealii::ReferenceCells::get_hypercube<dim>())
788 has_quads_or_hexes = true;
789 if (cell->reference_cell() == dealii::ReferenceCells::get_simplex<dim>())
790 has_triangles_or_tets = true;
791 }
792
793 std::unique_ptr<dealii::FiniteElement<dim, spacedim>> fe;
794 std::unique_ptr<dealii::Mapping<dim, spacedim>> mapping;
795
796 if (has_triangles_or_tets) {
797 fe = std::make_unique<dealii::FE_SimplexP<dim, spacedim>>(fe_degree);
798 mapping = std::make_unique<dealii::MappingFE<dim, spacedim>>(dealii::FE_SimplexP<dim, spacedim>(mapping_degree));
799 } else if (has_quads_or_hexes) {
800 fe = std::make_unique<dealii::FE_Q<dim, spacedim>>(fe_degree);
801 mapping = std::make_unique<dealii::MappingQ1<dim, spacedim>>();
802 } else {
803 throw std::runtime_error("Could not determine mesh cell type in triangulation");
804 }
805
806 return {std::move(fe), std::move(mapping)};
807}
808
817template <int dim, int spacedim = dim>
818std::unique_ptr<dealii::Quadrature<dim>> create_quadrature_for_mesh(
819 const dealii::Triangulation<dim, spacedim>& triangulation,
820 const unsigned int order = 2)
821{
822 bool has_quads_or_hexes = false;
823 bool has_triangles_or_tets = false;
824
825 for (const auto& cell : triangulation.active_cell_iterators()) {
826 if (cell->reference_cell() == dealii::ReferenceCells::get_hypercube<dim>())
827 has_quads_or_hexes = true;
828 if (cell->reference_cell() == dealii::ReferenceCells::get_simplex<dim>())
829 has_triangles_or_tets = true;
830 }
831
832 if (has_triangles_or_tets) {
833 return std::make_unique<dealii::QGaussSimplex<dim>>(order);
834 } else if (has_quads_or_hexes) {
835 return std::make_unique<dealii::QGauss<dim>>(order);
836 } else {
837 throw std::runtime_error("Could not determine mesh cell type for quadrature creation");
838 }
839}
840
851template <int spacedim>
853 const std::vector<dealii::Point<spacedim>>& points,
854 const std::vector<double>& density,
855 const std::string& filename,
856 const std::string& description = "Points with density values",
857 const std::string& density_name = "density")
858{
859 if (points.size() != density.size()) {
860 std::cerr << "Error: Points and density vectors must have the same size" << std::endl;
861 return false;
862 }
863
864 if (points.empty()) {
865 std::cerr << "Error: Cannot write empty point set" << std::endl;
866 return false;
867 }
868
869 // Create directories if they don't exist
870 std::filesystem::path path(filename);
871 std::filesystem::create_directories(path.parent_path());
872
873 std::ofstream vtk_file(filename);
874 if (!vtk_file.is_open()) {
875 std::cerr << "Error: Unable to open file for writing: " << filename << std::endl;
876 return false;
877 }
878
879 // Write VTK header
880 vtk_file << "# vtk DataFile Version 3.0\n"
881 << description << "\n"
882 << "ASCII\n"
883 << "DATASET UNSTRUCTURED_GRID\n"
884 << "POINTS " << points.size() << " double\n";
885
886 // Write point coordinates
887 for (const auto& p : points) {
888 vtk_file << p[0] << " " << p[1];
889 if (spacedim == 3) {
890 vtk_file << " " << p[2];
891 } else if (spacedim == 2) {
892 vtk_file << " 0.0"; // Add z=0 for 2D
893 }
894 vtk_file << "\n";
895 }
896
897 // Write cells (point cells)
898 const unsigned int n_points = points.size();
899 vtk_file << "CELLS " << n_points << " " << 2 * n_points << "\n";
900 for (unsigned int i = 0; i < n_points; ++i) {
901 vtk_file << "1 " << i << "\n";
902 }
903
904 // Write cell types
905 vtk_file << "CELL_TYPES " << n_points << "\n";
906 for (unsigned int i = 0; i < n_points; ++i) {
907 vtk_file << "1\n"; // VTK_VERTEX
908 }
909
910 // Write density as scalar data
911 vtk_file << "POINT_DATA " << n_points << "\n"
912 << "SCALARS " << density_name << " double 1\n"
913 << "LOOKUP_TABLE default\n";
914 for (std::size_t i = 0; i < n_points; ++i) {
915 vtk_file << density[i] << "\n";
916 }
917
918 vtk_file.close();
919
920 std::cout << "Points with density saved to: " << filename << std::endl;
921 return true;
922}
923
935template <int spacedim>
937 const std::vector<dealii::Point<spacedim>>& source_points,
938 const std::vector<dealii::Point<spacedim>>& mapped_points,
939 const std::vector<double>& density,
940 const std::string& filename,
941 const std::string& description = "Points with displacement vectors",
942 const std::string& density_name = "density")
943{
944 if (source_points.size() != mapped_points.size() ||
945 source_points.size() != density.size()) {
946 std::cerr << "Error: Source points, mapped points, and density vectors must have the same size" << std::endl;
947 return false;
948 }
949
950 if (source_points.empty()) {
951 std::cerr << "Error: Cannot write empty point set" << std::endl;
952 return false;
953 }
954
955 // Create directories if they don't exist
956 std::filesystem::path path(filename);
957 std::filesystem::create_directories(path.parent_path());
958
959 std::ofstream vtk_file(filename);
960 if (!vtk_file.is_open()) {
961 std::cerr << "Error: Unable to open file for writing: " << filename << std::endl;
962 return false;
963 }
964
965 // Write VTK header
966 vtk_file << "# vtk DataFile Version 3.0\n"
967 << description << "\n"
968 << "ASCII\n"
969 << "DATASET UNSTRUCTURED_GRID\n"
970 << "POINTS " << source_points.size() << " double\n";
971
972 // Write source point coordinates
973 for (const auto& p : source_points) {
974 vtk_file << p[0] << " " << p[1];
975 if (spacedim == 3) {
976 vtk_file << " " << p[2];
977 } else if (spacedim == 2) {
978 vtk_file << " 0.0"; // Add z=0 for 2D
979 }
980 vtk_file << "\n";
981 }
982
983 // Write cells (point cells)
984 const unsigned int n_points = source_points.size();
985 vtk_file << "CELLS " << n_points << " " << 2 * n_points << "\n";
986 for (unsigned int i = 0; i < n_points; ++i) {
987 vtk_file << "1 " << i << "\n";
988 }
989
990 // Write cell types
991 vtk_file << "CELL_TYPES " << n_points << "\n";
992 for (unsigned int i = 0; i < n_points; ++i) {
993 vtk_file << "1\n"; // VTK_VERTEX
994 }
995
996 // Write point data
997 vtk_file << "POINT_DATA " << n_points << "\n";
998
999 // Write displacement vectors
1000 vtk_file << "VECTORS displacement double\n";
1001 for (std::size_t i = 0; i < n_points; ++i) {
1002 dealii::Tensor<1, spacedim> displacement = mapped_points[i] - source_points[i];
1003 vtk_file << displacement[0] << " " << displacement[1];
1004 if (spacedim == 3) {
1005 vtk_file << " " << displacement[2];
1006 } else if (spacedim == 2) {
1007 vtk_file << " 0.0"; // Add z=0 for 2D
1008 }
1009 vtk_file << "\n";
1010 }
1011
1012 // Write density as scalar data
1013 vtk_file << "SCALARS " << density_name << " double 1\n"
1014 << "LOOKUP_TABLE default\n";
1015 for (std::size_t i = 0; i < n_points; ++i) {
1016 vtk_file << density[i] << "\n";
1017 }
1018
1019 vtk_file.close();
1020
1021 std::cout << "Points with displacement and density saved to: " << filename << std::endl;
1022 return true;
1023}
1024
1038template <int dim, int spacedim = dim>
1040 const std::string& filename,
1041 dealii::DoFHandler<dim, spacedim>& vtk_dof_handler,
1042 dealii::Vector<double>& vtk_field,
1043 dealii::Triangulation<dim, spacedim>& vtk_tria,
1044 const MPI_Comm& mpi_communicator,
1045 dealii::ConditionalOStream& pcout,
1046 bool broadcast_field = false,
1047 const std::string& field_name = "normalized_density")
1048{
1049 bool success = false;
1050 // Clear any existing triangulation
1051 vtk_tria.clear();
1052
1053 // Read mesh from VTK file
1054 dealii::GridIn<dim, spacedim> grid_in;
1055 grid_in.attach_triangulation(vtk_tria);
1056
1057 std::ifstream vtk_file(filename);
1058 if (!vtk_file) {
1059 pcout << Color::red << Color::bold
1060 << "Error: Could not open VTK file: " << filename
1061 << Color::reset << std::endl;
1062 return false;
1063 }
1064
1065 // Read VTK file
1066 grid_in.read_vtk(vtk_file);
1067 pcout << "VTK mesh: " << vtk_tria.n_active_cells() << " cells" << std::endl;
1068
1069 // Create and store FE (must outlive DoFHandler usage)
1070 auto fe = create_fe_for_mesh(vtk_tria);
1071
1072 // Clear and reinitialize DoFHandler with triangulation
1073 vtk_dof_handler.clear();
1074 vtk_dof_handler.reinit(vtk_tria);
1075 vtk_dof_handler.distribute_dofs(*fe);
1076
1077 // Initialize field vector
1078 vtk_field.reinit(vtk_dof_handler.n_dofs());
1079
1080 // Only rank 0 reads the file
1081 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0) {
1082 try {
1083 // Read scalar field data
1084 std::ifstream vtk_reader(filename);
1085 std::string line;
1086 bool found_point_data = false;
1087 bool found_scalars = false;
1088 std::string found_field_name;
1089
1090 while (std::getline(vtk_reader, line)) {
1091 if (line.find("POINT_DATA") != std::string::npos) {
1092 found_point_data = true;
1093 }
1094
1095 if (found_point_data && line.find("SCALARS") != std::string::npos) {
1096 std::istringstream iss(line);
1097 std::string dummy;
1098 iss >> dummy >> found_field_name;
1099
1100 // Check if this is the field we're looking for
1101 if (found_field_name == field_name) {
1102 found_scalars = true;
1103 pcout << "Found scalar field: " << found_field_name << std::endl;
1104
1105 // Skip LOOKUP_TABLE line
1106 std::getline(vtk_reader, line);
1107
1108 // Read scalar values
1109 for (unsigned int i = 0; i < vtk_dof_handler.n_dofs(); ++i) {
1110 if (!(vtk_reader >> vtk_field[i])) {
1111 pcout << Color::red << Color::bold
1112 << "Error reading scalar field data from VTK"
1113 << Color::reset << std::endl;
1114 found_scalars = false;
1115 break;
1116 }
1117 }
1118 break;
1119 }
1120 }
1121 }
1122
1123 success = found_scalars;
1124 if (!success) {
1125 pcout << Color::red << Color::bold
1126 << "Error: Could not find scalar field '" << field_name << "' in VTK file"
1127 << Color::reset << std::endl;
1128 }
1129 } catch (const std::exception& e) {
1130 pcout << Color::red << Color::bold
1131 << "Exception during VTK file reading: " << e.what()
1132 << Color::reset << std::endl;
1133 success = false;
1134 }
1135 }
1136
1137 if (broadcast_field) {
1138 success = dealii::Utilities::MPI::broadcast(mpi_communicator, success, 0);
1139 if (success) {
1140 // For source case: Broadcast field data to all processes
1141 vtk_field = dealii::Utilities::MPI::broadcast(mpi_communicator, vtk_field, 0);
1142 }
1143 }
1144
1145 return success;
1146}
1147
1148}
1149#endif
const std::string reset
const std::string red
const std::string bold
Collection of utility functions for file I/O, mesh handling and data management.
void write_vector(const VectorContainer &points, const std::string &filepath, const std::string &fileMode="txt")
Write a vector container to a file in binary or text format.
Definition utils.h:56
std::unique_ptr< dealii::FiniteElement< dim, spacedim > > create_fe_for_mesh(const dealii::Triangulation< dim, spacedim > &triangulation, const unsigned int degree=1)
Detect cell types in a triangulation and create appropriate finite element.
Definition utils.h:743
bool write_mesh(const dealii::Triangulation< dim, spacedim > &mesh, const std::string &filepath, const std::vector< std::string > &formats, const std::vector< double > *cell_data=nullptr, const std::string &data_name="cell_data")
Write mesh to file in specified formats with optional cell data.
Definition utils.h:410
std::vector< std::pair< std::string, std::string > > get_target_hierarchy_files(const std::string &dir)
Get target point cloud hierarchy files.
Definition utils.h:238
std::unique_ptr< dealii::Quadrature< dim > > create_quadrature_for_mesh(const dealii::Triangulation< dim, spacedim > &triangulation, const unsigned int order=2)
Create appropriate quadrature for a triangulation based on cell types.
Definition utils.h:818
bool read_vector(VectorContainer &points, const std::string &filepath, const std::string &fileMode="")
Read a vector container from a file in binary or text format.
Definition utils.h:108
bool write_points_with_density_vtk(const std::vector< dealii::Point< spacedim > > &points, const std::vector< double > &density, const std::string &filename, const std::string &description="Points with density values", const std::string &density_name="density")
Write points with associated density to VTK file.
Definition utils.h:852
std::string to_scientific_string(double value, int precision=6)
Convert a double to a string in scientific notation.
Definition utils.h:42
void interpolate_non_conforming_nearest(const dealii::DoFHandler< dim, spacedim > &source_dh, const dealii::Vector< double > &source_field, const dealii::DoFHandler< dim, spacedim > &target_dh, dealii::LinearAlgebra::distributed::Vector< double > &target_field)
Interpolate a field from a source mesh to a target mesh using nearest neighbor approach.
Definition utils.h:504
std::pair< std::unique_ptr< dealii::FiniteElement< dim, spacedim > >, std::unique_ptr< dealii::Mapping< dim, spacedim > > > create_fe_and_mapping_for_mesh(const dealii::Triangulation< dim, spacedim > &triangulation, const unsigned int fe_degree=1, const unsigned int mapping_degree=1)
Create appropriate finite element and mapping for a triangulation.
Definition utils.h:778
bool load_hierarchy_data(const std::string &hierarchy_dir, std::vector< std::vector< std::vector< size_t > > > &child_indices, int specific_level, const MPI_Comm &mpi_communicator, dealii::ConditionalOStream &pcout)
Load hierarchy data from files.
Definition utils.h:287
bool read_vtk_field(const std::string &filename, dealii::DoFHandler< dim, spacedim > &vtk_dof_handler, dealii::Vector< double > &vtk_field, dealii::Triangulation< dim, spacedim > &vtk_tria, const MPI_Comm &mpi_communicator, dealii::ConditionalOStream &pcout, bool broadcast_field=false, const std::string &field_name="normalized_density")
Read a scalar field from a VTK file.
Definition utils.h:1039
bool write_points_with_displacement_vtk(const std::vector< dealii::Point< spacedim > > &source_points, const std::vector< dealii::Point< spacedim > > &mapped_points, const std::vector< double > &density, const std::string &filename, const std::string &description="Points with displacement vectors", const std::string &density_name="density")
Write points with displacement vectors and density to VTK file.
Definition utils.h:936
void interpolate_non_conforming(const dealii::DoFHandler< dim, spacedim > &source_dh, const dealii::Vector< double > &source_field, const dealii::DoFHandler< dim, spacedim > &target_dh, dealii::LinearAlgebra::distributed::Vector< double > &target_field)
Interpolate a field from a source mesh to a target mesh.
Definition utils.h:625
std::vector< std::string > select_folder(const std::string &base_dir="output", bool allow_all=true)
List available epsilon folders and allow user selection.
Definition utils.h:184