Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions ApplicationLibCode/ProjectDataModel/Jobs/RimKeywordFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,54 @@ namespace
}
return result;
}

//--------------------------------------------------------------------------------------------------
/// Shorten a parser item name to at most maxWidth characters so long names (e.g.
/// "CONNECTION_TRANSMISSIBILITY_FACTOR") do not widen their column beyond the data. Each
/// underscore-separated token is truncated to progressively shorter prefixes ("CONN_TRAN_FACT",
/// "CON_TRA_FAC", ...), falling back to token initials ("CTF") and finally a hard cut.
//--------------------------------------------------------------------------------------------------
std::string shortenItemName( const std::string& name, size_t maxWidth )
{
if ( name.size() <= maxWidth ) return name;

std::vector<std::string> tokens;
std::stringstream tokenStream( name );
std::string token;
while ( std::getline( tokenStream, token, '_' ) )
{
if ( !token.empty() ) tokens.push_back( token );
}
if ( tokens.empty() ) return name.substr( 0, maxWidth );

auto joinPrefixes = [&tokens]( size_t prefixLength )
{
std::string joined;
for ( const auto& t : tokens )
{
if ( !joined.empty() ) joined += "_";
joined += t.substr( 0, prefixLength );
}
return joined;
};

size_t longestToken = 0;
for ( const auto& t : tokens )
longestToken = std::max( longestToken, t.size() );

for ( size_t prefixLength = longestToken - 1; prefixLength >= 2; --prefixLength )
{
std::string candidate = joinPrefixes( prefixLength );
if ( candidate.size() <= maxWidth ) return candidate;
}

std::string initials;
for ( const auto& t : tokens )
initials += t.front();
if ( initials.size() <= maxWidth ) return initials;

return name.substr( 0, maxWidth );
}
} // namespace

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -897,12 +945,21 @@ QString deckKeywordToAlignedString( const Opm::DeckKeyword& keyword )

const size_t numCols = header.size();
std::vector<size_t> width( numCols, 0 );
for ( size_t c = 0; c < numCols; ++c )
width[c] = header[c].size();
for ( const auto& row : rows )
for ( size_t c = 0; c < numCols; ++c )
width[c] = std::max( width[c], row[c].size() );

// Long parser item names (e.g. "CONNECTION_TRANSMISSIBILITY_FACTOR") would otherwise pad
// every data row to the header width and push lines past the 132-character limit enforced
// by some simulators. Shorten each header to the width of its data, letting narrow columns
// grow to a small minimum so the abbreviations stay readable.
constexpr size_t minHeaderWidth = 8;
Comment thread
kriben marked this conversation as resolved.
for ( size_t c = 0; c < numCols; ++c )
{
header[c] = shortenItemName( header[c], std::max( width[c], minHeaderWidth ) );
width[c] = std::max( width[c], header[c].size() );
}

// Header comment line: "--" occupies the same two columns as the data-row indent so the
// header names line up with the values below them.
if ( numCols > 0 )
Expand Down
37 changes: 37 additions & 0 deletions ApplicationLibCode/UnitTests/RifOpmFlowDeckFile-Test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "opm/input/eclipse/Deck/DeckKeyword.hpp"
#include "opm/input/eclipse/Deck/DeckRecord.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/B.hpp"
#include "opm/input/eclipse/Parser/ParserKeywords/C.hpp"

#include <QDebug>
#include <QDir>
Expand Down Expand Up @@ -575,3 +576,39 @@ TEST( RifOpmFlowDeckFileTest, RemoveAndInsertKeywordAtSectionStart )
ASSERT_NE( nextIt, keywords.end() );
EXPECT_EQ( "BCPROP", *nextIt ) << "BCPROP should be the first keyword inside SCHEDULE";
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RimKeywordFactoryTest, DeckKeywordToAlignedStringShortensLongHeaders )
{
using C = Opm::ParserKeywords::COMPDAT;

Opm::DeckKeyword kw( ( Opm::ParserKeywords::COMPDAT() ) );

std::vector<Opm::DeckItem> items;
items.push_back( RifOpmDeckTools::item( C::WELL::itemName, std::string( "WELL-1" ) ) );
items.push_back( RifOpmDeckTools::item( C::I::itemName, 12 ) );
items.push_back( RifOpmDeckTools::item( C::J::itemName, 34 ) );
items.push_back( RifOpmDeckTools::item( C::K1::itemName, 5 ) );
items.push_back( RifOpmDeckTools::item( C::K2::itemName, 7 ) );
items.push_back( RifOpmDeckTools::item( C::STATE::itemName, std::string( "OPEN" ) ) );
items.push_back( RifOpmDeckTools::defaultItem( C::SAT_TABLE::itemName ) );
items.push_back( RifOpmDeckTools::item( C::CONNECTION_TRANSMISSIBILITY_FACTOR::itemName, 0.1234567891 ) );
items.push_back( RifOpmDeckTools::item( C::DIAMETER::itemName, 0.216 ) );
items.push_back( RifOpmDeckTools::defaultItem( C::Kh::itemName ) );
items.push_back( RifOpmDeckTools::item( C::SKIN::itemName, 0.0 ) );
items.push_back( RifOpmDeckTools::defaultItem( C::D_FACTOR::itemName ) );
items.push_back( RifOpmDeckTools::item( C::DIR::itemName, std::string( "Z" ) ) );
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );

QString text = RimKeywordFactory::deckKeywordToAlignedString( kw );

// Long parser item names must be abbreviated so they do not widen the columns (issue #14136).
EXPECT_FALSE( text.contains( "CONNECTION_TRANSMISSIBILITY_FACTOR" ) );

for ( const QString& line : text.split( '\n' ) )
{
EXPECT_LE( line.size(), 132 ) << "Line exceeds 132 characters: " << line.toStdString();
}
}