From 2462170647a3a0ea8ea117ff60decffeaaa55f53 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Mon, 7 Sep 2026 16:27:02 +0200 Subject: [PATCH] #14683 Share OPM parser for keyword lookups --- .../FileInterface/RifEventKeywordFormatter.cpp | 15 +++------------ .../ProjectDataModel/Jobs/RimKeywordFactory.cpp | 17 +++-------------- .../ProjectDataModel/RiaOpmKeywordTools.cpp | 12 +++++++----- .../ProjectDataModel/RiaOpmKeywordTools.h | 6 ++++++ 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp index 3bbf1376c3..bc317ca391 100644 --- a/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp +++ b/ApplicationLibCode/FileInterface/RifEventKeywordFormatter.cpp @@ -25,6 +25,7 @@ #include "RimWellEventKeywordItem.h" #include "RiaLogging.h" +#include "RiaOpmKeywordTools.h" #include "RiaQStringFormatter.h" #include "cafAppEnum.h" @@ -46,16 +47,6 @@ namespace { -// Constructing an Opm::Parser builds the complete keyword database (several thousand keywords) and -// costs milliseconds. The parser is stateless once constructed, so a single shared instance is used -// for all keyword lookups. Building one per keyword made schedule generation scale linearly in the -// number of events with a very large constant (see issue: GenerateSchedule does not scale). -const Opm::Parser& sharedParser() -{ - static const Opm::Parser parser; - return parser; -} - QString keywordToString( const Opm::DeckKeyword& kw ) { if ( kw.name().empty() ) return {}; @@ -73,7 +64,7 @@ bool RifEventKeywordFormatter::isRecordlessKeyword( const Opm::DeckKeyword& keyw { try { - const auto& parserKeyword = sharedParser().getKeyword( keyword.name() ); + const auto& parserKeyword = RiaOpmKeywordTools::defaultParser().getKeyword( keyword.name() ); return parserKeyword.begin() == parserKeyword.end(); } catch ( const std::exception& ) @@ -94,7 +85,7 @@ std::optional RifEventKeywordFormatter::buildKeyword( const QS try { - const Opm::ParserKeyword& parserKw = sharedParser().getKeyword( kwName ); + const Opm::ParserKeyword& parserKw = RiaOpmKeywordTools::defaultParser().getKeyword( kwName ); Opm::DeckKeyword kw( parserKw ); const size_t numRecords = static_cast( std::distance( parserKw.begin(), parserKw.end() ) ); diff --git a/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordFactory.cpp b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordFactory.cpp index 796ab36d0d..5dc5ecf4fe 100644 --- a/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordFactory.cpp +++ b/ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordFactory.cpp @@ -20,6 +20,7 @@ #include "Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h" +#include "RiaOpmKeywordTools.h" #include "RiaResultNames.h" #include "RifEclipseInputFileTools.h" @@ -64,18 +65,6 @@ /// /// //================================================================================================== -namespace -{ -// A single shared parser instance is used for all keyword schema lookups. Constructing an -// Opm::Parser populates the complete keyword database and costs milliseconds, which is far too -// expensive to repeat for every keyword being serialised. -const Opm::Parser& sharedParser() -{ - static const Opm::Parser parser; - return parser; -} -} // namespace - namespace RimKeywordFactory { @@ -944,7 +933,7 @@ QString deckKeywordToAlignedString( const Opm::DeckKeyword& keyword ) std::vector canonicalItemOrder; try { - const Opm::ParserKeyword& parserKeyword = sharedParser().getKeyword( keyword.name() ); + const Opm::ParserKeyword& parserKeyword = RiaOpmKeywordTools::defaultParser().getKeyword( keyword.name() ); if ( std::distance( parserKeyword.begin(), parserKeyword.end() ) == 1 ) { const Opm::ParserRecord& parserRecord = parserKeyword.getRecord( 0 ); @@ -1089,7 +1078,7 @@ QString deckKeywordToAlignedString( const Opm::DeckKeyword& keyword ) bool slashTerminated = true; try { - slashTerminated = !sharedParser().getKeyword( keyword.name() ).hasFixedSize(); + slashTerminated = !RiaOpmKeywordTools::defaultParser().getKeyword( keyword.name() ).hasFixedSize(); } catch ( ... ) { diff --git a/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.cpp b/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.cpp index 14e11c48d5..22342d0476 100644 --- a/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.cpp +++ b/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.cpp @@ -26,14 +26,16 @@ #include #include -namespace -{ -const Opm::Parser& sharedParser() +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const Opm::Parser& RiaOpmKeywordTools::defaultParser() { + // Constructing an Opm::Parser builds the complete keyword database. Keep one shared instance + // for all keyword schema lookups. static const Opm::Parser parser( true ); return parser; } -} // namespace //-------------------------------------------------------------------------------------------------- /// @@ -41,7 +43,7 @@ const Opm::Parser& sharedParser() std::optional RiaOpmKeywordTools::keywordInfo( const QString& keywordName ) { const QString keyword = keywordName.toUpper(); - const auto& parser = sharedParser(); + const auto& parser = defaultParser(); const auto deckName = keyword.toStdString(); if ( !parser.isRecognizedKeyword( deckName ) ) return std::nullopt; diff --git a/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.h b/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.h index ed6b66e082..57a00816fc 100644 --- a/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.h +++ b/ApplicationLibCode/ProjectDataModel/RiaOpmKeywordTools.h @@ -23,6 +23,11 @@ #include #include +namespace Opm +{ +class Parser; +} + struct RiaOpmKeywordInfo { QString name; @@ -32,5 +37,6 @@ struct RiaOpmKeywordInfo namespace RiaOpmKeywordTools { +const Opm::Parser& defaultParser(); std::optional keywordInfo( const QString& keywordName ); } // namespace RiaOpmKeywordTools