From 7e5d3d60f086a3af01b1280e9aa2041d90c02628 Mon Sep 17 00:00:00 2001 From: BenPinet Date: Wed, 21 Oct 2026 15:36:26 +0200 Subject: [PATCH 1/6] feat(PVDInput): load time series through ExternalTimeSeriesHelpers The .pvd datasets are written into an ExternalTimeSeriesHelpers (unique vertices and Block polyhedra) which is then applied on the BRep, the same way as the native og_ets time series. Requires the ExternalTimeSeriesHelpers of OpenGeode. Co-Authored-By: Claude Opus 5.5 --- .../internal/brep_blocks_matcher.hpp | 5 +- .../brep_time_attributes_transfer.hpp | 38 ++++++----- .../io/time_series/brep_blocks_matcher.cpp | 19 +----- .../brep_time_attributes_transfer.cpp | 64 +++++++++---------- src/geode/io/time_series/pvd_input.cpp | 60 +++++++++-------- 5 files changed, 92 insertions(+), 94 deletions(-) diff --git a/include/geode/io/time_series/internal/brep_blocks_matcher.hpp b/include/geode/io/time_series/internal/brep_blocks_matcher.hpp index c4da76db..f38a9ba1 100644 --- a/include/geode/io/time_series/internal/brep_blocks_matcher.hpp +++ b/include/geode/io/time_series/internal/brep_blocks_matcher.hpp @@ -44,11 +44,12 @@ namespace geode namespace geode::internal { - // Solid elements to Block elements, per Block id + // Solid polyhedra to Block polyhedra per Block id, solid vertices to + // unique vertices struct SolidToBlocksMappings { absl::flat_hash_map< uuid, GenericMapping< index_t > > polyhedra; - absl::flat_hash_map< uuid, GenericMapping< index_t > > vertices; + GenericMapping< index_t > unique_vertices; }; class BRepBlocksMatcher diff --git a/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp b/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp index c2e79a1c..28d3fbe4 100644 --- a/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp +++ b/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp @@ -28,8 +28,9 @@ #include #include +#include + #include -#include #include #include @@ -40,32 +41,38 @@ namespace geode FORWARD_DECLARATION_DIMENSION_CLASS( SolidMesh ); ALIAS_3D( SolidMesh ); class AttributeManager; - class BRep; + class ExternalTimeSeriesHelpers; } // namespace geode namespace geode::internal { class BRepTimeAttributesTransfer { - // Elements of one dataset of a time step and their mappings to - // the Block elements + // Helpers elements filled by a dataset and the mapping to them + struct MappedManager + { + AttributeManager& manager; + const GenericMapping< index_t >& mapping; + }; + + // Elements of one dataset of a time step and the helpers elements + // they fill struct DatasetElements { AttributeManager& manager; - const absl::flat_hash_map< uuid, GenericMapping< index_t > >& - block_mappings; + std::vector< MappedManager > targets; }; public: - BRepTimeAttributesTransfer( BRep& brep, + BRepTimeAttributesTransfer( ExternalTimeSeriesHelpers& helpers, absl::Span< const std::string_view > ignored_attributes ); /*! * Write the attributes of all the datasets of a time step (e.g. one * per GEOS region and per MPI rank) as time step attributes of the - * Block meshes. + * ExternalTimeSeriesHelpers (unique vertices and Block polyhedra). * Attributes sharing a name across the datasets are merged into a - * single time step attribute per Block. + * single time step attribute. * @warning The attributes of the given meshes are modified: those * sharing a name are given the same id. */ @@ -74,10 +81,8 @@ namespace geode::internal absl::Span< const SolidToBlocksMappings > mappings ); private: - void write_elements_step( double time, - absl::Span< const DatasetElements > datasets, - absl::FunctionRef< AttributeManager&( const SolidMesh3D& ) > - block_manager ); + void write_elements_step( + double time, absl::Span< const DatasetElements > datasets ); [[nodiscard]] absl::flat_hash_set< std::string > unify_attribute_ids( absl::Span< const DatasetElements > datasets ); @@ -85,13 +90,12 @@ namespace geode::internal [[nodiscard]] std::optional< std::string > transferred_name( const AttributeManager& manager, const uuid& attribute_id ) const; - void write_block_step( double time, + void write_target_step( double time, const AttributeManager& manager, - AttributeManager& block_manager, - const GenericMapping< index_t >& mesh2block ); + const MappedManager& target ); private: - BRep& brep_; + ExternalTimeSeriesHelpers& helpers_; absl::flat_hash_set< std::string > ignored_attributes_; }; } // namespace geode::internal diff --git a/src/geode/io/time_series/brep_blocks_matcher.cpp b/src/geode/io/time_series/brep_blocks_matcher.cpp index b6f02275..2095c1d5 100644 --- a/src/geode/io/time_series/brep_blocks_matcher.cpp +++ b/src/geode/io/time_series/brep_blocks_matcher.cpp @@ -67,21 +67,6 @@ namespace } return { closest, min_distance }; } - - void map_block_vertices( geode::internal::SolidToBlocksMappings& mappings, - absl::Span< const geode::ComponentMeshVertex > component_vertices, - geode::index_t solid_vertex ) - { - for( const auto& component_vertex : component_vertices ) - { - if( component_vertex.component_id.type - == geode::Block3D::component_type_static() ) - { - mappings.vertices[component_vertex.component_id.id].map( - solid_vertex, component_vertex.vertex ); - } - } - } } // namespace namespace geode::internal @@ -155,9 +140,7 @@ namespace geode::internal const auto& point = block_mesh.point( block_vertex ); const auto [solid_vertex, distance] = closest_polyhedron_vertex( solid, p, point ); - map_block_vertices( mappings, - brep_.component_mesh_vertices( unique_vertex ), - solid_vertex ); + mappings.unique_vertices.map( solid_vertex, unique_vertex ); } } return mappings; diff --git a/src/geode/io/time_series/brep_time_attributes_transfer.cpp b/src/geode/io/time_series/brep_time_attributes_transfer.cpp index b5d16380..d124d30e 100644 --- a/src/geode/io/time_series/brep_time_attributes_transfer.cpp +++ b/src/geode/io/time_series/brep_time_attributes_transfer.cpp @@ -33,8 +33,7 @@ #include -#include -#include +#include namespace { @@ -87,8 +86,9 @@ namespace namespace geode::internal { BRepTimeAttributesTransfer::BRepTimeAttributesTransfer( - BRep& brep, absl::Span< const std::string_view > ignored_attributes ) - : brep_( brep ), + ExternalTimeSeriesHelpers& helpers, + absl::Span< const std::string_view > ignored_attributes ) + : helpers_( helpers ), ignored_attributes_( ignored_attributes.begin(), ignored_attributes.end() ) { @@ -108,42 +108,43 @@ namespace geode::internal for( const auto dataset : Indices{ meshes } ) { const auto& mesh = *meshes[dataset]; + const auto& dataset_mappings = mappings[dataset]; vertices.push_back( { mesh.vertex_attribute_manager(), - mappings[dataset].vertices } ); - polyhedra.push_back( { mesh.polyhedron_attribute_manager(), - mappings[dataset].polyhedra } ); + { { helpers_.unique_vertex_attribute_manager(), + dataset_mappings.unique_vertices } } } ); + auto& dataset_polyhedra = polyhedra.emplace_back( + DatasetElements{ mesh.polyhedron_attribute_manager(), {} } ); + for( const auto& [block_id, mesh2block] : + dataset_mappings.polyhedra ) + { + dataset_polyhedra.targets.push_back( + { helpers_.block_polyhedron_attribute_manager( block_id ), + mesh2block } ); + } } - write_elements_step( time, vertices, - []( const SolidMesh3D& block_mesh ) -> AttributeManager& { - return block_mesh.vertex_attribute_manager(); - } ); - write_elements_step( time, polyhedra, - []( const SolidMesh3D& block_mesh ) -> AttributeManager& { - return block_mesh.polyhedron_attribute_manager(); - } ); + write_elements_step( time, vertices ); + write_elements_step( time, polyhedra ); } - void BRepTimeAttributesTransfer::write_elements_step( double time, - absl::Span< const DatasetElements > datasets, - absl::FunctionRef< AttributeManager&( const SolidMesh3D& ) > - block_manager ) + void BRepTimeAttributesTransfer::write_elements_step( + double time, absl::Span< const DatasetElements > datasets ) { const auto names = unify_attribute_ids( datasets ); - for( const auto& block : brep_.blocks() ) + for( const auto& dataset : datasets ) { - auto& manager = block_manager( block.mesh() ); - for( const auto& name : names ) + for( const auto& target : dataset.targets ) { - delete_existing_step( manager, name, time ); + for( const auto& name : names ) + { + delete_existing_step( target.manager, name, time ); + } } } for( const auto& dataset : datasets ) { - for( const auto& [block_id, mesh2block] : dataset.block_mappings ) + for( const auto& target : dataset.targets ) { - write_block_step( time, dataset.manager, - block_manager( brep_.block( block_id ).mesh() ), - mesh2block ); + write_target_step( time, dataset.manager, target ); } } } @@ -190,10 +191,9 @@ namespace geode::internal return name; } - void BRepTimeAttributesTransfer::write_block_step( double time, + void BRepTimeAttributesTransfer::write_target_step( double time, const AttributeManager& manager, - AttributeManager& block_manager, - const GenericMapping< index_t >& mesh2block ) + const MappedManager& target ) { for( const auto& attribute_id : manager.attribute_ids() ) { @@ -204,8 +204,8 @@ namespace geode::internal auto properties = manager.find_generic_attribute( attribute_id )->properties(); properties.time = time; - block_manager.import( manager, mesh2block, attribute_id ); - block_manager.set_attribute_properties( attribute_id, properties ); + target.manager.import( manager, target.mapping, attribute_id ); + target.manager.set_attribute_properties( attribute_id, properties ); } } } // namespace geode::internal diff --git a/src/geode/io/time_series/pvd_input.cpp b/src/geode/io/time_series/pvd_input.cpp index e0610c3b..2a4466ee 100644 --- a/src/geode/io/time_series/pvd_input.cpp +++ b/src/geode/io/time_series/pvd_input.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -199,6 +200,39 @@ namespace } return files; } + + geode::ExternalTimeSeriesHelpers read_time_series( + const geode::BRep& brep, std::string_view filename ) + { + geode::ExternalTimeSeriesHelpers helpers{ brep }; + const geode::internal::BRepBlocksMatcher matcher{ brep }; + geode::internal::BRepTimeAttributesTransfer transfer{ helpers, + GEOS_IGNORED_ATTRIBUTES }; + std::vector< geode::internal::SolidToBlocksMappings > mappings; + for( const auto& dataset : read_pvd_collection( filename ) ) + { + std::vector< std::unique_ptr< geode::SolidMesh3D > > meshes; + for( const auto& vtu : dataset_vtu_files( dataset.file ) ) + { + meshes.push_back( geode::load_polyhedral_solid< 3 >( vtu ) ); + } + if( mappings.empty() ) + { + for( const auto& mesh : meshes ) + { + mappings.push_back( matcher.mappings( *mesh ) ); + } + } + geode::OpenGeodeIOTimeSeriesException::check_exception( + meshes.size() == mappings.size(), nullptr, + geode::OpenGeodeException::TYPE::data, "[PVDInput] ", + dataset.file, " lists ", meshes.size(), + " datasets whereas the first time step lists ", + mappings.size() ); + transfer.write_step( dataset.time, meshes, mappings ); + } + return helpers; + } } // namespace namespace geode::internal @@ -238,30 +272,6 @@ namespace geode::internal void PVDBRepTimeSeriesInput::read( BRep& brep ) { - const BRepBlocksMatcher matcher{ brep }; - BRepTimeAttributesTransfer transfer{ brep, GEOS_IGNORED_ATTRIBUTES }; - std::vector< SolidToBlocksMappings > mappings; - for( const auto& dataset : read_pvd_collection( this->filename() ) ) - { - std::vector< std::unique_ptr< SolidMesh3D > > meshes; - for( const auto& vtu : dataset_vtu_files( dataset.file ) ) - { - meshes.push_back( load_polyhedral_solid< 3 >( vtu ) ); - } - if( mappings.empty() ) - { - for( const auto& mesh : meshes ) - { - mappings.push_back( matcher.mappings( *mesh ) ); - } - } - OpenGeodeIOTimeSeriesException::check_exception( - meshes.size() == mappings.size(), nullptr, - OpenGeodeException::TYPE::data, "[PVDInput] ", dataset.file, - " lists ", meshes.size(), - " datasets whereas the first time step lists ", - mappings.size() ); - transfer.write_step( dataset.time, meshes, mappings ); - } + read_time_series( brep, this->filename() ).apply_to( brep ); } } // namespace geode::internal From ad064c7c7162288ab862133d4575a45bdfd4b669 Mon Sep 17 00:00:00 2001 From: BenPinet Date: Thu, 22 Oct 2026 16:17:29 +0200 Subject: [PATCH 2/6] feat(PVDInput): build time series point sets from the datasets Replace the BRep blocks matcher by point sets built from the .pvd datasets (merged colocated vertices and polyhedron barycenters), handed to ExternalTimeSeriesHelpers which matches them with the BRep. Co-Authored-By: Claude Opus 5.5 --- ...ansfer.hpp => pvd_attributes_transfer.hpp} | 32 ++-- ..._blocks_matcher.hpp => pvd_point_sets.hpp} | 58 +++---- src/geode/io/time_series/CMakeLists.txt | 8 +- .../io/time_series/brep_blocks_matcher.cpp | 148 ------------------ ...ansfer.cpp => pvd_attributes_transfer.cpp} | 67 +++----- src/geode/io/time_series/pvd_input.cpp | 42 ++--- src/geode/io/time_series/pvd_point_sets.cpp | 112 +++++++++++++ 7 files changed, 200 insertions(+), 267 deletions(-) rename include/geode/io/time_series/internal/{brep_time_attributes_transfer.hpp => pvd_attributes_transfer.hpp} (77%) rename include/geode/io/time_series/internal/{brep_blocks_matcher.hpp => pvd_point_sets.hpp} (56%) delete mode 100644 src/geode/io/time_series/brep_blocks_matcher.cpp rename src/geode/io/time_series/{brep_time_attributes_transfer.cpp => pvd_attributes_transfer.cpp} (73%) create mode 100644 src/geode/io/time_series/pvd_point_sets.cpp diff --git a/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp b/include/geode/io/time_series/internal/pvd_attributes_transfer.hpp similarity index 77% rename from include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp rename to include/geode/io/time_series/internal/pvd_attributes_transfer.hpp index 28d3fbe4..39a55b94 100644 --- a/include/geode/io/time_series/internal/brep_time_attributes_transfer.hpp +++ b/include/geode/io/time_series/internal/pvd_attributes_transfer.hpp @@ -34,43 +34,37 @@ #include #include -#include +#include namespace geode { FORWARD_DECLARATION_DIMENSION_CLASS( SolidMesh ); ALIAS_3D( SolidMesh ); class AttributeManager; - class ExternalTimeSeriesHelpers; } // namespace geode namespace geode::internal { - class BRepTimeAttributesTransfer + // Writes the attributes of the .pvd datasets on the PVDPointSets + class PVDAttributesTransfer { - // Helpers elements filled by a dataset and the mapping to them - struct MappedManager - { - AttributeManager& manager; - const GenericMapping< index_t >& mapping; - }; - - // Elements of one dataset of a time step and the helpers elements - // they fill + // Elements of one dataset of a time step, the point set vertices they + // fill and the mapping to them struct DatasetElements { AttributeManager& manager; - std::vector< MappedManager > targets; + AttributeManager& target; + const GenericMapping< index_t >& mapping; }; public: - BRepTimeAttributesTransfer( ExternalTimeSeriesHelpers& helpers, + explicit PVDAttributesTransfer( absl::Span< const std::string_view > ignored_attributes ); /*! * Write the attributes of all the datasets of a time step (e.g. one * per GEOS region and per MPI rank) as time step attributes of the - * ExternalTimeSeriesHelpers (unique vertices and Block polyhedra). + * time series point sets (unique vertices and cell barycenters). * Attributes sharing a name across the datasets are merged into a * single time step attribute. * @warning The attributes of the given meshes are modified: those @@ -78,7 +72,7 @@ namespace geode::internal */ void write_step( double time, absl::Span< const std::unique_ptr< SolidMesh3D > > meshes, - absl::Span< const SolidToBlocksMappings > mappings ); + const PVDPointSets& point_sets ); private: void write_elements_step( @@ -90,12 +84,10 @@ namespace geode::internal [[nodiscard]] std::optional< std::string > transferred_name( const AttributeManager& manager, const uuid& attribute_id ) const; - void write_target_step( double time, - const AttributeManager& manager, - const MappedManager& target ); + void write_dataset_step( + double time, const DatasetElements& dataset ) const; private: - ExternalTimeSeriesHelpers& helpers_; absl::flat_hash_set< std::string > ignored_attributes_; }; } // namespace geode::internal diff --git a/include/geode/io/time_series/internal/brep_blocks_matcher.hpp b/include/geode/io/time_series/internal/pvd_point_sets.hpp similarity index 56% rename from include/geode/io/time_series/internal/brep_blocks_matcher.hpp rename to include/geode/io/time_series/internal/pvd_point_sets.hpp index f38a9ba1..be5a7a5d 100644 --- a/include/geode/io/time_series/internal/brep_blocks_matcher.hpp +++ b/include/geode/io/time_series/internal/pvd_point_sets.hpp @@ -23,15 +23,14 @@ #pragma once +#include #include -#include #include #include -#include -#include +#include #include @@ -39,46 +38,33 @@ namespace geode { FORWARD_DECLARATION_DIMENSION_CLASS( SolidMesh ); ALIAS_3D( SolidMesh ); - class BRep; } // namespace geode namespace geode::internal { - // Solid polyhedra to Block polyhedra per Block id, solid vertices to - // unique vertices - struct SolidToBlocksMappings + // Vertices of a .pvd dataset to unique vertices, its polyhedra to cell + // barycenters + struct PVDDatasetMappings { - absl::flat_hash_map< uuid, GenericMapping< index_t > > polyhedra; - GenericMapping< index_t > unique_vertices; + GenericMapping< index_t > vertices; + GenericMapping< index_t > polyhedra; }; - class BRepBlocksMatcher + // Point sets of a .pvd time series, given to the ExternalTimeSeriesHelpers + // once all the time steps are written + struct PVDPointSets { - struct BlockElement - { - uuid block_id; - index_t element; - }; - - public: - explicit BRepBlocksMatcher( const BRep& brep ); - - [[nodiscard]] SolidToBlocksMappings mappings( - const SolidMesh3D& solid ) const; - - private: - [[nodiscard]] std::vector< Point3D > compute_barycenters(); - - [[nodiscard]] std::vector< index_t > match_polyhedra( - const SolidMesh3D& solid ) const; - - [[nodiscard]] SolidToBlocksMappings build_mappings( - const SolidMesh3D& solid, - absl::Span< const index_t > matched_ids ) const; - - private: - const BRep& brep_; - std::vector< BlockElement > polyhedra_; - NNSearch3D barycenters_; + std::unique_ptr< PointSet3D > unique_vertices; + std::unique_ptr< PointSet3D > cell_barycenters; + std::vector< PVDDatasetMappings > mappings; }; + + /*! + * Create the .pvd point sets from the datasets of a time step + * (e.g. one per GEOS region and per MPI rank): the dataset vertices, + * colocated ones merged, and the dataset polyhedron barycenters, with + * the mappings of each dataset to them. + */ + [[nodiscard]] PVDPointSets create_pvd_point_sets( + absl::Span< const std::unique_ptr< SolidMesh3D > > meshes ); } // namespace geode::internal diff --git a/src/geode/io/time_series/CMakeLists.txt b/src/geode/io/time_series/CMakeLists.txt index 2bf33ae4..86b228f6 100644 --- a/src/geode/io/time_series/CMakeLists.txt +++ b/src/geode/io/time_series/CMakeLists.txt @@ -22,16 +22,16 @@ add_geode_library( NAME time_series FOLDER "geode/io/time_series" SOURCES - "brep_blocks_matcher.cpp" "common.cpp" "pvd_input.cpp" - "brep_time_attributes_transfer.cpp" + "pvd_attributes_transfer.cpp" + "pvd_point_sets.cpp" PUBLIC_HEADERS "common.hpp" INTERNAL_HEADERS - "internal/brep_blocks_matcher.hpp" "internal/pvd_input.hpp" - "internal/brep_time_attributes_transfer.hpp" + "internal/pvd_attributes_transfer.hpp" + "internal/pvd_point_sets.hpp" PRIVATE_DEPENDENCIES OpenGeode::basic OpenGeode::geometry diff --git a/src/geode/io/time_series/brep_blocks_matcher.cpp b/src/geode/io/time_series/brep_blocks_matcher.cpp deleted file mode 100644 index 2095c1d5..00000000 --- a/src/geode/io/time_series/brep_blocks_matcher.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2019 - 2026 Geode-solutions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#include - -#include -#include -#include - -namespace -{ - - std::tuple< geode::index_t, double > closest_polyhedron_vertex( - const geode::SolidMesh3D& solid_mesh, - geode::index_t polyhedron, - const geode::Point3D& point ) - { - auto closest = geode::NO_ID; - auto min_distance = std::numeric_limits< double >::max(); - for( const auto v : - geode::LRange{ solid_mesh.nb_polyhedron_vertices( polyhedron ) } ) - { - const auto vertex = - solid_mesh.polyhedron_vertex( { polyhedron, v } ); - const auto distance = geode::point_point_distance( - point, solid_mesh.point( vertex ) ); - if( distance < min_distance ) - { - min_distance = distance; - closest = vertex; - } - } - return { closest, min_distance }; - } -} // namespace - -namespace geode::internal -{ - BRepBlocksMatcher::BRepBlocksMatcher( const BRep& brep ) - : brep_( brep ), barycenters_{ compute_barycenters() } - { - } - - SolidToBlocksMappings BRepBlocksMatcher::mappings( - const SolidMesh3D& solid ) const - { - const auto matched_ids = match_polyhedra( solid ); - return build_mappings( solid, matched_ids ); - } - - std::vector< Point3D > BRepBlocksMatcher::compute_barycenters() - { - std::vector< Point3D > barycenters; - for( const auto& block : brep_.blocks() ) - { - const auto& mesh = block.mesh(); - for( const auto p : Range{ mesh.nb_polyhedra() } ) - { - polyhedra_.push_back( { block.id(), p } ); - barycenters.push_back( mesh.polyhedron_barycenter( p ) ); - } - } - return barycenters; - } - - std::vector< index_t > BRepBlocksMatcher::match_polyhedra( - const SolidMesh3D& solid ) const - { - std::vector< index_t > matched_ids; - matched_ids.reserve( solid.nb_polyhedra() ); - for( const auto p : Range{ solid.nb_polyhedra() } ) - { - const auto barycenter = solid.polyhedron_barycenter( p ); - const auto matched_id = barycenters_.closest_neighbor( barycenter ); - matched_ids.push_back( matched_id ); - } - return matched_ids; - } - - SolidToBlocksMappings BRepBlocksMatcher::build_mappings( - const SolidMesh3D& solid, - absl::Span< const index_t > matched_ids ) const - { - SolidToBlocksMappings mappings; - absl::flat_hash_set< index_t > mapped_unique_vertices; - for( const auto p : Range{ solid.nb_polyhedra() } ) - { - const auto matched_id = matched_ids[p]; - const auto& block_polyhedron = polyhedra_[matched_id]; - const auto& block_id = block_polyhedron.block_id; - mappings.polyhedra[block_id].map( p, block_polyhedron.element ); - const auto& block = brep_.block( block_id ); - const auto& block_mesh = block.mesh(); - for( const auto v : LRange{ block_mesh.nb_polyhedron_vertices( - block_polyhedron.element ) } ) - { - const auto block_vertex = block_mesh.polyhedron_vertex( - { block_polyhedron.element, v } ); - const auto unique_vertex = brep_.unique_vertex( - { block.component_id(), block_vertex } ); - if( !mapped_unique_vertices.insert( unique_vertex ).second ) - { - continue; - } - const auto& point = block_mesh.point( block_vertex ); - const auto [solid_vertex, distance] = - closest_polyhedron_vertex( solid, p, point ); - mappings.unique_vertices.map( solid_vertex, unique_vertex ); - } - } - return mappings; - } -} // namespace geode::internal diff --git a/src/geode/io/time_series/brep_time_attributes_transfer.cpp b/src/geode/io/time_series/pvd_attributes_transfer.cpp similarity index 73% rename from src/geode/io/time_series/brep_time_attributes_transfer.cpp rename to src/geode/io/time_series/pvd_attributes_transfer.cpp index d124d30e..e8f5942d 100644 --- a/src/geode/io/time_series/brep_time_attributes_transfer.cpp +++ b/src/geode/io/time_series/pvd_attributes_transfer.cpp @@ -21,7 +21,7 @@ * */ -#include +#include #include @@ -31,10 +31,9 @@ #include #include +#include #include -#include - namespace { struct SharedAttribute @@ -69,8 +68,7 @@ namespace } if( shared->second.type != type ) { - geode::Logger::warning( "[BRepTimeAttributesTransfer] Attribute ", - name, + geode::Logger::warning( "[PVDAttributesTransfer] Attribute ", name, " has different types among the datasets of a time step, " "values of type ", type, " are not imported" ); @@ -85,23 +83,22 @@ namespace namespace geode::internal { - BRepTimeAttributesTransfer::BRepTimeAttributesTransfer( - ExternalTimeSeriesHelpers& helpers, + PVDAttributesTransfer::PVDAttributesTransfer( absl::Span< const std::string_view > ignored_attributes ) - : helpers_( helpers ), - ignored_attributes_( - ignored_attributes.begin(), ignored_attributes.end() ) + : ignored_attributes_( + ignored_attributes.begin(), ignored_attributes.end() ) { } - void BRepTimeAttributesTransfer::write_step( double time, + void PVDAttributesTransfer::write_step( double time, absl::Span< const std::unique_ptr< SolidMesh3D > > meshes, - absl::Span< const SolidToBlocksMappings > mappings ) + const PVDPointSets& point_sets ) { + const auto& mappings = point_sets.mappings; OpenGeodeIOTimeSeriesException::check_exception( meshes.size() == mappings.size(), nullptr, OpenGeodeException::TYPE::internal, - "[BRepTimeAttributesTransfer::write_step] Each dataset should " + "[PVDAttributesTransfer::write_step] Each dataset should " "have its mappings" ); std::vector< DatasetElements > vertices; std::vector< DatasetElements > polyhedra; @@ -110,42 +107,30 @@ namespace geode::internal const auto& mesh = *meshes[dataset]; const auto& dataset_mappings = mappings[dataset]; vertices.push_back( { mesh.vertex_attribute_manager(), - { { helpers_.unique_vertex_attribute_manager(), - dataset_mappings.unique_vertices } } } ); - auto& dataset_polyhedra = polyhedra.emplace_back( - DatasetElements{ mesh.polyhedron_attribute_manager(), {} } ); - for( const auto& [block_id, mesh2block] : - dataset_mappings.polyhedra ) - { - dataset_polyhedra.targets.push_back( - { helpers_.block_polyhedron_attribute_manager( block_id ), - mesh2block } ); - } + point_sets.unique_vertices->vertex_attribute_manager(), + dataset_mappings.vertices } ); + polyhedra.push_back( { mesh.polyhedron_attribute_manager(), + point_sets.cell_barycenters->vertex_attribute_manager(), + dataset_mappings.polyhedra } ); } write_elements_step( time, vertices ); write_elements_step( time, polyhedra ); } - void BRepTimeAttributesTransfer::write_elements_step( + void PVDAttributesTransfer::write_elements_step( double time, absl::Span< const DatasetElements > datasets ) { const auto names = unify_attribute_ids( datasets ); for( const auto& dataset : datasets ) { - for( const auto& target : dataset.targets ) + for( const auto& name : names ) { - for( const auto& name : names ) - { - delete_existing_step( target.manager, name, time ); - } + delete_existing_step( dataset.target, name, time ); } } for( const auto& dataset : datasets ) { - for( const auto& target : dataset.targets ) - { - write_target_step( time, dataset.manager, target ); - } + write_dataset_step( time, dataset ); } } @@ -153,7 +138,7 @@ namespace geode::internal // so that every dataset fills the same time step attribute of a Block. // Returns the names of the transferred attributes. absl::flat_hash_set< std::string > - BRepTimeAttributesTransfer::unify_attribute_ids( + PVDAttributesTransfer::unify_attribute_ids( absl::Span< const DatasetElements > datasets ) { absl::flat_hash_map< std::string, SharedAttribute > shared_attributes; @@ -178,7 +163,7 @@ namespace geode::internal return names; } - std::optional< std::string > BRepTimeAttributesTransfer::transferred_name( + std::optional< std::string > PVDAttributesTransfer::transferred_name( const AttributeManager& manager, const uuid& attribute_id ) const { const auto attribute = manager.find_generic_attribute( attribute_id ); @@ -191,10 +176,10 @@ namespace geode::internal return name; } - void BRepTimeAttributesTransfer::write_target_step( double time, - const AttributeManager& manager, - const MappedManager& target ) + void PVDAttributesTransfer::write_dataset_step( + double time, const DatasetElements& dataset ) const { + const auto& manager = dataset.manager; for( const auto& attribute_id : manager.attribute_ids() ) { if( !transferred_name( manager, attribute_id ) ) @@ -204,8 +189,8 @@ namespace geode::internal auto properties = manager.find_generic_attribute( attribute_id )->properties(); properties.time = time; - target.manager.import( manager, target.mapping, attribute_id ); - target.manager.set_attribute_properties( attribute_id, properties ); + dataset.target.import( manager, dataset.mapping, attribute_id ); + dataset.target.set_attribute_properties( attribute_id, properties ); } } } // namespace geode::internal diff --git a/src/geode/io/time_series/pvd_input.cpp b/src/geode/io/time_series/pvd_input.cpp index 2a4466ee..e23497af 100644 --- a/src/geode/io/time_series/pvd_input.cpp +++ b/src/geode/io/time_series/pvd_input.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -39,14 +40,15 @@ #include #include +#include #include #include #include #include -#include -#include +#include +#include namespace { @@ -202,13 +204,12 @@ namespace } geode::ExternalTimeSeriesHelpers read_time_series( - const geode::BRep& brep, std::string_view filename ) + std::string_view filename ) { - geode::ExternalTimeSeriesHelpers helpers{ brep }; - const geode::internal::BRepBlocksMatcher matcher{ brep }; - geode::internal::BRepTimeAttributesTransfer transfer{ helpers, - GEOS_IGNORED_ATTRIBUTES }; - std::vector< geode::internal::SolidToBlocksMappings > mappings; + geode::internal::PVDAttributesTransfer transfer{ + GEOS_IGNORED_ATTRIBUTES + }; + std::optional< geode::internal::PVDPointSets > point_sets; for( const auto& dataset : read_pvd_collection( filename ) ) { std::vector< std::unique_ptr< geode::SolidMesh3D > > meshes; @@ -216,20 +217,25 @@ namespace { meshes.push_back( geode::load_polyhedral_solid< 3 >( vtu ) ); } - if( mappings.empty() ) + if( !point_sets ) { - for( const auto& mesh : meshes ) - { - mappings.push_back( matcher.mappings( *mesh ) ); - } + point_sets = geode::internal::create_pvd_point_sets( meshes ); } + const auto nb_datasets = point_sets->mappings.size(); geode::OpenGeodeIOTimeSeriesException::check_exception( - meshes.size() == mappings.size(), nullptr, + meshes.size() == nb_datasets, nullptr, geode::OpenGeodeException::TYPE::data, "[PVDInput] ", dataset.file, " lists ", meshes.size(), - " datasets whereas the first time step lists ", - mappings.size() ); - transfer.write_step( dataset.time, meshes, mappings ); + " datasets whereas the first time step lists ", nb_datasets ); + transfer.write_step( dataset.time, meshes, point_sets.value() ); + } + geode::ExternalTimeSeriesHelpers helpers; + if( point_sets ) + { + helpers.set_unique_vertices( + std::move( point_sets->unique_vertices ) ); + helpers.set_cell_barycenters( + std::move( point_sets->cell_barycenters ) ); } return helpers; } @@ -272,6 +278,6 @@ namespace geode::internal void PVDBRepTimeSeriesInput::read( BRep& brep ) { - read_time_series( brep, this->filename() ).apply_to( brep ); + read_time_series( this->filename() ).apply_to( brep ); } } // namespace geode::internal diff --git a/src/geode/io/time_series/pvd_point_sets.cpp b/src/geode/io/time_series/pvd_point_sets.cpp new file mode 100644 index 00000000..3bacbd98 --- /dev/null +++ b/src/geode/io/time_series/pvd_point_sets.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include + +#include +#include + +#include +#include + +namespace +{ + std::unique_ptr< geode::PointSet3D > create_point_set( + absl::Span< const geode::Point3D > points ) + { + auto point_set = geode::PointSet3D::create(); + auto builder = geode::PointSetBuilder3D::create( *point_set ); + for( const auto& point : points ) + { + builder->create_point( point ); + } + return point_set; + } + + // GEOS writes the vertices shared by several regions or MPI ranks in + // each of their datasets + std::unique_ptr< geode::PointSet3D > create_unique_vertices( + absl::Span< const std::unique_ptr< geode::SolidMesh3D > > meshes, + absl::Span< geode::internal::PVDDatasetMappings > mappings ) + { + std::vector< geode::Point3D > points; + for( const auto& mesh : meshes ) + { + for( const auto vertex : geode::Range{ mesh->nb_vertices() } ) + { + points.push_back( mesh->point( vertex ) ); + } + } + const auto colocated_info = + geode::NNSearch3D{ std::move( points ) }.colocated_index_mapping( + geode::GLOBAL_EPSILON ); + geode::index_t point{ 0 }; + for( const auto dataset : geode::Indices{ meshes } ) + { + auto& vertices = mappings[dataset].vertices; + for( const auto vertex : + geode::Range{ meshes[dataset]->nb_vertices() } ) + { + vertices.map( + vertex, colocated_info.colocated_mapping[point++] ); + } + } + return create_point_set( colocated_info.unique_points ); + } + + std::unique_ptr< geode::PointSet3D > create_cell_barycenters( + absl::Span< const std::unique_ptr< geode::SolidMesh3D > > meshes, + absl::Span< geode::internal::PVDDatasetMappings > mappings ) + { + std::vector< geode::Point3D > barycenters; + for( const auto dataset : geode::Indices{ meshes } ) + { + const auto& mesh = *meshes[dataset]; + auto& polyhedra = mappings[dataset].polyhedra; + for( const auto polyhedron : geode::Range{ mesh.nb_polyhedra() } ) + { + polyhedra.map( polyhedron, barycenters.size() ); + barycenters.push_back( + mesh.polyhedron_barycenter( polyhedron ) ); + } + } + return create_point_set( barycenters ); + } +} // namespace + +namespace geode::internal +{ + PVDPointSets create_pvd_point_sets( + absl::Span< const std::unique_ptr< SolidMesh3D > > meshes ) + { + PVDPointSets point_sets; + point_sets.mappings.resize( meshes.size() ); + const auto mappings = absl::MakeSpan( point_sets.mappings ); + point_sets.unique_vertices = create_unique_vertices( meshes, mappings ); + point_sets.cell_barycenters = + create_cell_barycenters( meshes, mappings ); + return point_sets; + } +} // namespace geode::internal From 90554da8f53de320bd5bf8d2f0ef6fd4d5911941 Mon Sep 17 00:00:00 2001 From: BenPinet <126688250+BenPinet@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:18:56 +0000 Subject: [PATCH 3/6] Apply prepare changes --- src/geode/io/time_series/pvd_attributes_transfer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geode/io/time_series/pvd_attributes_transfer.cpp b/src/geode/io/time_series/pvd_attributes_transfer.cpp index e8f5942d..976c7603 100644 --- a/src/geode/io/time_series/pvd_attributes_transfer.cpp +++ b/src/geode/io/time_series/pvd_attributes_transfer.cpp @@ -86,7 +86,7 @@ namespace geode::internal PVDAttributesTransfer::PVDAttributesTransfer( absl::Span< const std::string_view > ignored_attributes ) : ignored_attributes_( - ignored_attributes.begin(), ignored_attributes.end() ) + ignored_attributes.begin(), ignored_attributes.end() ) { } From ea42d3324d69be015a59912521c9d9e0dc33fa33 Mon Sep 17 00:00:00 2001 From: BotellaA <3213882+BotellaA@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:37:53 +0000 Subject: [PATCH 4/6] Apply prepare changes --- .releaserc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index bf47fdf5..30a901b9 100644 --- a/.releaserc +++ b/.releaserc @@ -6,6 +6,6 @@ plugins: [ '@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator', - '@semantic-release/github' + ['@semantic-release/github', { successCommentCondition: false, failCommentCondition: false }] ] } From 15852cfea01929a5619beec4885d83cfdae49696 Mon Sep 17 00:00:00 2001 From: BenPinet Date: Mon, 26 Oct 2026 14:27:27 +0100 Subject: [PATCH 5/6] feat(PVDInput): use the renamed ExternalTimeSeries Co-Authored-By: Claude Opus 5.5 --- .../geode/io/time_series/internal/pvd_point_sets.hpp | 2 +- src/geode/io/time_series/pvd_input.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/geode/io/time_series/internal/pvd_point_sets.hpp b/include/geode/io/time_series/internal/pvd_point_sets.hpp index be5a7a5d..ca6a1c6c 100644 --- a/include/geode/io/time_series/internal/pvd_point_sets.hpp +++ b/include/geode/io/time_series/internal/pvd_point_sets.hpp @@ -50,7 +50,7 @@ namespace geode::internal GenericMapping< index_t > polyhedra; }; - // Point sets of a .pvd time series, given to the ExternalTimeSeriesHelpers + // Point sets of a .pvd time series, given to the ExternalTimeSeries // once all the time steps are written struct PVDPointSets { diff --git a/src/geode/io/time_series/pvd_input.cpp b/src/geode/io/time_series/pvd_input.cpp index e23497af..23f6f181 100644 --- a/src/geode/io/time_series/pvd_input.cpp +++ b/src/geode/io/time_series/pvd_input.cpp @@ -44,7 +44,7 @@ #include #include -#include +#include #include #include @@ -203,7 +203,7 @@ namespace return files; } - geode::ExternalTimeSeriesHelpers read_time_series( + geode::ExternalTimeSeries read_time_series( std::string_view filename ) { geode::internal::PVDAttributesTransfer transfer{ @@ -229,15 +229,15 @@ namespace " datasets whereas the first time step lists ", nb_datasets ); transfer.write_step( dataset.time, meshes, point_sets.value() ); } - geode::ExternalTimeSeriesHelpers helpers; + geode::ExternalTimeSeries time_series; if( point_sets ) { - helpers.set_unique_vertices( + time_series.set_unique_vertices( std::move( point_sets->unique_vertices ) ); - helpers.set_cell_barycenters( + time_series.set_cell_barycenters( std::move( point_sets->cell_barycenters ) ); } - return helpers; + return time_series; } } // namespace From e21649bb12aa9a6c35e8925d805fbb05c325b0f0 Mon Sep 17 00:00:00 2001 From: BenPinet <126688250+BenPinet@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:28:09 +0000 Subject: [PATCH 6/6] Apply prepare changes --- src/geode/io/time_series/pvd_input.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/geode/io/time_series/pvd_input.cpp b/src/geode/io/time_series/pvd_input.cpp index 23f6f181..80a8ebda 100644 --- a/src/geode/io/time_series/pvd_input.cpp +++ b/src/geode/io/time_series/pvd_input.cpp @@ -44,8 +44,8 @@ #include #include -#include #include +#include #include #include @@ -203,8 +203,7 @@ namespace return files; } - geode::ExternalTimeSeries read_time_series( - std::string_view filename ) + geode::ExternalTimeSeries read_time_series( std::string_view filename ) { geode::internal::PVDAttributesTransfer transfer{ GEOS_IGNORED_ATTRIBUTES