ResInsight/ApplicationLibCode/Commands/CompletionExportCommands/RicWellPathFractureTextReportFeatureImpl.cpp
Magne Sjaastad 8bab748fa6 Improve MSW export including multi lateral wells
Based on branch https://github.com/OPM/ResInsight/tree/system-msw-refactor

- Move completion settings to property of well path
- Rename to RimFishbones
- Export implicit COMPSEGS for fishbones main bore
- Add valve for each branch
- Increase version number to be able to handle import of legacy project files
2021-02-26 14:59:57 +01:00

820 lines
28 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018- 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 "RicWellPathFractureTextReportFeatureImpl.h"
#include "RiaRegressionTestRunner.h"
#include "RicExportFractureCompletionsImpl.h"
#include "RicWellPathFractureReportItem.h"
#include "RifTextDataTableFormatter.h"
#include "RigCompletionData.h"
#include "RigTransmissibilityEquations.h"
#include "RimEclipseCase.h"
#include "RimEllipseFractureTemplate.h"
#include "RimFileWellPath.h"
#include "RimFractureContainment.h"
#include "RimFractureTemplate.h"
#include "RimFractureTemplateCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimStimPlanFractureTemplate.h"
#include "RimTools.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPathCompletions.h"
#include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString orientationText( RimFractureTemplate::FracOrientationEnum orientation )
{
return caf::AppEnum<RimFractureTemplate::FracOrientationEnum>::uiText( orientation );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifTextDataTableColumn floatNumberColumn( const QString& text )
{
return RifTextDataTableColumn( text, RifTextDataTableDoubleFormatting( RIF_FLOAT, 3 ), RIGHT );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::wellPathFractureReport(
RimEclipseCase* sourceCase,
const std::vector<RimWellPath*>& wellPaths,
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems ) const
{
QString lineStart = "--";
QString text;
QTextStream textStream( &text );
textStream
<< lineStart
<< "========================================================================================================\n";
textStream << lineStart << " RESINSIGHT DATA\n";
textStream << lineStart << "\n";
std::vector<RimStimPlanFractureTemplate*> stimPlanTemplates;
std::vector<RimEllipseFractureTemplate*> ellipseTemplates;
{
auto proj = RimProject::current();
auto fractureTemplates = proj->activeOilField()->fractureDefinitionCollection()->fractureTemplates();
std::set<QString> usedFractureTemplateNames;
for ( const auto& item : wellPathFractureReportItems )
{
usedFractureTemplateNames.insert( item.fractureTemplateName() );
}
for ( const auto fracTemplate : fractureTemplates )
{
if ( usedFractureTemplateNames.find( fracTemplate->name() ) == usedFractureTemplateNames.end() )
{
continue;
}
auto stimPlanTemplate = dynamic_cast<RimStimPlanFractureTemplate*>( fracTemplate );
if ( stimPlanTemplate )
{
stimPlanTemplates.push_back( stimPlanTemplate );
}
auto ellipseTemplate = dynamic_cast<RimEllipseFractureTemplate*>( fracTemplate );
if ( ellipseTemplate )
{
ellipseTemplates.push_back( ellipseTemplate );
}
}
}
if ( !RiaRegressionTestRunner::instance()->isRunningRegressionTests() )
{
if ( sourceCase )
{
textStream << lineStart << " Grid Model:\n";
textStream << lineStart << " " << sourceCase->gridFileName() << "\n";
textStream << lineStart << "\n";
}
{
QString tableText = createWellFileLocationText( wellPaths );
textStream << tableText;
textStream << lineStart << "\n";
}
{
QString tableText = createStimPlanFileLocationText( stimPlanTemplates );
textStream << tableText;
textStream << lineStart << "\n";
}
}
{
QString tableText = createEllipseFractureText( ellipseTemplates );
textStream << tableText;
textStream << lineStart << "\n";
}
{
QString tableText = createStimPlanFractureText( stimPlanTemplates );
textStream << tableText;
textStream << lineStart << "\n";
}
{
std::vector<RimFractureTemplate*> fracTemplates;
fracTemplates.insert( fracTemplates.end(), ellipseTemplates.begin(), ellipseTemplates.end() );
fracTemplates.insert( fracTemplates.end(), stimPlanTemplates.begin(), stimPlanTemplates.end() );
QString tableText = createFractureText( fracTemplates );
textStream << tableText;
textStream << lineStart << "\n";
}
{
std::vector<RimWellPathFracture*> wellPathFractures;
for ( const auto& w : wellPaths )
{
for ( const auto& frac : w->fractureCollection()->activeFractures() )
{
wellPathFractures.push_back( frac );
}
}
std::sort( wellPathFractures.begin(), wellPathFractures.end(), RimWellPathFracture::compareByWellPathNameAndMD );
{
QString tableText = createFractureInstancesText( wellPathFractures );
textStream << tableText;
textStream << lineStart << "\n";
}
{
QString tableText = createFractureCompletionSummaryText( wellPathFractureReportItems );
textStream << tableText;
textStream << lineStart << "\n";
}
{
QString tableText = createFracturePressureDepletionSummaryText( wellPathFractureReportItems );
textStream << tableText;
textStream << lineStart << "\n";
}
{
textStream << lineStart << " Maximum number of connections per well\n";
textStream << lineStart << "\n";
QString tableText = createConnectionsPerWellText( wellPathFractureReportItems );
textStream << tableText;
textStream << lineStart << "\n";
}
}
return text;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*> RicWellPathFractureTextReportFeatureImpl::wellPathsWithActiveFractures()
{
std::vector<RimWellPath*> wellPaths;
auto* wellPathColl = RimTools::wellPathCollection();
if ( wellPathColl )
{
for ( const auto& wellPath : wellPathColl->allWellPaths() )
{
if ( !wellPath->fractureCollection()->activeFractures().empty() )
{
wellPaths.push_back( wellPath );
}
}
}
return wellPaths;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createWellFileLocationText( const std::vector<RimWellPath*>& wellPaths ) const
{
if ( wellPaths.empty() ) return "";
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "Well" ),
RifTextDataTableColumn( "Location" ),
};
formatter.header( header );
formatter.addHorizontalLine( '-' );
if ( !wellPaths.empty() )
{
for ( const auto& wellPath : wellPaths )
{
auto fileWellPath = dynamic_cast<RimFileWellPath*>( wellPath );
if ( fileWellPath )
{
formatter.add( wellPath->completionSettings()->wellNameForExport() );
formatter.add( fileWellPath->filePath() );
formatter.rowCompleted();
}
}
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFileLocationText(
const std::vector<RimStimPlanFractureTemplate*>& stimPlanTemplates ) const
{
if ( stimPlanTemplates.empty() ) return "";
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "StimPlan Name" ),
RifTextDataTableColumn( "Location" ),
};
formatter.header( header );
formatter.addHorizontalLine( '-' );
if ( !stimPlanTemplates.empty() )
{
for ( const auto& stimPlanTemplate : stimPlanTemplates )
{
formatter.add( stimPlanTemplate->name() );
formatter.add( stimPlanTemplate->fileName() );
formatter.rowCompleted();
}
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFractureText(
const std::vector<RimStimPlanFractureTemplate*>& stimPlanTemplates ) const
{
if ( stimPlanTemplates.empty() ) return "";
QString tableText;
RiaDefines::EclipseUnitSystem unitSystem = stimPlanTemplates.front()->fractureTemplateUnit();
bool isFieldUnits = unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "StimPlan" ),
RifTextDataTableColumn( " " ),
floatNumberColumn( "WDiam" ),
floatNumberColumn( "Skin" ),
};
formatter.header( header );
// Second header line
{
formatter.add( "Template" ); // Template
formatter.add( "Orientation" ); // Orientation
formatter.add( isFieldUnits ? "[in]" : "[m]" ); // WDiam
formatter.add( "[] " ); // Skin
formatter.rowCompleted();
}
formatter.addHorizontalLine( '-' );
for ( const auto& stimPlanTemplate : stimPlanTemplates )
{
formatter.add( stimPlanTemplate->name() );
formatter.add( orientationText( stimPlanTemplate->orientationType() ) );
formatter.add( stimPlanTemplate->wellDiameter() );
formatter.add( stimPlanTemplate->skinFactor() );
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createEllipseFractureText(
const std::vector<RimEllipseFractureTemplate*>& ellipseTemplates ) const
{
if ( ellipseTemplates.empty() ) return "";
QString tableText;
RiaDefines::EclipseUnitSystem unitSystem = ellipseTemplates.front()->fractureTemplateUnit();
bool isFieldUnits = unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "Ellipse" ),
RifTextDataTableColumn( " " ),
floatNumberColumn( "Xf" ),
floatNumberColumn( "Height" ),
floatNumberColumn( "Kf" ),
floatNumberColumn( "Wf" ),
floatNumberColumn( "WDiam" ),
floatNumberColumn( "Skin" ),
};
formatter.header( header );
// Second header line
{
formatter.add( "Template" ); // Template
formatter.add( "Orientation" ); // Orientation
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // Xf
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // Height
formatter.add( "[mD]" ); // Kf
formatter.add( isFieldUnits ? "[in]" : "[m]" ); // Wf
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // WDiam
formatter.add( "[] " ); // Skin
formatter.rowCompleted();
}
formatter.addHorizontalLine( '-' );
for ( const auto& ellipseTemplate : ellipseTemplates )
{
formatter.add( ellipseTemplate->name() );
formatter.add( orientationText( ellipseTemplate->orientationType() ) );
formatter.add( ellipseTemplate->halfLength() );
formatter.add( ellipseTemplate->height() );
formatter.add(
RigTransmissibilityEquations::permeability( ellipseTemplate->conductivity(), ellipseTemplate->width() ) );
formatter.add( ellipseTemplate->width() );
formatter.add( ellipseTemplate->wellDiameter() );
formatter.add( ellipseTemplate->skinFactor() );
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createFractureText(
const std::vector<RimFractureTemplate*>& fractureTemplates ) const
{
if ( fractureTemplates.empty() ) return "";
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( " " ),
floatNumberColumn( "Top" ),
floatNumberColumn( "Bot" ),
floatNumberColumn( "Fault" ),
floatNumberColumn( "Height" ),
floatNumberColumn( "Half Length" ),
floatNumberColumn( "DFac" ),
floatNumberColumn( "Conductivity" ),
};
formatter.header( header );
// Second header line
{
formatter.add( "Template" );
formatter.add( "Cont" );
formatter.add( "Cont" );
formatter.add( "Truncation" );
formatter.add( "Scale" );
formatter.add( "Scale" );
formatter.add( "Scale" );
formatter.add( "Scale" );
formatter.rowCompleted();
}
formatter.addHorizontalLine( '-' );
for ( const auto& fracTemplate : fractureTemplates )
{
formatter.add( fracTemplate->name() );
if ( fracTemplate->fractureContainment()->isEnabled() )
{
formatter.add( fracTemplate->fractureContainment()->topKLayer() );
formatter.add( fracTemplate->fractureContainment()->baseKLayer() );
}
else
{
formatter.add( "N/A" );
formatter.add( "N/A" );
}
if ( fracTemplate->fractureContainment()->minimumFaultThrow() >= 0.0 )
{
formatter.add( fracTemplate->fractureContainment()->minimumFaultThrow() );
}
else
{
formatter.add( "N/A" );
}
double halfLengthScale, heightScale, dfactorScale, conductivityScale;
fracTemplate->scaleFactors( &halfLengthScale, &heightScale, &dfactorScale, &conductivityScale );
formatter.add( heightScale );
formatter.add( halfLengthScale );
formatter.add( dfactorScale );
formatter.add( conductivityScale );
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createFractureInstancesText(
const std::vector<RimWellPathFracture*>& fractures ) const
{
if ( fractures.empty() ) return "";
RiaDefines::EclipseUnitSystem unitSystem = fractures.front()->fractureUnit(); // Fix
bool isFieldUnits = unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD;
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "" ),
RifTextDataTableColumn( "" ),
RifTextDataTableColumn( "" ),
floatNumberColumn( "MD" ),
floatNumberColumn( "Dip" ),
floatNumberColumn( "Tilt" ),
floatNumberColumn( "LPerf" ),
floatNumberColumn( "PerfEff" ),
floatNumberColumn( "Wdia" ),
RifTextDataTableColumn( "Dfac", RifTextDataTableDoubleFormatting( RifTextDataTableDoubleFormat::RIF_SCIENTIFIC ), RIGHT ),
};
formatter.header( header );
// Second header line
{
formatter.add( "Well" );
formatter.add( "Fracture" );
formatter.add( "Template" );
formatter.add( "" ); // MD
formatter.add( "" ); // Dip
formatter.add( "" ); // Tilt
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // LPerf
formatter.add( "[]" ); // PerfEff
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // WDia
formatter.add( "[...]" ); // Dfac
formatter.rowCompleted();
}
formatter.addHorizontalLine( '-' );
for ( const auto& fracture : fractures )
{
fracture->ensureValidNonDarcyProperties();
QString wellName;
RimWellPath* wellPath = nullptr;
fracture->firstAncestorOrThisOfType( wellPath );
if ( wellPath )
{
wellName = wellPath->completionSettings()->wellNameForExport();
}
formatter.add( wellName );
formatter.add( fracture->name() );
if ( fracture->fractureTemplate() )
{
formatter.add( fracture->fractureTemplate()->name() );
}
else
{
formatter.add( "N/A" );
}
formatter.add( fracture->fractureMD() );
formatter.add( fracture->dip() );
formatter.add( fracture->tilt() );
if ( fracture->fractureTemplate() &&
fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH )
{
formatter.add( fracture->perforationLength() );
}
else
{
formatter.add( "N/A" );
}
formatter.add( fracture->perforationEfficiency() );
formatter.add( fracture->wellRadius() * 2.0 );
if ( fracture->fractureTemplate() && fracture->fractureTemplate()->isNonDarcyFlowEnabled() )
{
formatter.add( fracture->nonDarcyProperties().dFactor );
}
else
{
formatter.add( "N/A" );
}
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createFractureCompletionSummaryText(
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems ) const
{
QString tableText;
RiaDefines::EclipseUnitSystem unitSystem = wellPathFractureReportItems.front().unitSystem();
bool isFieldUnits = unitSystem == RiaDefines::EclipseUnitSystem::UNITS_FIELD;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
const QString meanText = "Mean";
std::vector<RifTextDataTableColumn> header = {
RifTextDataTableColumn( "" ), // Well
RifTextDataTableColumn( "" ), // Fracture
RifTextDataTableColumn( "" ), // Template
floatNumberColumn( "" ), // Tr
floatNumberColumn( "" ), //#con
floatNumberColumn( "" ), // Fcd
RifTextDataTableColumn( "", RifTextDataTableDoubleFormatting( RIF_FLOAT, 1 ), RIGHT ), // Area
RifTextDataTableColumn( meanText, RifTextDataTableDoubleFormatting( RIF_FLOAT, 1 ), RIGHT ), // KfWf
RifTextDataTableColumn( meanText, RifTextDataTableDoubleFormatting( RIF_FLOAT, 1 ), RIGHT ), // Kf
floatNumberColumn( meanText ), // wf
RifTextDataTableColumn( meanText, RifTextDataTableDoubleFormatting( RIF_FLOAT, 1 ), RIGHT ), // xf
RifTextDataTableColumn( meanText, RifTextDataTableDoubleFormatting( RIF_FLOAT, 1 ), RIGHT ), // H
floatNumberColumn( meanText ), // Km
};
formatter.header( header );
// Second header line
{
formatter.add( "" );
formatter.add( "" );
formatter.add( "" );
formatter.add( "Tr" ); // Tr
formatter.add( "#con" ); // #con
formatter.add( "Fcd" ); // Fcd
formatter.add( "Area" ); // Area
formatter.add( "KfWf" ); // KfWf
formatter.add( "Kf" ); // Kf
formatter.add( "wf" ); // wf
formatter.add( "Xf" ); // Xf
formatter.add( "H" ); // H
formatter.add( "Km" ); // Km
formatter.rowCompleted();
}
// Third header line
{
formatter.add( "Well" );
formatter.add( "Fracture" );
formatter.add( "Template" );
formatter.add( isFieldUnits ? "[cP.rb/day/psi]" : "[cP.rm3/day/bars]" ); // Tr
formatter.add( "" ); // #con
formatter.add( "[]" ); // Fcd
formatter.add( isFieldUnits ? "[ft2]" : "[m2]" ); // Area
formatter.add( isFieldUnits ? "[mDft]" : "[mDm]" ); // KfWf
formatter.add( "[mD]" ); // Kf
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // wf
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // Xf
formatter.add( isFieldUnits ? "[ft]" : "[m]" ); // H
formatter.add( "[mD]" ); // Km
formatter.rowCompleted();
}
formatter.addHorizontalLine( '-' );
for ( const auto& reportItem : wellPathFractureReportItems )
{
formatter.add( reportItem.wellPathNameForExport() );
formatter.add( reportItem.fractureName() );
formatter.add( reportItem.fractureTemplateName() );
formatter.add( reportItem.transmissibility() );
formatter.add( reportItem.connectionCount() );
formatter.add( reportItem.fcd() );
formatter.add( reportItem.area() );
formatter.add( reportItem.kfwf() ); // KfWf
formatter.add( reportItem.kf() ); // Kf
formatter.add( reportItem.wf() ); // wf
formatter.add( reportItem.xf() ); // Xf
formatter.add( reportItem.h() ); // H
formatter.add( reportItem.km() ); // Km
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createFracturePressureDepletionSummaryText(
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems ) const
{
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = { RifTextDataTableColumn( "Well" ),
RifTextDataTableColumn( "Fracture" ),
RifTextDataTableColumn( "Actual WBHP" ),
RifTextDataTableColumn( "Min Pressure Drop" ),
RifTextDataTableColumn( "Max Pressure Drop" ) };
bool createdTable = false;
for ( const auto& reportItem : wellPathFractureReportItems )
{
if ( reportItem.performPressureDepletionScaling() )
{
if ( !createdTable )
{
formatter.comment(
QString( "Pressure Depletion Time step: %1" ).arg( reportItem.pressureDepletionTimeStepString() ) );
formatter.comment( QString( "WBHP Source: %1" ).arg( reportItem.pressureDepletionWBHPString() ) );
formatter.comment( QString( "User Defined WBHP: %1" ).arg( reportItem.pressureDepletionUserWBHP() ) );
formatter.header( header );
formatter.addHorizontalLine( '-' );
createdTable = true;
}
formatter.add( reportItem.wellPathNameForExport() );
formatter.add( reportItem.fractureName() );
formatter.add( reportItem.pressureDepletionActualWBHP() );
formatter.add( reportItem.pressureDepletionMinPressureDrop() );
formatter.add( reportItem.pressureDepletionMaxPressureDrop() );
formatter.rowCompleted();
}
}
if ( createdTable )
{
formatter.tableCompleted();
}
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicWellPathFractureTextReportFeatureImpl::createConnectionsPerWellText(
const std::vector<RicWellPathFractureReportItem>& wellPathFractureReportItems ) const
{
QString tableText;
QTextStream stream( &tableText );
RifTextDataTableFormatter formatter( stream );
configureFormatter( &formatter );
std::vector<RifTextDataTableColumn> header = { RifTextDataTableColumn( "Well" ), floatNumberColumn( "ConnCount" ) };
formatter.header( header );
formatter.addHorizontalLine( '-' );
std::map<QString /*Well*/, size_t> wellConnectionCounts;
for ( const auto& reportItem : wellPathFractureReportItems )
{
QString wellPathName = reportItem.wellPathNameForExport();
if ( wellConnectionCounts.find( wellPathName ) == wellConnectionCounts.end() )
{
wellConnectionCounts.insert( std::make_pair( wellPathName, 0 ) );
}
wellConnectionCounts[wellPathName] += reportItem.connectionCount();
}
for ( const auto& connCount : wellConnectionCounts )
{
formatter.add( connCount.first );
formatter.add( connCount.second );
formatter.rowCompleted();
}
formatter.tableCompleted();
return tableText;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathFractureTextReportFeatureImpl::configureFormatter( RifTextDataTableFormatter* formatter ) const
{
if ( !formatter ) return;
formatter->setColumnSpacing( 3 );
formatter->setTableRowPrependText( "-- " );
formatter->setTableRowLineAppendText( "" );
}