Files
ResInsight/ApplicationLibCode/ReservoirDataModel/CompletionsMsw/RigMswDataFormatter.cpp
T

435 lines
18 KiB
C++
Raw Normal View History

2025-10-30 18:46:04 +01:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight 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 3 of the License, or
// (at your option) any later version.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RigMswDataFormatter.h"
#include "RifTextDataTableFormatter.h"
2025-10-31 07:37:03 +01:00
#include "RigMswTableData.h"
#include "RigMswUnifiedData.h"
2025-10-30 18:46:04 +01:00
namespace
{
//--------------------------------------------------------------------------------------------------
/// Helper function to format WELSEGS segment data rows
//--------------------------------------------------------------------------------------------------
2025-11-13 14:53:14 +01:00
void formatWelsegsRows( RifTextDataTableFormatter& formatter, const std::vector<WelsegsRow>& rows )
2025-10-30 18:46:04 +01:00
{
for ( const auto& row : rows )
{
2025-11-05 16:27:19 +01:00
formatter.add( row.segment1 );
formatter.add( row.segment2 );
formatter.add( row.branch );
formatter.add( row.joinSegment );
2025-10-30 18:46:04 +01:00
formatter.add( row.length );
formatter.addOptionalValue( row.depth );
formatter.addOptionalValue( row.diameter );
formatter.addOptionalValue( row.roughness );
if ( !row.description.empty() )
{
formatter.addOptionalComment( QString::fromStdString( row.description ) );
}
2025-10-30 18:46:04 +01:00
formatter.rowCompleted();
}
}
//--------------------------------------------------------------------------------------------------
/// Helper function to format COMPSEGS data rows
//--------------------------------------------------------------------------------------------------
2025-11-13 14:53:14 +01:00
void formatCompsegsRows( RifTextDataTableFormatter& formatter, const std::vector<CompsegsRow>& rows, bool isLgrData )
2025-10-30 18:46:04 +01:00
{
for ( const auto& row : rows )
{
if ( isLgrData )
{
formatter.addStdString( row.gridName );
}
2025-11-05 16:27:19 +01:00
formatter.add( row.i );
formatter.add( row.j );
formatter.add( row.k );
formatter.add( row.branch );
formatter.add( row.distanceStart );
formatter.add( row.distanceEnd );
2025-11-13 14:53:14 +01:00
formatter.rowCompleted();
}
2025-10-30 18:46:04 +01:00
}
2025-11-05 16:27:19 +01:00
} // namespace
2025-10-30 18:46:04 +01:00
//--------------------------------------------------------------------------------------------------
/// Helper function to format WSEGVALV data rows
//--------------------------------------------------------------------------------------------------
2025-11-13 14:53:14 +01:00
void formatWsegvalvRows( RifTextDataTableFormatter& formatter, const std::vector<WsegvalvRow>& rows )
2025-10-30 18:46:04 +01:00
{
for ( const auto& row : rows )
{
2025-11-05 16:27:19 +01:00
formatter.addStdString( row.well );
2025-10-30 18:46:04 +01:00
formatter.add( row.segmentNumber );
2025-11-05 16:27:19 +01:00
formatter.add( row.cv );
formatter.add( row.area );
2025-10-30 18:46:04 +01:00
formatter.rowCompleted();
}
}
//--------------------------------------------------------------------------------------------------
/// Helper function to format WSEGAICD data rows
//--------------------------------------------------------------------------------------------------
2025-11-13 14:53:14 +01:00
void formatWsegaicdRows( RifTextDataTableFormatter& formatter, const std::vector<WsegaicdRow>& rows )
2025-10-30 18:46:04 +01:00
{
for ( const auto& row : rows )
{
2025-11-05 16:27:19 +01:00
formatter.addStdString( row.well );
formatter.add( row.segment1 );
formatter.add( row.segment2 );
formatter.add( row.strength );
formatter.addOptionalValue( row.length );
formatter.addOptionalValue( row.densityCali );
formatter.addOptionalValue( row.viscosityCali );
formatter.addOptionalValue( row.criticalValue );
formatter.addOptionalValue( row.widthTrans );
2025-11-13 14:53:14 +01:00
formatter.addOptionalValue( row.maxViscRatio );
2025-11-05 16:27:19 +01:00
if ( row.methodScalingFactor.has_value() )
{
formatter.add( row.methodScalingFactor.value() );
}
else
{
formatter.add( formatter.defaultMarker() );
}
formatter.add( row.maxAbsRate );
formatter.add( row.flowRateExponent );
formatter.add( row.viscExponent );
if ( row.status.has_value() )
{
formatter.addStdString( row.status.value() );
}
else
{
formatter.addStdString( "OPEN" );
}
formatter.addOptionalValue( row.oilFlowFraction );
formatter.addOptionalValue( row.waterFlowFraction );
formatter.addOptionalValue( row.gasFlowFraction );
formatter.addOptionalValue( row.oilViscFraction );
formatter.addOptionalValue( row.waterViscFraction );
formatter.addOptionalValue( row.gasViscFraction );
2025-10-30 18:46:04 +01:00
formatter.rowCompleted();
}
}
//--------------------------------------------------------------------------------------------------
/// Helper function to create COMPSEGS headers
//--------------------------------------------------------------------------------------------------
std::vector<RifTextDataTableColumn> createCompsegsHeader( bool isLgrData )
{
if ( isLgrData )
{
return { RifTextDataTableColumn( "Grid" ),
RifTextDataTableColumn( "I" ),
RifTextDataTableColumn( "J" ),
RifTextDataTableColumn( "K" ),
RifTextDataTableColumn( "Branch no" ),
RifTextDataTableColumn( "Start Length" ),
RifTextDataTableColumn( "End Length" ),
RifTextDataTableColumn( "Dir Pen" ),
RifTextDataTableColumn( "End Range" ),
RifTextDataTableColumn( "Connection Depth" ) };
}
else
{
return { RifTextDataTableColumn( "I" ),
RifTextDataTableColumn( "J" ),
RifTextDataTableColumn( "K" ),
RifTextDataTableColumn( "Branch no" ),
RifTextDataTableColumn( "Start Length" ),
RifTextDataTableColumn( "End Length" ),
RifTextDataTableColumn( "Dir Pen" ),
RifTextDataTableColumn( "End Range" ),
RifTextDataTableColumn( "Connection Depth" ) };
}
}
//--------------------------------------------------------------------------------------------------
/// Helper function to create WSEGVALV headers
//--------------------------------------------------------------------------------------------------
std::vector<RifTextDataTableColumn> createWsegvalvHeader()
{
return { RifTextDataTableColumn( "Well" ),
RifTextDataTableColumn( "Seg No" ),
RifTextDataTableColumn( "Cv" ),
RifTextDataTableColumn( "Ac", RifTextDataTableDoubleFormatting( RIF_CONSISE, 4 ) ) };
}
//--------------------------------------------------------------------------------------------------
/// Helper function to create WELSEGS segment headers
//--------------------------------------------------------------------------------------------------
std::vector<RifTextDataTableColumn> createWelsegsSegmentHeader()
{
return { RifTextDataTableColumn( "First Seg" ),
RifTextDataTableColumn( "Last Seg" ),
RifTextDataTableColumn( "Branch No" ),
RifTextDataTableColumn( "Outlet Seg" ),
RifTextDataTableColumn( "Length" ),
RifTextDataTableColumn( "Depth Change" ),
RifTextDataTableColumn( "Diam" ),
RifTextDataTableColumn( "Rough", RifTextDataTableDoubleFormatting( RIF_FLOAT, 7 ) ) };
}
//--------------------------------------------------------------------------------------------------
/// Format WELSEGS table for a single well
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::formatWelsegsTable( RifTextDataTableFormatter& formatter, const RigMswTableData& tableData )
{
if ( !tableData.hasWelsegsData() ) return;
const auto& welsegsHeader = tableData.welsegsHeader();
formatter.keyword( "WELSEGS" );
std::vector<RifTextDataTableColumn> tableHeader = {
RifTextDataTableColumn( "Well" ),
RifTextDataTableColumn( "Dep 1" ),
RifTextDataTableColumn( "Tlen 1" ),
RifTextDataTableColumn( "Vol 1" ),
RifTextDataTableColumn( "Len&Dep" ),
RifTextDataTableColumn( "PresDrop" ),
};
formatter.header( tableHeader );
// Write header row
2025-11-05 16:27:19 +01:00
formatter.addStdString( welsegsHeader.well );
formatter.add( welsegsHeader.topDepth );
formatter.add( welsegsHeader.topLength );
formatter.addOptionalValue( welsegsHeader.wellboreVolume );
formatter.addStdString( welsegsHeader.infoType );
2025-11-13 14:53:14 +01:00
if ( welsegsHeader.pressureComponents.has_value() )
{
formatter.addStdString( "'" + welsegsHeader.pressureComponents.value() + "'" );
}
else
{
formatter.add( formatter.defaultMarker() );
}
2025-10-30 18:46:04 +01:00
formatter.rowCompleted();
// Column headers for segment data
auto segmentHeader = createWelsegsSegmentHeader();
formatter.header( segmentHeader );
// Write segment data using helper function
formatWelsegsRows( formatter, tableData.welsegsData() );
formatter.tableCompleted();
}
2025-11-13 14:53:14 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::addWsegaicdHeader( RifTextDataTableFormatter& formatter )
{
// Write out header for AICD table
std::vector<QString> columnDescriptions = { "Well Name",
"Segment Number",
"Segment Number",
"Strength of AICD",
"Flow Scaling Factor for AICD",
"Density of Calibration Fluid",
"Viscosity of Calibration Fluid",
"Critical water in liquid fraction for emulsions viscosity model",
"Emulsion viscosity transition region",
"Max ratio of emulsion viscosity to continuous phase viscosity",
"Flow scaling factor method",
"Maximum flow rate for AICD device",
"Volume flow rate exponent, x",
"Viscosity function exponent, y",
"Device OPEN/SHUT",
"Exponent of the oil flowing fraction in the density mixture calculation",
"Exponent of the water flowing fraction in the density mixture calculation",
"Exponent of the gas flowing fraction in the density mixture calculation",
"Exponent of the oil flowing fraction in the density viscosity calculation",
"Exponent of the water flowing fraction in the density viscosity calculation",
"Exponent of the gas flowing fraction in the density viscosity calculation" };
formatter.keyword( "WSEGAICD" );
formatter.comment( "Column Overview:" );
for ( size_t i = 0; i < columnDescriptions.size(); ++i )
{
formatter.comment( QString( "%1: %2" ).arg( i + 1, 2, 10, QChar( '0' ) ).arg( columnDescriptions[i] ) );
}
std::vector<RifTextDataTableColumn> header;
for ( size_t i = 1; i <= 21; ++i )
{
QString cName = QString( "%1" ).arg( i, 2, 10, QChar( '0' ) );
RifTextDataTableColumn col( cName, RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_CONSISE ), RIGHT );
header.push_back( col );
}
formatter.header( header );
}
2025-10-30 18:46:04 +01:00
//--------------------------------------------------------------------------------------------------
/// Format COMPSEGS table for a single well
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::formatCompsegsTable( RifTextDataTableFormatter& formatter, const RigMswTableData& tableData, bool isLgrData )
{
std::vector<CompsegsRow> rows;
if ( isLgrData )
{
rows = tableData.lgrCompsegsData();
}
else
{
rows = tableData.mainGridCompsegsData();
}
if ( rows.empty() ) return;
formatter.keyword( "COMPSEGS" );
// Add well name
{
std::vector<RifTextDataTableColumn> header = { RifTextDataTableColumn( "Name" ) };
formatter.header( header );
formatter.addStdString( tableData.wellName() );
formatter.rowCompleted();
}
auto header = createCompsegsHeader( isLgrData );
formatter.header( header );
formatCompsegsRows( formatter, rows, isLgrData );
formatter.tableCompleted();
}
//--------------------------------------------------------------------------------------------------
/// Format WSEGVALV table for a single well
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::formatWsegvalvTable( RifTextDataTableFormatter& formatter, const RigMswTableData& tableData )
{
if ( !tableData.hasWsegvalvData() ) return;
formatter.keyword( "WSEGVALV" );
auto header = createWsegvalvHeader();
formatter.header( header );
formatWsegvalvRows( formatter, tableData.wsegvalvData() );
formatter.tableCompleted();
}
//--------------------------------------------------------------------------------------------------
/// Format WSEGAICD table for a single well
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::formatWsegaicdTable( RifTextDataTableFormatter& formatter, const RigMswTableData& tableData )
{
if ( !tableData.hasWsegaicdData() ) return;
2025-11-13 14:53:14 +01:00
RifTextDataTableFormatter tightFormatter( formatter );
tightFormatter.setColumnSpacing( 1 );
tightFormatter.setTableRowPrependText( " " );
2025-10-30 18:46:04 +01:00
2025-11-13 14:53:14 +01:00
addWsegaicdHeader( tightFormatter );
formatWsegaicdRows( tightFormatter, tableData.wsegaicdData() );
tightFormatter.tableCompleted();
2025-10-30 18:46:04 +01:00
}
//--------------------------------------------------------------------------------------------------
/// Format WELSEGS table for unified data (multiple wells)
//--------------------------------------------------------------------------------------------------
2025-10-31 07:37:03 +01:00
void RigMswDataFormatter::formatWelsegsTable( RifTextDataTableFormatter& formatter, const RigMswUnifiedDataWIP& unifiedData )
2025-10-30 18:46:04 +01:00
{
2025-11-05 16:27:19 +01:00
// TODO: Handle multiple WELSEGS headers?
2025-10-30 18:46:04 +01:00
}
//--------------------------------------------------------------------------------------------------
/// Format WSEGVALV table for unified data (multiple wells)
//--------------------------------------------------------------------------------------------------
2025-10-31 07:37:03 +01:00
void RigMswDataFormatter::formatWsegvalvTable( RifTextDataTableFormatter& formatter, const RigMswUnifiedDataWIP& unifiedData )
2025-10-30 18:46:04 +01:00
{
auto rows = unifiedData.getAllWsegvalvRows();
if ( rows.empty() ) return;
formatter.keyword( "WSEGVALV" );
auto header = createWsegvalvHeader();
formatter.header( header );
formatWsegvalvRows( formatter, rows );
formatter.tableCompleted();
}
//--------------------------------------------------------------------------------------------------
/// Format WSEGAICD table for unified data (multiple wells)
//--------------------------------------------------------------------------------------------------
2025-10-31 07:37:03 +01:00
void RigMswDataFormatter::formatWsegaicdTable( RifTextDataTableFormatter& formatter, const RigMswUnifiedDataWIP& unifiedData )
2025-10-30 18:46:04 +01:00
{
auto rows = unifiedData.getAllWsegaicdRows();
if ( rows.empty() ) return;
2025-11-13 14:53:14 +01:00
RifTextDataTableFormatter tightFormatter( formatter );
tightFormatter.setColumnSpacing( 1 );
tightFormatter.setTableRowPrependText( " " );
2025-10-30 18:46:04 +01:00
2025-11-13 14:53:14 +01:00
addWsegaicdHeader( tightFormatter );
2025-10-30 18:46:04 +01:00
2025-11-13 14:53:14 +01:00
formatWsegaicdRows( tightFormatter, unifiedData.getAllWsegaicdRows() );
tightFormatter.tableCompleted();
2025-10-30 18:46:04 +01:00
}
//--------------------------------------------------------------------------------------------------
/// Format all MSW tables for a single well
//--------------------------------------------------------------------------------------------------
void RigMswDataFormatter::formatMswTables( RifTextDataTableFormatter& formatter, const RigMswTableData& tableData )
{
formatWelsegsTable( formatter, tableData );
formatCompsegsTable( formatter, tableData, false ); // Main grid
if ( tableData.hasLgrData() )
{
formatCompsegsTable( formatter, tableData, true ); // LGR data
}
formatWsegvalvTable( formatter, tableData );
formatWsegaicdTable( formatter, tableData );
}
//--------------------------------------------------------------------------------------------------
/// Format all MSW tables for unified data (multiple wells)
//--------------------------------------------------------------------------------------------------
2025-10-31 07:37:03 +01:00
void RigMswDataFormatter::formatMswTables( RifTextDataTableFormatter& formatter, const RigMswUnifiedDataWIP& unifiedData )
2025-10-30 18:46:04 +01:00
{
formatWelsegsTable( formatter, unifiedData );
for ( const auto& wellData : unifiedData.wellDataList() )
{
bool isLgrData = false;
formatCompsegsTable( formatter, wellData, isLgrData ); // Main grid, LGR is handeled separately
}
formatWsegvalvTable( formatter, unifiedData );
formatWsegaicdTable( formatter, unifiedData );
}