From f95434d369ec536118c6a2fc016a3573cda1dd6d Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 1 Apr 2026 15:34:54 +0200 Subject: [PATCH 01/11] add basic structure of OpenFOAM reader --- src/CMakeLists.txt | 73 ++-- src/t8_openfoam/t8_openfoam_reader.hxx | 486 +++++++++++++++++++++++++ 2 files changed, 526 insertions(+), 33 deletions(-) create mode 100644 src/t8_openfoam/t8_openfoam_reader.hxx diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a9a421db91..93f63e7fbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -229,7 +229,7 @@ target_compile_definitions( T8 PUBLIC T8_LIBS="${T8_LIBS}" ) install( FILES t8.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Note: Some of the created install directories will be empty, for example t8_cmesh/t8_cmesh_internal @@ -238,42 +238,42 @@ install( FILES # It seems like we cannot force CMake to not create the folder. # See https://gitlab.kitware.com/cmake/cmake/-/issues/19189 -install( DIRECTORY t8_helper_functions - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hxx" +install( DIRECTORY t8_helper_functions + DESTINATION PRIVATE_HEADER + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hxx" ) install( DIRECTORY t8_cmesh - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.h" PATTERN "*.hxx" PATTERN "t8_cmesh/t8_cmesh_internal*" EXCLUDE PATTERN "t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_connectivity.hxx" EXCLUDE PATTERN "t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_conn_*.hxx" EXCLUDE ) -install( DIRECTORY t8_data - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING +install( DIRECTORY t8_data + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hxx" ) -install( DIRECTORY t8_eclass - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING +install( DIRECTORY t8_eclass + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hxx" ) -install( DIRECTORY t8_element - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING +install( DIRECTORY t8_element + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hxx" ) install( DIRECTORY t8_forest - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hxx" PATTERN "t8_forest/t8_forest_balance.h" EXCLUDE @@ -289,27 +289,27 @@ install( DIRECTORY t8_geometry PATTERN "t8_geometry/t8_geometry_implementations/t8_geometry_cad.hxx" EXCLUDE ) install( DIRECTORY t8_misc - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hxx" ) -install( DIRECTORY t8_schemes - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING +install( DIRECTORY t8_schemes + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h" - PATTERN "*.hxx" + PATTERN "*.hxx" ) -install( DIRECTORY t8_types - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.hxx" +install( DIRECTORY t8_types + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hxx" PATTERN "*.h" ) -install( DIRECTORY t8_vtk +install( DIRECTORY t8_vtk DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.h" + FILES_MATCHING + PATTERN "*.h" PATTERN "*.hxx" PATTERN "t8_vtk/t8_with_vtk/t8_vtk_parallel.hxx" EXCLUDE PATTERN "t8_vtk/t8_with_vtk/t8_vtk_polydata.hxx" EXCLUDE @@ -318,6 +318,13 @@ install( DIRECTORY t8_vtk PATTERN "t8_vtk/t8_vtk_writer_helper.hxx" EXCLUDE ) +install( DIRECTORY t8_openfoam + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hxx" + PATTERN "*.h" +) + install( TARGETS T8 EXPORT ${PROJECT_NAME}-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx new file mode 100644 index 0000000000..6d126b1503 --- /dev/null +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -0,0 +1,486 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_openfoam_reader.hxx + * Class for reading OpenFOAM meshes into t8code forests. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct t8_openfoam_reader +{ + public: + using t8_path = std::filesystem::path; + + /** + * Reader for OpenFOAM cases. + * \param [in] foamfile Path to the *.foam file inside the OpenFOAM case directory. + * \param [in] comm The communicator to use for the obtained forest. + */ + t8_openfoam_reader (t8_path foamfile, sc_MPI_Comm comm) + : m_case_dir (get_case_dir (foamfile)), m_cmesh (nullptr), m_comm (comm) + { + SC_CHECK_ABORTF (std::filesystem::exists (foamfile), "ERROR: Foam file does not exist: %s", foamfile.c_str ()); + }; + + /** + * Reads the OpenFOAM mesh. + * \note: When this class is finished it will return a forest instead of a cmesh. + * This is necessary to link the mesh data to the forest and adaptation and interpolation of the data can be performed. + * \return A cmesh containing the OpenFOAM mesh. + */ + t8_cmesh_t + read () + { + + /* Build paths to the standard OpenFOAM mesh files. */ + const t8_path case_faces_dir = m_case_dir / "constant/polyMesh/faces"; + const t8_path case_boundary_dir = m_case_dir / "constant/polyMesh/boundary"; + const t8_path case_neighbor_dir = m_case_dir / "constant/polyMesh/neighbour"; + const t8_path case_owner_dir = m_case_dir / "constant/polyMesh/owner"; + const t8_path case_points_dir = m_case_dir / "constant/polyMesh/points"; + + /* The file reading needs to happen in this order, since these functions depend on each other. */ + bool error = 0; + error = !read_points (case_points_dir); + error &= !read_faces (case_faces_dir); + error &= !read_owner (case_owner_dir); + error &= !read_neighbor (case_neighbor_dir); + + if (error) { + /* Return the uninitialized cmesh (nullptr) */ + t8_errorf ("ERROR during OpenFOAM case reading.\n"); + return nullptr; + } + if (!build_cmesh ()) { + t8_errorf ("ERROR during OpenFOAM cmesh build.\n"); + return nullptr; + } + return m_cmesh; + } + + private: + /** + * Compute the path to the OpenFOAM case (parent folder of *.foam file) + * \param [in] foamfile Path to the *.foam file + * \return Path to the OpenFOAM case + */ + t8_path + get_case_dir (t8_path foamfile) + { + t8_path path { foamfile }; + return path.parent_path (); + } + + /** + * Skips the OpenFOAM file header and positions the stream at the first numeric value. + * Also checks if the file is in the ascii format. + * \param[in, out] input_stream The file stream to read from. + * \return True on success, false if header not found or format not ascii. + */ + bool + skip_openfoam_header (std::istream& input_stream) + { + + /* Typical OpenFOAM header example */ + + /*--------------------------------*- C++ -*----------------------------------*\ + // ========= | + // \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + // \\ / O peration | Website: https://openfoam.org + // \\ / A and | Version: 13 + // \\/ M anipulation | + // \*---------------------------------------------------------------------------*/ + // FoamFile + // { + // format ascii; <-- format string we are searching for + // class vectorField; + // location "constant/polyMesh"; + // object points; + // } + // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + // + // + // 18 <-- first numeric value we are searching for + // ( + // (0 0 0) + // (0.5 0 0) + // (1 0 0) + // (0 0.5 0) + // [ ... ] + + std::string line; + bool format_checked = false; + + /* Loop through file till end of file or thill we break manually at the right position. */ + while (std::getline (input_stream, line)) { + std::istringstream line_stream (line); + + /* If the format was not checked yet try to check it now. */ + if (!format_checked) { + /* Check for ascii format */ + std::string field; + line_stream >> field; + + /* If the line contains the word "format" it is the right line */ + if (field == "format") { + std::string format_value; + line_stream >> format_value; + + /* Remove trailing semicolon if present */ + if (!format_value.empty () && format_value.back () == ';') + format_value.pop_back (); + + if (format_value != "ascii") { + t8_errorf ("ERROR: OpenFOAM file format is not ascii: %s\n", format_value.c_str ()); + return false; + } + format_checked = true; + continue; + } + } + + /* We do not need to check for numeric values when the format string did not appear yet. */ + if (format_checked) { + /* Skip to first numeric value and then revert to start of numeric value */ + size_t numeric_value; + if (line_stream >> numeric_value) { + /* Numeric value found, move stream to start of line again */ + input_stream.seekg (-static_cast (line.size ()) - 1, std::ios_base::cur); + return true; + } + } + } + + /* Reaching this code means something went wrong. */ + if (!format_checked) { + t8_errorf ("ERROR: Could not find format specification for OpenFOAM file.\n"); + return false; + } + t8_errorf ("ERROR: Could not find numeric list size after header.\n"); + return false; + } + + /** + * Reads an OpenFOAM label list into a vector. + * These lists can have one of three encodings (to my knowledge): + * expanded list: + * + * ( + * + * ... + * + * ) + * + * compact list: + * (val_0 ... val_n) + * + * uniform list: + * {val} //all values are the same + * + * \param [in, out] input_stream Stream positioned at the list start. + * \param [out] values Parsed list values. + * \return True on success. + */ + bool + read_openfoam_label_list (std::istream& input_stream, std::vector& values) + { + /* Get size of list */ + size_t list_size = 0; + input_stream >> list_size; + + /* Get list opening delimiter */ + char opening_delimiter = '\0'; + input_stream >> opening_delimiter; + + values.clear (); + values.reserve (list_size); + + /* Expanded or compact list: (val_0 ... val_n) with or without line break */ + if (opening_delimiter == '(') { + /* Read values */ + while (values.size () < list_size) { + size_t list_value; + input_stream >> list_value; + values.emplace_back (list_value); + } + + /* Check if closing delimiter is at the expected place. */ + char closing_delimiter = '\0'; + input_stream >> closing_delimiter; + if (closing_delimiter != ')') { + return false; + } + + return true; + } + + /* Uniform encoding: {val} */ + if (opening_delimiter == '{') { + /* Get the uniform value. */ + size_t uniform_value = 0; + input_stream >> uniform_value; + + /* Check for expected closing delimiter. */ + char closing_delimiter = '\0'; + input_stream >> closing_delimiter; + if (closing_delimiter != '}') { + return false; + } + + /* Assign uniform values to list. */ + values.assign (list_size, uniform_value); + return true; + } + + return false; + } + + /** + * Reads in an OpenFOAM points file and stores the points. + * \param [in] points_dir The path to the points file. + * \return True on success. + */ + bool + read_points (const t8_path& points_dir) + { + /* TODO: Implement compact lists for points (mostly happen when mesh only has one element) */ + + std::ifstream file { points_dir }; + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", points_dir.c_str ()); + m_points.clear (); + return false; + } + /* Skip OpenFOAM header. */ + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", points_dir.c_str ()); + return false; + } + + /* Get the point count */ + std::string line; + size_t n_points = 0; + while (std::getline (file, line)) { + std::istringstream iss (line); + if (iss >> n_points) + break; + } + T8_ASSERTF (n_points > 0, "ERROR: Mesh contains no coordinates."); + + std::vector> points; + points.reserve (n_points); + + /* Skip opening '(' */ + std::getline (file, line); + + /* Read all points */ + for (size_t i_point = 0; i_point < n_points; ++i_point) { + std::getline (file, line); + std::istringstream iss (line); + char c; + t8_3D_vec point; + if (!(iss >> c >> point[0] >> point[1] >> point[2] >> c)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", points_dir.c_str ()); + m_points.clear (); + return false; + } + m_points.emplace_back (point); + } + return true; + } + + /** + * Reads in an OpenFOAM faces file stores the faces. + * \param [in] faces_dir The path to the faces file. + * \return True on success. + */ + bool + read_faces (const t8_path& faces_dir) + { + std::ifstream file (faces_dir); + /* Check if file exists. */ + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", faces_dir.c_str ()); + m_face_points.clear (); + return false; + } + /* Skip OpenFOAM header. */ + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + return false; + } + + /* Get number of faces */ + size_t n_faces = 0; + std::string line; + std::getline (file, line); + std::istringstream iss (line); + if (!(iss >> n_faces)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + } + + /* skip opening '(' */ + std::getline (file, line); + + m_face_points.reserve (n_faces); + + /* Read all faces into m_face_points */ + for (size_t i_face = 0; i_face < n_faces; ++i_face) { + std::vector points; + if (!read_openfoam_label_list (file, points)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + return false; + } + m_face_points.emplace_back (std::move (points)); + } + return true; + } + + /** + * Reads in an OpenFOAM owner file, assigns the faces to their cells and + * also fills one half of the neighborhoods. + * \param [in] owner_dir The path to the owner file. + * \return True on success. + */ + bool + read_owner (const t8_path& owner_dir) + { + T8_ASSERT (!m_face_points.empty ()); + + std::ifstream file (owner_dir); + /* Check if file can be opened */ + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", owner_dir.c_str ()); + return false; + } + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_dir.c_str ()); + return false; + } + + /** List of read values from owners file. Position is equal to face id and value + * is the owner of the face. face_id -> cell_id */ + std::vector owner_list; + if (!read_openfoam_label_list (file, owner_list)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_dir.c_str ()); + return false; + } + + /* The number of cells in the mesh is the highest owner_id + 1 (since it starts at 0). */ + const size_t num_cells = *std::max_element (owner_list.begin (), owner_list.end ()) + 1; + t8_debugf ("Reading OpenFOAM mesh with %li cells.\n", num_cells); + m_cell_faces.resize (num_cells); + + /* Assign faces to their cells and fill neighborhoods. */ + for (size_t face_id = 0; face_id < owner_list.size (); ++face_id) { + const size_t cell_id = owner_list[face_id]; + /* Add face to owner cell. Normal points outwards (1). */ + m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 1)); + } + + return true; + } + + /** + * Reads in an OpenFOAM owner file, assigns the faces to their cells and + * also fills the other half of the neighborhoods. + * \param [in] neighbor_dir The path to the points file. + * \return True on success. + */ + bool + read_neighbor (const t8_path& neighbor_dir) + { + T8_ASSERT (!m_cell_faces.empty ()); + /* Check if file can be opened */ + std::ifstream file (neighbor_dir); + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", neighbor_dir.c_str ()); + return false; + } + + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_dir.c_str ()); + return false; + } + + /** List of read values from neighbor file. Position is equal to face id and value + * is the neighbor of the face. face_id -> cell_id */ + std::vector neighbor_list; + if (!read_openfoam_label_list (file, neighbor_list)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_dir.c_str ()); + return false; + } + + /* Assign faces to their cells and fill neighborhoods. */ + for (size_t face_id = 0; face_id < neighbor_list.size (); ++face_id) { + const size_t cell_id = neighbor_list[face_id]; + /* Add face to owner cell. Normal points inwards (-1). */ + m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 0)); + } + + return true; + } + + /** + * Build the cmesh from the raw mesh data. + * \return True on success + */ + bool + build_cmesh () + { + t8_global_errorf ("ERROR: Not implemented yet. \n"); + return 0; + } + + /** Path to the OpenFOAM case. */ + t8_path m_case_dir; + /** The cmesh to build. */ + t8_cmesh_t m_cmesh; + /** The assigned communicator. */ + sc_MPI_Comm m_comm; + + /** Holds all points of the mesh. point_id -> x, y, z */ + std::vector m_points; + /** Holds all faces of the mesh. face_id -> point_id 0, ..., point_id n */ + std::vector> m_face_points; + /** Holds all cells and their faces. Second value shows of normal points outwards (1) or inwards (0). + * cell_id -> face_id 0, ..., face_id n */ + std::vector>> m_cell_faces; +}; From d885fcb92ffd3ca94ba8d74ee93d56d551fb98d6 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 1 Apr 2026 15:35:21 +0200 Subject: [PATCH 02/11] add an OpenFOAM reader example --- example/CMakeLists.txt | 3 +- .../cmesh/OpenFOAM/t8_read_openfoam_case.cxx | 120 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 example/IO/cmesh/OpenFOAM/t8_read_openfoam_case.cxx diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 272486619b..85931fbc4f 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -31,7 +31,7 @@ install( TARGETS t8example LIBRARY ) function( add_t8_example ) set( options "" ) - set( oneValueArgs "NAME" ) + set( oneValueArgs "NAME" ) set( multiValueArgs "SOURCES" ) cmake_parse_arguments( ADD_T8_EXAMPLE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) @@ -85,6 +85,7 @@ add_t8_example( NAME t8_read_tetgen SOURCES IO/cmesh/tetgen/ add_t8_example( NAME t8_time_tetgen SOURCES IO/cmesh/tetgen/t8_time_tetgen_file.cxx ) add_t8_example( NAME t8_forest_tetgen SOURCES IO/cmesh/tetgen/t8_forest_from_tetgen.cxx ) add_t8_example( NAME t8_read_triangle SOURCES IO/cmesh/triangle/t8_read_triangle_file.cxx ) +add_t8_example( NAME t8_read_openfoam_case SOURCES IO/cmesh/OpenFOAM/t8_read_openfoam_case.cxx ) copy_example_file (IO/cmesh/gmsh/circlesquare_hybrid_hole.msh) diff --git a/example/IO/cmesh/OpenFOAM/t8_read_openfoam_case.cxx b/example/IO/cmesh/OpenFOAM/t8_read_openfoam_case.cxx new file mode 100644 index 0000000000..ac31464cde --- /dev/null +++ b/example/IO/cmesh/OpenFOAM/t8_read_openfoam_case.cxx @@ -0,0 +1,120 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element types in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* This example shows how to read an OpenFOAM mesh and save it onto a cmesh. + * DISCLAIMER: THIS EXAMPLE IS WORK IN PROGRESS AND THE SHOWN FEATURE IS NOT YET FINISHED. +*/ + +#include +#include +#include +#include + +#include + +/** + * Build a cmesh from an OpenFOAM case directory and write it to vtk. + * \param [in] foamfile The path to the OpenFOAM case file (*.case) + */ +static void +build_cmesh_from_openfoam_case (const std::filesystem::path& foamfile) +{ + /* Use the OpenFOAM reader to read the case. */ + t8_openfoam_reader reader (foamfile, sc_MPI_COMM_WORLD); + t8_cmesh_t cmesh = reader.read (); + + /* If the cmesh is empty something went wrong. */ + if (!cmesh) { + t8_global_errorf ("ERROR: Failed to read OpenFOAM case %s\n", foamfile.string ().c_str ()); + return; + } + + t8_global_productionf ("Successfully built cmesh from OpenFOAM case: %s\n", foamfile.string ().c_str ()); + t8_global_productionf ("cmesh contains %lli trees.\n", (long long) t8_cmesh_get_num_trees (cmesh)); + + /* Write the cmesh to vtk. */ + std::string vtk_file = "t8_openfoam_mesh_" + foamfile.parent_path ().filename ().string (); + t8_cmesh_vtk_write_file (cmesh, vtk_file.c_str ()); + + /* Destroy the cmesh. */ + t8_cmesh_destroy (&cmesh); +} + +int +main (int argc, char* argv[]) +{ + int mpiret, helpme; + sc_options_t* opt; + const char* casefile = nullptr; + char usage[BUFSIZ]; + char help[BUFSIZ]; + int sreturn; + + /* Initialize t8code */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, nullptr, SC_LP_ESSENTIAL); + t8_init (SC_LP_DEFAULT); + + /* Set up a short help message */ + snprintf (usage, BUFSIZ, "Usage:\t%s ", basename (argv[0])); + sreturn = snprintf (help, BUFSIZ, + "This program reads the mesh inside an OpenFOAM case " + "and constructs a t8code coarse mesh from it.\n" + "The path must lead to a OpenFOAM *.foam file inside the OpenFOAM case directory.\n" + "\n%s\n", + usage); + + if (sreturn >= BUFSIZ) { + /* The help message was truncated */ + /* Note: gcc >= 7.1 prints a warning if we + * do not check the return value of snprintf. */ + t8_debugf ("Warning: Truncated help message to '%s'\n", help); + } + + /* Read user options */ + opt = sc_options_new (argv[0]); + sc_options_add_switch (opt, 'h', "help", &helpme, "Display this help message."); + sc_options_add_string (opt, 'f', "foamcase", &casefile, "", "Path to the OpenFOAM case (case.foam)."); + + const int parsed = sc_options_parse (t8_get_package_id (), SC_LP_ERROR, opt, argc, argv); + t8_global_errorf ("THE FEATURES OF THIS EXAMPLE ARE NOT FINISHED YET. THEREFORE, THIS EXAMPLE MIGHT CRASH ABRUPTLY.\n" + "THIS MESSAGE WILL BE REMOVED WHEN THE IMPLEMENTATION OF THE OPENFOAM READER IS FINISHED.\n"); + if (helpme) { + t8_global_productionf ("%s\n", help); + sc_options_print_summary (t8_get_package_id (), SC_LP_PRODUCTION, opt); + } + else if (parsed < 0 || strcmp (casefile, "") == 0) { + t8_global_productionf ("Wrong usage.\n"); + sc_options_print_usage (t8_get_package_id (), SC_LP_ERROR, opt, nullptr); + } + else { + build_cmesh_from_openfoam_case (casefile); + } + + sc_options_destroy (opt); + sc_finalize (); + sc_MPI_Finalize (); + + return 0; +} From a0c86a70c1e4307a93645e541b02083db60c2253 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 1 Apr 2026 16:07:41 +0200 Subject: [PATCH 03/11] fix documentation --- src/t8_openfoam/t8_openfoam_reader.hxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index 6d126b1503..c5a424f23e 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -43,9 +43,13 @@ #include #include +/** + * Reads an OpenFOAM case into t8code data structures. + */ struct t8_openfoam_reader { public: + /** Abbreviation for std::filesystem::path */ using t8_path = std::filesystem::path; /** @@ -111,7 +115,7 @@ struct t8_openfoam_reader /** * Skips the OpenFOAM file header and positions the stream at the first numeric value. * Also checks if the file is in the ascii format. - * \param[in, out] input_stream The file stream to read from. + * \param[in,out] input_stream The file stream to read from. * \return True on success, false if header not found or format not ascii. */ bool @@ -201,6 +205,7 @@ struct t8_openfoam_reader * Reads an OpenFOAM label list into a vector. * These lists can have one of three encodings (to my knowledge): * expanded list: + * \code * * ( * @@ -213,8 +218,9 @@ struct t8_openfoam_reader * * uniform list: * {val} //all values are the same + * \endcode * - * \param [in, out] input_stream Stream positioned at the list start. + * \param [in,out] input_stream Stream positioned at the list start. * \param [out] values Parsed list values. * \return True on success. */ From a612eca4f9bd23d9204c2c45bfc07eb78bec1b9a Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 2 Apr 2026 10:48:58 +0200 Subject: [PATCH 04/11] makr comm as unused --- src/t8_openfoam/t8_openfoam_reader.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index c5a424f23e..b486f6db80 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -480,7 +480,7 @@ struct t8_openfoam_reader /** The cmesh to build. */ t8_cmesh_t m_cmesh; /** The assigned communicator. */ - sc_MPI_Comm m_comm; + [[maybe_unised]] sc_MPI_Comm m_comm; /** Holds all points of the mesh. point_id -> x, y, z */ std::vector m_points; From 95badc866aefdf53c2773dce80860f79fb6741c5 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 2 Apr 2026 11:07:44 +0200 Subject: [PATCH 05/11] removed unused comm --- src/t8_openfoam/t8_openfoam_reader.hxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index b486f6db80..7634270ca0 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -479,8 +479,6 @@ struct t8_openfoam_reader t8_path m_case_dir; /** The cmesh to build. */ t8_cmesh_t m_cmesh; - /** The assigned communicator. */ - [[maybe_unised]] sc_MPI_Comm m_comm; /** Holds all points of the mesh. point_id -> x, y, z */ std::vector m_points; From 958cd646d7050fa094559a89614006089585d46d Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 2 Apr 2026 13:22:26 +0200 Subject: [PATCH 06/11] remove unused comm --- src/t8_openfoam/t8_openfoam_reader.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index 7634270ca0..c1524e0cb0 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -57,8 +57,8 @@ struct t8_openfoam_reader * \param [in] foamfile Path to the *.foam file inside the OpenFOAM case directory. * \param [in] comm The communicator to use for the obtained forest. */ - t8_openfoam_reader (t8_path foamfile, sc_MPI_Comm comm) - : m_case_dir (get_case_dir (foamfile)), m_cmesh (nullptr), m_comm (comm) + t8_openfoam_reader (t8_path foamfile, [[maybe_unused]] sc_MPI_Comm comm) + : m_case_dir (get_case_dir (foamfile)), m_cmesh (nullptr) { SC_CHECK_ABORTF (std::filesystem::exists (foamfile), "ERROR: Foam file does not exist: %s", foamfile.c_str ()); }; From ca0e2eea73b7d17cea412cadaa13533128e9690f Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer <49643115+sandro-elsweijer@users.noreply.github.com> Date: Mon, 18 May 2026 13:22:38 +0200 Subject: [PATCH 07/11] Apply suggestions from code review Co-authored-by: spenke91 --- src/t8_openfoam/t8_openfoam_reader.hxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index c1524e0cb0..f6a56c66bc 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -53,7 +53,7 @@ struct t8_openfoam_reader using t8_path = std::filesystem::path; /** - * Reader for OpenFOAM cases. + * Constructor of OpenFOAM reader. * \param [in] foamfile Path to the *.foam file inside the OpenFOAM case directory. * \param [in] comm The communicator to use for the obtained forest. */ @@ -82,10 +82,10 @@ struct t8_openfoam_reader /* The file reading needs to happen in this order, since these functions depend on each other. */ bool error = 0; - error = !read_points (case_points_dir); - error &= !read_faces (case_faces_dir); - error &= !read_owner (case_owner_dir); - error &= !read_neighbor (case_neighbor_dir); + error = !read_points (case_points_dir); + error = error && !read_faces (case_faces_dir); + error = error && !read_owner (case_owner_dir); + error = error && !read_neighbor (case_neighbor_dir); if (error) { /* Return the uninitialized cmesh (nullptr) */ @@ -152,7 +152,7 @@ struct t8_openfoam_reader std::string line; bool format_checked = false; - /* Loop through file till end of file or thill we break manually at the right position. */ + /* Loop through file till end of file or till we break manually at the right position. */ while (std::getline (input_stream, line)) { std::istringstream line_stream (line); @@ -425,7 +425,7 @@ struct t8_openfoam_reader } /** - * Reads in an OpenFOAM owner file, assigns the faces to their cells and + * Reads in an OpenFOAM neighbor file, assigns the faces to their cells and * also fills the other half of the neighborhoods. * \param [in] neighbor_dir The path to the points file. * \return True on success. @@ -472,7 +472,7 @@ struct t8_openfoam_reader build_cmesh () { t8_global_errorf ("ERROR: Not implemented yet. \n"); - return 0; + return false; } /** Path to the OpenFOAM case. */ From 2cc4df2c52369e78b9f5b92f0e7ade4a01b6eebb Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Mon, 18 May 2026 13:41:11 +0200 Subject: [PATCH 08/11] rename dir to file --- src/t8_openfoam/t8_openfoam_reader.hxx | 70 +++++++++++++------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index f6a56c66bc..673289e26c 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -53,7 +53,7 @@ struct t8_openfoam_reader using t8_path = std::filesystem::path; /** - * Constructor of OpenFOAM reader. + * Constructor of OpenFOAM reader. * \param [in] foamfile Path to the *.foam file inside the OpenFOAM case directory. * \param [in] comm The communicator to use for the obtained forest. */ @@ -74,18 +74,18 @@ struct t8_openfoam_reader { /* Build paths to the standard OpenFOAM mesh files. */ - const t8_path case_faces_dir = m_case_dir / "constant/polyMesh/faces"; - const t8_path case_boundary_dir = m_case_dir / "constant/polyMesh/boundary"; - const t8_path case_neighbor_dir = m_case_dir / "constant/polyMesh/neighbour"; - const t8_path case_owner_dir = m_case_dir / "constant/polyMesh/owner"; - const t8_path case_points_dir = m_case_dir / "constant/polyMesh/points"; + const t8_path case_faces_file = m_case_dir / "constant/polyMesh/faces"; + const t8_path case_boundary_file = m_case_dir / "constant/polyMesh/boundary"; + const t8_path case_neighbor_file = m_case_dir / "constant/polyMesh/neighbour"; + const t8_path case_owner_file = m_case_dir / "constant/polyMesh/owner"; + const t8_path case_points_file = m_case_dir / "constant/polyMesh/points"; /* The file reading needs to happen in this order, since these functions depend on each other. */ bool error = 0; - error = !read_points (case_points_dir); - error = error && !read_faces (case_faces_dir); - error = error && !read_owner (case_owner_dir); - error = error && !read_neighbor (case_neighbor_dir); + error = !read_points (case_points_file); + error = error && !read_faces (case_faces_file); + error = error && !read_owner (case_owner_file); + error = error && !read_neighbor (case_neighbor_file); if (error) { /* Return the uninitialized cmesh (nullptr) */ @@ -280,23 +280,23 @@ struct t8_openfoam_reader /** * Reads in an OpenFOAM points file and stores the points. - * \param [in] points_dir The path to the points file. + * \param [in] points_file The path to the points file. * \return True on success. */ bool - read_points (const t8_path& points_dir) + read_points (const t8_path& points_file) { /* TODO: Implement compact lists for points (mostly happen when mesh only has one element) */ - std::ifstream file { points_dir }; + std::ifstream file { points_file }; if (!file) { - t8_errorf ("ERROR: File not found: %s\n", points_dir.c_str ()); + t8_errorf ("ERROR: File not found: %s\n", points_file.c_str ()); m_points.clear (); return false; } /* Skip OpenFOAM header. */ if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", points_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); return false; } @@ -323,7 +323,7 @@ struct t8_openfoam_reader char c; t8_3D_vec point; if (!(iss >> c >> point[0] >> point[1] >> point[2] >> c)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", points_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); m_points.clear (); return false; } @@ -334,22 +334,22 @@ struct t8_openfoam_reader /** * Reads in an OpenFOAM faces file stores the faces. - * \param [in] faces_dir The path to the faces file. + * \param [in] faces_file The path to the faces file. * \return True on success. */ bool - read_faces (const t8_path& faces_dir) + read_faces (const t8_path& faces_file) { - std::ifstream file (faces_dir); + std::ifstream file (faces_file); /* Check if file exists. */ if (!file) { - t8_errorf ("ERROR: File not found: %s\n", faces_dir.c_str ()); + t8_errorf ("ERROR: File not found: %s\n", faces_file.c_str ()); m_face_points.clear (); return false; } /* Skip OpenFOAM header. */ if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); return false; } @@ -359,7 +359,7 @@ struct t8_openfoam_reader std::getline (file, line); std::istringstream iss (line); if (!(iss >> n_faces)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); } /* skip opening '(' */ @@ -371,7 +371,7 @@ struct t8_openfoam_reader for (size_t i_face = 0; i_face < n_faces; ++i_face) { std::vector points; if (!read_openfoam_label_list (file, points)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); return false; } m_face_points.emplace_back (std::move (points)); @@ -382,22 +382,22 @@ struct t8_openfoam_reader /** * Reads in an OpenFOAM owner file, assigns the faces to their cells and * also fills one half of the neighborhoods. - * \param [in] owner_dir The path to the owner file. + * \param [in] owner_file The path to the owner file. * \return True on success. */ bool - read_owner (const t8_path& owner_dir) + read_owner (const t8_path& owner_file) { T8_ASSERT (!m_face_points.empty ()); - std::ifstream file (owner_dir); + std::ifstream file (owner_file); /* Check if file can be opened */ if (!file) { - t8_errorf ("ERROR: File not found: %s\n", owner_dir.c_str ()); + t8_errorf ("ERROR: File not found: %s\n", owner_file.c_str ()); return false; } if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", owner_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); return false; } @@ -405,7 +405,7 @@ struct t8_openfoam_reader * is the owner of the face. face_id -> cell_id */ std::vector owner_list; if (!read_openfoam_label_list (file, owner_list)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", owner_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); return false; } @@ -427,22 +427,22 @@ struct t8_openfoam_reader /** * Reads in an OpenFOAM neighbor file, assigns the faces to their cells and * also fills the other half of the neighborhoods. - * \param [in] neighbor_dir The path to the points file. + * \param [in] neighbor_file The path to the points file. * \return True on success. */ bool - read_neighbor (const t8_path& neighbor_dir) + read_neighbor (const t8_path& neighbor_file) { T8_ASSERT (!m_cell_faces.empty ()); /* Check if file can be opened */ - std::ifstream file (neighbor_dir); + std::ifstream file (neighbor_file); if (!file) { - t8_errorf ("ERROR: File not found: %s\n", neighbor_dir.c_str ()); + t8_errorf ("ERROR: File not found: %s\n", neighbor_file.c_str ()); return false; } if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); return false; } @@ -450,7 +450,7 @@ struct t8_openfoam_reader * is the neighbor of the face. face_id -> cell_id */ std::vector neighbor_list; if (!read_openfoam_label_list (file, neighbor_list)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_dir.c_str ()); + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); return false; } From f6d0397d7e6acc2f851f429cb2f408ad9ffbe95e Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Mon, 18 May 2026 13:45:45 +0200 Subject: [PATCH 09/11] more documentation --- src/t8_openfoam/t8_openfoam_reader.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index 673289e26c..ca962152cc 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -279,7 +279,7 @@ struct t8_openfoam_reader } /** - * Reads in an OpenFOAM points file and stores the points. + * Reads in an OpenFOAM points file and stores the points in the member variable \ref m_points. * \param [in] points_file The path to the points file. * \return True on success. */ @@ -333,7 +333,7 @@ struct t8_openfoam_reader } /** - * Reads in an OpenFOAM faces file stores the faces. + * Reads in an OpenFOAM faces file and stores the faces in the member variable \ref m_face_points. * \param [in] faces_file The path to the faces file. * \return True on success. */ From b0b748897c7bc1bc6fd5a95fed37a2e6b2252ac8 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 20 May 2026 10:49:08 +0200 Subject: [PATCH 10/11] move openfoam reader implementation details to source file --- src/CMakeLists.txt | 1 + src/t8_openfoam/t8_openfoam_reader.cxx | 365 +++++++++++++++++++++++++ src/t8_openfoam/t8_openfoam_reader.hxx | 332 +--------------------- 3 files changed, 375 insertions(+), 323 deletions(-) create mode 100644 src/t8_openfoam/t8_openfoam_reader.cxx diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93f63e7fbb..b03bf539a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -207,6 +207,7 @@ target_sources( T8 PRIVATE t8_vtk/t8_vtk_writer.cxx t8_vtk/t8_vtk_write_ASCII.cxx t8_vtk/t8_vtk_writer_helper.cxx + t8_openfoam/t8_openfoam_reader.cxx ) target_compile_definitions( T8 PUBLIC T8_CMAKE_BUILD ) diff --git a/src/t8_openfoam/t8_openfoam_reader.cxx b/src/t8_openfoam/t8_openfoam_reader.cxx new file mode 100644 index 0000000000..3f9c6aecb7 --- /dev/null +++ b/src/t8_openfoam/t8_openfoam_reader.cxx @@ -0,0 +1,365 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_openfoam_reader.cxx + * Implementation details for \ref t8_openfoam_reader.hxx + */ + +#include + +t8_cmesh_t +t8_openfoam_reader::read () +{ + + /* Build paths to the standard OpenFOAM mesh files. */ + const t8_path case_faces_file = m_case_dir / "constant/polyMesh/faces"; + const t8_path case_boundary_file = m_case_dir / "constant/polyMesh/boundary"; + const t8_path case_neighbor_file = m_case_dir / "constant/polyMesh/neighbour"; + const t8_path case_owner_file = m_case_dir / "constant/polyMesh/owner"; + const t8_path case_points_file = m_case_dir / "constant/polyMesh/points"; + + /* The file reading needs to happen in this order, since these functions depend on each other. */ + bool error = 0; + error = !read_points (case_points_file); + error = error && !read_faces (case_faces_file); + error = error && !read_owner (case_owner_file); + error = error && !read_neighbor (case_neighbor_file); + + if (error) { + /* Return the uninitialized cmesh (nullptr) */ + t8_errorf ("ERROR during OpenFOAM case reading.\n"); + return nullptr; + } + if (!build_cmesh ()) { + t8_errorf ("ERROR during OpenFOAM cmesh build.\n"); + return nullptr; + } + return m_cmesh; +} + +bool +t8_openfoam_reader::skip_openfoam_header (std::istream& input_stream) +{ + + /* Typical OpenFOAM header example */ + + /*--------------------------------*- C++ -*----------------------------------*\ + // ========= | + // \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + // \\ / O peration | Website: https://openfoam.org + // \\ / A and | Version: 13 + // \\/ M anipulation | + // \*---------------------------------------------------------------------------*/ + // FoamFile + // { + // format ascii; <-- format string we are searching for + // class vectorField; + // location "constant/polyMesh"; + // object points; + // } + // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + // + // + // 18 <-- first numeric value we are searching for + // ( + // (0 0 0) + // (0.5 0 0) + // (1 0 0) + // (0 0.5 0) + // [ ... ] + + std::string line; + bool format_checked = false; + + /* Loop through file till end of file or till we break manually at the right position. */ + while (std::getline (input_stream, line)) { + std::istringstream line_stream (line); + + /* If the format was not checked yet try to check it now. */ + if (!format_checked) { + /* Check for ascii format */ + std::string field; + line_stream >> field; + + /* If the line contains the word "format" it is the right line */ + if (field == "format") { + std::string format_value; + line_stream >> format_value; + + /* Remove trailing semicolon if present */ + if (!format_value.empty () && format_value.back () == ';') + format_value.pop_back (); + + if (format_value != "ascii") { + t8_errorf ("ERROR: OpenFOAM file format is not ascii: %s\n", format_value.c_str ()); + return false; + } + format_checked = true; + continue; + } + } + + /* We do not need to check for numeric values when the format string did not appear yet. */ + if (format_checked) { + /* Skip to first numeric value and then revert to start of numeric value */ + size_t numeric_value; + if (line_stream >> numeric_value) { + /* Numeric value found, move stream to start of line again */ + input_stream.seekg (-static_cast (line.size ()) - 1, std::ios_base::cur); + return true; + } + } + } + + /* Reaching this code means something went wrong. */ + if (!format_checked) { + t8_errorf ("ERROR: Could not find format specification for OpenFOAM file.\n"); + return false; + } + t8_errorf ("ERROR: Could not find numeric list size after header.\n"); + return false; +} + +bool +t8_openfoam_reader::read_openfoam_label_list (std::istream& input_stream, std::vector& values) +{ + /* Get size of list */ + size_t list_size = 0; + input_stream >> list_size; + + /* Get list opening delimiter */ + char opening_delimiter = '\0'; + input_stream >> opening_delimiter; + + values.clear (); + values.reserve (list_size); + + /* Expanded or compact list: (val_0 ... val_n) with or without line break */ + if (opening_delimiter == '(') { + /* Read values */ + while (values.size () < list_size) { + size_t list_value; + input_stream >> list_value; + values.emplace_back (list_value); + } + + /* Check if closing delimiter is at the expected place. */ + char closing_delimiter = '\0'; + input_stream >> closing_delimiter; + if (closing_delimiter != ')') { + return false; + } + + return true; + } + + /* Uniform encoding: {val} */ + if (opening_delimiter == '{') { + /* Get the uniform value. */ + size_t uniform_value = 0; + input_stream >> uniform_value; + + /* Check for expected closing delimiter. */ + char closing_delimiter = '\0'; + input_stream >> closing_delimiter; + if (closing_delimiter != '}') { + return false; + } + + /* Assign uniform values to list. */ + values.assign (list_size, uniform_value); + return true; + } + + return false; +} + +bool +t8_openfoam_reader::read_points (const t8_path& points_file) +{ + /* TODO: Implement compact lists for points (mostly happen when mesh only has one element) */ + + std::ifstream file { points_file }; + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", points_file.c_str ()); + m_points.clear (); + return false; + } + /* Skip OpenFOAM header. */ + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); + return false; + } + + /* Get the point count */ + std::string line; + size_t n_points = 0; + while (std::getline (file, line)) { + std::istringstream iss (line); + if (iss >> n_points) + break; + } + T8_ASSERTF (n_points > 0, "ERROR: Mesh contains no coordinates."); + + std::vector> points; + points.reserve (n_points); + + /* Skip opening '(' */ + std::getline (file, line); + + /* Read all points */ + for (size_t i_point = 0; i_point < n_points; ++i_point) { + std::getline (file, line); + std::istringstream iss (line); + char c; + t8_3D_vec point; + if (!(iss >> c >> point[0] >> point[1] >> point[2] >> c)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); + m_points.clear (); + return false; + } + m_points.emplace_back (point); + } + return true; +} + +bool +t8_openfoam_reader::read_faces (const t8_path& faces_file) +{ + std::ifstream file (faces_file); + /* Check if file exists. */ + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", faces_file.c_str ()); + m_face_points.clear (); + return false; + } + /* Skip OpenFOAM header. */ + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); + return false; + } + + /* Get number of faces */ + size_t n_faces = 0; + std::string line; + std::getline (file, line); + std::istringstream iss (line); + if (!(iss >> n_faces)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); + } + + /* skip opening '(' */ + std::getline (file, line); + + m_face_points.reserve (n_faces); + + /* Read all faces into m_face_points */ + for (size_t i_face = 0; i_face < n_faces; ++i_face) { + std::vector points; + if (!read_openfoam_label_list (file, points)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); + return false; + } + m_face_points.emplace_back (std::move (points)); + } + return true; +} + +bool +t8_openfoam_reader::read_owner (const t8_path& owner_file) +{ + T8_ASSERT (!m_face_points.empty ()); + + std::ifstream file (owner_file); + /* Check if file can be opened */ + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", owner_file.c_str ()); + return false; + } + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); + return false; + } + + /** List of read values from owners file. Position is equal to face id and value + * is the owner of the face. face_id -> cell_id */ + std::vector owner_list; + if (!read_openfoam_label_list (file, owner_list)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); + return false; + } + + /* The number of cells in the mesh is the highest owner_id + 1 (since it starts at 0). */ + const size_t num_cells = *std::max_element (owner_list.begin (), owner_list.end ()) + 1; + t8_debugf ("Reading OpenFOAM mesh with %li cells.\n", num_cells); + m_cell_faces.resize (num_cells); + + /* Assign faces to their cells and fill neighborhoods. */ + for (size_t face_id = 0; face_id < owner_list.size (); ++face_id) { + const size_t cell_id = owner_list[face_id]; + /* Add face to owner cell. Normal points outwards (1). */ + m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 1)); + } + + return true; +} + +bool +t8_openfoam_reader::read_neighbor (const t8_path& neighbor_file) +{ + T8_ASSERT (!m_cell_faces.empty ()); + /* Check if file can be opened */ + std::ifstream file (neighbor_file); + if (!file) { + t8_errorf ("ERROR: File not found: %s\n", neighbor_file.c_str ()); + return false; + } + + if (!skip_openfoam_header (file)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); + return false; + } + + /** List of read values from neighbor file. Position is equal to face id and value + * is the neighbor of the face. face_id -> cell_id */ + std::vector neighbor_list; + if (!read_openfoam_label_list (file, neighbor_list)) { + t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); + return false; + } + + /* Assign faces to their cells and fill neighborhoods. */ + for (size_t face_id = 0; face_id < neighbor_list.size (); ++face_id) { + const size_t cell_id = neighbor_list[face_id]; + /* Add face to owner cell. Normal points inwards (-1). */ + m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 0)); + } + + return true; +} + +bool +t8_openfoam_reader::build_cmesh () +{ + t8_global_errorf ("ERROR: Not implemented yet. \n"); + return false; +} diff --git a/src/t8_openfoam/t8_openfoam_reader.hxx b/src/t8_openfoam/t8_openfoam_reader.hxx index ca962152cc..b9eb5bf0c1 100644 --- a/src/t8_openfoam/t8_openfoam_reader.hxx +++ b/src/t8_openfoam/t8_openfoam_reader.hxx @@ -70,34 +70,7 @@ struct t8_openfoam_reader * \return A cmesh containing the OpenFOAM mesh. */ t8_cmesh_t - read () - { - - /* Build paths to the standard OpenFOAM mesh files. */ - const t8_path case_faces_file = m_case_dir / "constant/polyMesh/faces"; - const t8_path case_boundary_file = m_case_dir / "constant/polyMesh/boundary"; - const t8_path case_neighbor_file = m_case_dir / "constant/polyMesh/neighbour"; - const t8_path case_owner_file = m_case_dir / "constant/polyMesh/owner"; - const t8_path case_points_file = m_case_dir / "constant/polyMesh/points"; - - /* The file reading needs to happen in this order, since these functions depend on each other. */ - bool error = 0; - error = !read_points (case_points_file); - error = error && !read_faces (case_faces_file); - error = error && !read_owner (case_owner_file); - error = error && !read_neighbor (case_neighbor_file); - - if (error) { - /* Return the uninitialized cmesh (nullptr) */ - t8_errorf ("ERROR during OpenFOAM case reading.\n"); - return nullptr; - } - if (!build_cmesh ()) { - t8_errorf ("ERROR during OpenFOAM cmesh build.\n"); - return nullptr; - } - return m_cmesh; - } + read (); private: /** @@ -105,7 +78,7 @@ struct t8_openfoam_reader * \param [in] foamfile Path to the *.foam file * \return Path to the OpenFOAM case */ - t8_path + inline t8_path get_case_dir (t8_path foamfile) { t8_path path { foamfile }; @@ -119,87 +92,7 @@ struct t8_openfoam_reader * \return True on success, false if header not found or format not ascii. */ bool - skip_openfoam_header (std::istream& input_stream) - { - - /* Typical OpenFOAM header example */ - - /*--------------------------------*- C++ -*----------------------------------*\ - // ========= | - // \\ / F ield | OpenFOAM: The Open Source CFD Toolbox - // \\ / O peration | Website: https://openfoam.org - // \\ / A and | Version: 13 - // \\/ M anipulation | - // \*---------------------------------------------------------------------------*/ - // FoamFile - // { - // format ascii; <-- format string we are searching for - // class vectorField; - // location "constant/polyMesh"; - // object points; - // } - // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - // - // - // 18 <-- first numeric value we are searching for - // ( - // (0 0 0) - // (0.5 0 0) - // (1 0 0) - // (0 0.5 0) - // [ ... ] - - std::string line; - bool format_checked = false; - - /* Loop through file till end of file or till we break manually at the right position. */ - while (std::getline (input_stream, line)) { - std::istringstream line_stream (line); - - /* If the format was not checked yet try to check it now. */ - if (!format_checked) { - /* Check for ascii format */ - std::string field; - line_stream >> field; - - /* If the line contains the word "format" it is the right line */ - if (field == "format") { - std::string format_value; - line_stream >> format_value; - - /* Remove trailing semicolon if present */ - if (!format_value.empty () && format_value.back () == ';') - format_value.pop_back (); - - if (format_value != "ascii") { - t8_errorf ("ERROR: OpenFOAM file format is not ascii: %s\n", format_value.c_str ()); - return false; - } - format_checked = true; - continue; - } - } - - /* We do not need to check for numeric values when the format string did not appear yet. */ - if (format_checked) { - /* Skip to first numeric value and then revert to start of numeric value */ - size_t numeric_value; - if (line_stream >> numeric_value) { - /* Numeric value found, move stream to start of line again */ - input_stream.seekg (-static_cast (line.size ()) - 1, std::ios_base::cur); - return true; - } - } - } - - /* Reaching this code means something went wrong. */ - if (!format_checked) { - t8_errorf ("ERROR: Could not find format specification for OpenFOAM file.\n"); - return false; - } - t8_errorf ("ERROR: Could not find numeric list size after header.\n"); - return false; - } + skip_openfoam_header (std::istream& input_stream); /** * Reads an OpenFOAM label list into a vector. @@ -225,58 +118,7 @@ struct t8_openfoam_reader * \return True on success. */ bool - read_openfoam_label_list (std::istream& input_stream, std::vector& values) - { - /* Get size of list */ - size_t list_size = 0; - input_stream >> list_size; - - /* Get list opening delimiter */ - char opening_delimiter = '\0'; - input_stream >> opening_delimiter; - - values.clear (); - values.reserve (list_size); - - /* Expanded or compact list: (val_0 ... val_n) with or without line break */ - if (opening_delimiter == '(') { - /* Read values */ - while (values.size () < list_size) { - size_t list_value; - input_stream >> list_value; - values.emplace_back (list_value); - } - - /* Check if closing delimiter is at the expected place. */ - char closing_delimiter = '\0'; - input_stream >> closing_delimiter; - if (closing_delimiter != ')') { - return false; - } - - return true; - } - - /* Uniform encoding: {val} */ - if (opening_delimiter == '{') { - /* Get the uniform value. */ - size_t uniform_value = 0; - input_stream >> uniform_value; - - /* Check for expected closing delimiter. */ - char closing_delimiter = '\0'; - input_stream >> closing_delimiter; - if (closing_delimiter != '}') { - return false; - } - - /* Assign uniform values to list. */ - values.assign (list_size, uniform_value); - return true; - } - - return false; - } + read_openfoam_label_list (std::istream& input_stream, std::vector& values); /** * Reads in an OpenFOAM points file and stores the points in the member variable \ref m_points. @@ -284,53 +126,7 @@ struct t8_openfoam_reader * \return True on success. */ bool - read_points (const t8_path& points_file) - { - /* TODO: Implement compact lists for points (mostly happen when mesh only has one element) */ - - std::ifstream file { points_file }; - if (!file) { - t8_errorf ("ERROR: File not found: %s\n", points_file.c_str ()); - m_points.clear (); - return false; - } - /* Skip OpenFOAM header. */ - if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); - return false; - } - - /* Get the point count */ - std::string line; - size_t n_points = 0; - while (std::getline (file, line)) { - std::istringstream iss (line); - if (iss >> n_points) - break; - } - T8_ASSERTF (n_points > 0, "ERROR: Mesh contains no coordinates."); - - std::vector> points; - points.reserve (n_points); - - /* Skip opening '(' */ - std::getline (file, line); - - /* Read all points */ - for (size_t i_point = 0; i_point < n_points; ++i_point) { - std::getline (file, line); - std::istringstream iss (line); - char c; - t8_3D_vec point; - if (!(iss >> c >> point[0] >> point[1] >> point[2] >> c)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", points_file.c_str ()); - m_points.clear (); - return false; - } - m_points.emplace_back (point); - } - return true; - } + read_points (const t8_path& points_file); /** * Reads in an OpenFOAM faces file and stores the faces in the member variable \ref m_face_points. @@ -338,46 +134,7 @@ struct t8_openfoam_reader * \return True on success. */ bool - read_faces (const t8_path& faces_file) - { - std::ifstream file (faces_file); - /* Check if file exists. */ - if (!file) { - t8_errorf ("ERROR: File not found: %s\n", faces_file.c_str ()); - m_face_points.clear (); - return false; - } - /* Skip OpenFOAM header. */ - if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); - return false; - } - - /* Get number of faces */ - size_t n_faces = 0; - std::string line; - std::getline (file, line); - std::istringstream iss (line); - if (!(iss >> n_faces)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); - } - - /* skip opening '(' */ - std::getline (file, line); - - m_face_points.reserve (n_faces); - - /* Read all faces into m_face_points */ - for (size_t i_face = 0; i_face < n_faces; ++i_face) { - std::vector points; - if (!read_openfoam_label_list (file, points)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", faces_file.c_str ()); - return false; - } - m_face_points.emplace_back (std::move (points)); - } - return true; - } + read_faces (const t8_path& faces_file); /** * Reads in an OpenFOAM owner file, assigns the faces to their cells and @@ -386,43 +143,7 @@ struct t8_openfoam_reader * \return True on success. */ bool - read_owner (const t8_path& owner_file) - { - T8_ASSERT (!m_face_points.empty ()); - - std::ifstream file (owner_file); - /* Check if file can be opened */ - if (!file) { - t8_errorf ("ERROR: File not found: %s\n", owner_file.c_str ()); - return false; - } - if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); - return false; - } - - /** List of read values from owners file. Position is equal to face id and value - * is the owner of the face. face_id -> cell_id */ - std::vector owner_list; - if (!read_openfoam_label_list (file, owner_list)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", owner_file.c_str ()); - return false; - } - - /* The number of cells in the mesh is the highest owner_id + 1 (since it starts at 0). */ - const size_t num_cells = *std::max_element (owner_list.begin (), owner_list.end ()) + 1; - t8_debugf ("Reading OpenFOAM mesh with %li cells.\n", num_cells); - m_cell_faces.resize (num_cells); - - /* Assign faces to their cells and fill neighborhoods. */ - for (size_t face_id = 0; face_id < owner_list.size (); ++face_id) { - const size_t cell_id = owner_list[face_id]; - /* Add face to owner cell. Normal points outwards (1). */ - m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 1)); - } - - return true; - } + read_owner (const t8_path& owner_file); /** * Reads in an OpenFOAM neighbor file, assigns the faces to their cells and @@ -431,49 +152,14 @@ struct t8_openfoam_reader * \return True on success. */ bool - read_neighbor (const t8_path& neighbor_file) - { - T8_ASSERT (!m_cell_faces.empty ()); - /* Check if file can be opened */ - std::ifstream file (neighbor_file); - if (!file) { - t8_errorf ("ERROR: File not found: %s\n", neighbor_file.c_str ()); - return false; - } - - if (!skip_openfoam_header (file)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); - return false; - } - - /** List of read values from neighbor file. Position is equal to face id and value - * is the neighbor of the face. face_id -> cell_id */ - std::vector neighbor_list; - if (!read_openfoam_label_list (file, neighbor_list)) { - t8_errorf ("ERROR: Unable to parse file: %s\n", neighbor_file.c_str ()); - return false; - } - - /* Assign faces to their cells and fill neighborhoods. */ - for (size_t face_id = 0; face_id < neighbor_list.size (); ++face_id) { - const size_t cell_id = neighbor_list[face_id]; - /* Add face to owner cell. Normal points inwards (-1). */ - m_cell_faces[cell_id].emplace_back (std::make_pair (face_id, 0)); - } - - return true; - } + read_neighbor (const t8_path& neighbor_file); /** * Build the cmesh from the raw mesh data. * \return True on success */ bool - build_cmesh () - { - t8_global_errorf ("ERROR: Not implemented yet. \n"); - return false; - } + build_cmesh (); /** Path to the OpenFOAM case. */ t8_path m_case_dir; From 99e7e214925a2062edfceee5fc4adf855e17bd43 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Wed, 20 May 2026 11:09:40 +0200 Subject: [PATCH 11/11] make error handling better --- src/t8_openfoam/t8_openfoam_reader.cxx | 30 +++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/t8_openfoam/t8_openfoam_reader.cxx b/src/t8_openfoam/t8_openfoam_reader.cxx index 3f9c6aecb7..ebe64f1b3f 100644 --- a/src/t8_openfoam/t8_openfoam_reader.cxx +++ b/src/t8_openfoam/t8_openfoam_reader.cxx @@ -153,8 +153,9 @@ t8_openfoam_reader::read_openfoam_label_list (std::istream& input_stream, std::v values.clear (); values.reserve (list_size); + switch (opening_delimiter) { /* Expanded or compact list: (val_0 ... val_n) with or without line break */ - if (opening_delimiter == '(') { + case '(': { /* Read values */ while (values.size () < list_size) { size_t list_value; @@ -165,32 +166,35 @@ t8_openfoam_reader::read_openfoam_label_list (std::istream& input_stream, std::v /* Check if closing delimiter is at the expected place. */ char closing_delimiter = '\0'; input_stream >> closing_delimiter; - if (closing_delimiter != ')') { - return false; + if (closing_delimiter == ')') { + return true; } - - return true; + [[fallthrough]]; } /* Uniform encoding: {val} */ - if (opening_delimiter == '{') { + case '{': { /* Get the uniform value. */ size_t uniform_value = 0; input_stream >> uniform_value; + /* Assign uniform values to list. */ + values.assign (list_size, uniform_value); + return true; + /* Check for expected closing delimiter. */ char closing_delimiter = '\0'; input_stream >> closing_delimiter; - if (closing_delimiter != '}') { - return false; + if (closing_delimiter == '}') { + return true; } - - /* Assign uniform values to list. */ - values.assign (list_size, uniform_value); - return true; + [[fallthrough]]; } - return false; + default: + t8_errorf ("Unrecognized encoding of OpenFOAM list.\n"); + return false; + } } bool