///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RicWellPathFractureTextReportFeatureImpl.h" #include "RiaApplication.h" #include "RiaRegressionTestRunner.h" #include "RicExportFractureCompletionsImpl.h" #include "RicWellPathFractureReportItem.h" #include "RifEclipseDataTableFormatter.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 "RimWellPathFracture.h" #include "RimWellPathFractureCollection.h" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString orientationText(RimFractureTemplate::FracOrientationEnum orientation) { return caf::AppEnum::uiText(orientation); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RifEclipseOutputTableColumn floatNumberColumn(const QString& text) { return RifEclipseOutputTableColumn(text, RifEclipseOutputTableDoubleFormatting(RIF_FLOAT, 3), RIGHT); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RicWellPathFractureTextReportFeatureImpl::wellPathFractureReport( RimEclipseCase* sourceCase, const std::vector& wellPaths, const std::vector& wellPathFractureReportItems) const { QString lineStart = "--"; QString text; QTextStream textStream(&text); textStream << lineStart << "========================================================================================================\n"; textStream << lineStart << " RESINSIGHT DATA\n"; textStream << lineStart << "\n"; std::vector stimPlanTemplates; std::vector ellipseTemplates; { auto proj = RiaApplication::instance()->project(); auto fractureTemplates = proj->activeOilField()->fractureDefinitionCollection()->fractureTemplates(); std::set 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(fracTemplate); if (stimPlanTemplate) { stimPlanTemplates.push_back(stimPlanTemplate); } auto ellipseTemplate = dynamic_cast(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 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 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 RicWellPathFractureTextReportFeatureImpl::wellPathsWithActiveFractures() { std::vector wellPaths; auto* wellPathColl = RimTools::wellPathCollection(); if (wellPathColl) { for (const auto& wellPath : wellPathColl->wellPaths()) { if (!wellPath->fractureCollection()->activeFractures().empty()) { wellPaths.push_back(wellPath); } } } return wellPaths; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RicWellPathFractureTextReportFeatureImpl::createWellFileLocationText(const std::vector& wellPaths) const { if (wellPaths.empty()) return ""; QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn("Well"), RifEclipseOutputTableColumn("Location"), }; formatter.header(header); formatter.addHorizontalLine('-'); if (!wellPaths.empty()) { for (const auto& wellPath : wellPaths) { auto fileWellPath = dynamic_cast(wellPath); if (fileWellPath) { formatter.add(wellPath->name()); formatter.add(fileWellPath->filepath()); formatter.rowCompleted(); } } } formatter.tableCompleted(); return tableText; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFileLocationText( const std::vector& stimPlanTemplates) const { if (stimPlanTemplates.empty()) return ""; QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn("StimPlan Name"), RifEclipseOutputTableColumn("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& stimPlanTemplates) const { if (stimPlanTemplates.empty()) return ""; QString tableText; RiaEclipseUnitTools::UnitSystem unitSystem = stimPlanTemplates.front()->fractureTemplateUnit(); bool isFieldUnits = unitSystem == RiaEclipseUnitTools::UNITS_FIELD; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn("StimPlan"), RifEclipseOutputTableColumn(" "), 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& ellipseTemplates) const { if (ellipseTemplates.empty()) return ""; QString tableText; RiaEclipseUnitTools::UnitSystem unitSystem = ellipseTemplates.front()->fractureTemplateUnit(); bool isFieldUnits = unitSystem == RiaEclipseUnitTools::UNITS_FIELD; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn("Ellipse"), RifEclipseOutputTableColumn(" "), 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& fractureTemplates) const { if (fractureTemplates.empty()) return ""; QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn(" "), 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& fractures) const { if (fractures.empty()) return ""; RiaEclipseUnitTools::UnitSystem unitSystem = fractures.front()->fractureUnit(); // Fix bool isFieldUnits = unitSystem == RiaEclipseUnitTools::UNITS_FIELD; QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = { RifEclipseOutputTableColumn(""), RifEclipseOutputTableColumn(""), RifEclipseOutputTableColumn(""), floatNumberColumn("MD"), floatNumberColumn("Dip"), floatNumberColumn("Tilt"), floatNumberColumn("LPerf"), floatNumberColumn("PerfEff"), floatNumberColumn("Wdia"), RifEclipseOutputTableColumn( "Dfac", RifEclipseOutputTableDoubleFormatting(RifEclipseOutputTableDoubleFormat::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->name(); } 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& wellPathFractureReportItems) const { QString tableText; RiaEclipseUnitTools::UnitSystem unitSystem = wellPathFractureReportItems.front().unitSystem(); bool isFieldUnits = unitSystem == RiaEclipseUnitTools::UNITS_FIELD; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); const QString meanText = "Mean"; std::vector header = { RifEclipseOutputTableColumn(""), // Well RifEclipseOutputTableColumn(""), // Fracture RifEclipseOutputTableColumn(""), // Template floatNumberColumn(""), // Tr floatNumberColumn(""), //#con floatNumberColumn(""), // Fcd RifEclipseOutputTableColumn("", RifEclipseOutputTableDoubleFormatting(RIF_FLOAT, 1), RIGHT), // Area RifEclipseOutputTableColumn(meanText, RifEclipseOutputTableDoubleFormatting(RIF_FLOAT, 1), RIGHT), // KfWf RifEclipseOutputTableColumn(meanText, RifEclipseOutputTableDoubleFormatting(RIF_FLOAT, 1), RIGHT), // Kf floatNumberColumn(meanText), // wf RifEclipseOutputTableColumn(meanText, RifEclipseOutputTableDoubleFormatting(RIF_FLOAT, 1), RIGHT), // xf RifEclipseOutputTableColumn(meanText, RifEclipseOutputTableDoubleFormatting(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& wellPathFractureReportItems) const { QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = {RifEclipseOutputTableColumn("Well"), RifEclipseOutputTableColumn("Fracture"), RifEclipseOutputTableColumn("Actual WBHP"), RifEclipseOutputTableColumn("Min Pressure Drop"), RifEclipseOutputTableColumn("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& wellPathFractureReportItems) const { QString tableText; QTextStream stream(&tableText); RifEclipseDataTableFormatter formatter(stream); configureFormatter(&formatter); std::vector header = {RifEclipseOutputTableColumn("Well"), floatNumberColumn("ConnCount")}; formatter.header(header); formatter.addHorizontalLine('-'); std::map 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(RifEclipseDataTableFormatter* formatter) const { if (!formatter) return; formatter->setColumnSpacing(3); formatter->setTableRowPrependText("-- "); formatter->setTableRowLineAppendText(""); }