ResInsight/ApplicationCode/Commands/CompletionExportCommands/RicWellPathExportMswCompletionsImpl.cpp
2018-12-18 17:09:43 +01:00

1786 lines
84 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 "RicWellPathExportMswCompletionsImpl.h"
#include "RiaLogging.h"
#include "RiaWeightedMeanCalculator.h"
#include "RicExportCompletionDataSettingsUi.h"
#include "RicExportFractureCompletionsImpl.h"
#include "RicMswExportInfo.h"
#include "RicMswValveAccumulators.h"
#include "RicWellPathExportCompletionsFileTools.h"
#include "RifEclipseDataTableFormatter.h"
#include "RigActiveCellInfo.h"
#include "RigEclipseCaseData.h"
#include "RigGridBase.h"
#include "RigMainGrid.h"
#include "RigWellLogExtractor.h"
#include "RigWellPath.h"
#include "RigWellPathIntersectionTools.h"
#include "RimEclipseCase.h"
#include "RimFishbonesCollection.h"
#include "RimFishbonesMultipleSubs.h"
#include "RimFractureTemplate.h"
#include "RimPerforationCollection.h"
#include "RimPerforationInterval.h"
#include "RimWellPath.h"
#include "RimWellPathCompletions.h"
#include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h"
#include "RimWellPathValve.h"
#include <QFile>
#include <algorithm>
//--------------------------------------------------------------------------------------------------
/// Internal definitions
//--------------------------------------------------------------------------------------------------
class SubSegmentIntersectionInfo
{
public:
SubSegmentIntersectionInfo(size_t globCellIndex,
double startTVD,
double endTVD,
double startMD,
double endMD,
cvf::Vec3d lengthsInCell);
static std::vector<SubSegmentIntersectionInfo>
spiltIntersectionSegmentsToMaxLength(const RigWellPath* pathGeometry,
const std::vector<WellPathCellIntersectionInfo>& intersections,
double maxSegmentLength);
static int numberOfSplittedSegments(double startMd, double endMd, double maxSegmentLength);
size_t globCellIndex;
double startTVD;
double endTVD;
double startMD;
double endMD;
cvf::Vec3d intersectionLengthsInCellCS;
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions(
const RicExportCompletionDataSettingsUi& exportSettings,
const std::vector<RimWellPath*>& wellPaths)
{
std::shared_ptr<QFile> unifiedExportFile;
if (exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::UNIFIED_FILE)
{
QString unifiedFileName = QString("UnifiedCompletions_MSW_%1").arg(exportSettings.caseToApply->caseUserDescription());
unifiedExportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, unifiedFileName);
}
for (const auto& wellPath : wellPaths)
{
std::shared_ptr<QFile> unifiedWellPathFile;
bool exportFractures = exportSettings.includeFractures() && !wellPath->fractureCollection()->activeFractures().empty();
bool exportPerforations =
exportSettings.includePerforations() && !wellPath->perforationIntervalCollection()->activePerforations().empty();
bool exportFishbones =
exportSettings.includeFishbones() && !wellPath->fishbonesCollection()->activeFishbonesSubs().empty();
bool exportAnyCompletion = exportFractures || exportPerforations || exportFishbones;
if (exportAnyCompletion && exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::SPLIT_ON_WELL &&
!unifiedWellPathFile)
{
QString wellFileName =
QString("%1_UnifiedCompletions_MSW_%2")
.arg(wellPath->completions()->wellNameForExport(), exportSettings.caseToApply->caseUserDescription());
unifiedWellPathFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, wellFileName);
}
if (exportFractures)
{
std::shared_ptr<QFile> fractureExportFile;
if (unifiedExportFile)
fractureExportFile = unifiedExportFile;
else if (unifiedWellPathFile)
fractureExportFile = unifiedWellPathFile;
else
{
QString fileName =
QString("%1_Fracture_MSW_%2")
.arg(wellPath->completions()->wellNameForExport(), exportSettings.caseToApply->caseUserDescription());
fractureExportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName);
}
exportWellSegmentsForFractures(
exportSettings.caseToApply, fractureExportFile, wellPath, wellPath->fractureCollection()->activeFractures());
}
if (exportPerforations)
{
std::shared_ptr<QFile> perforationsExportFile;
if (unifiedExportFile)
perforationsExportFile = unifiedExportFile;
else if (unifiedWellPathFile)
perforationsExportFile = unifiedWellPathFile;
else
{
QString fileName =
QString("%1_Perforation_MSW_%2")
.arg(wellPath->completions()->wellNameForExport(), exportSettings.caseToApply->caseUserDescription());
perforationsExportFile =
RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName);
}
exportWellSegmentsForPerforations(exportSettings.caseToApply,
perforationsExportFile,
wellPath,
exportSettings.timeStep,
wellPath->perforationIntervalCollection()->activePerforations());
}
if (exportFishbones)
{
std::shared_ptr<QFile> fishbonesExportFile;
if (unifiedExportFile)
fishbonesExportFile = unifiedExportFile;
else if (unifiedWellPathFile)
fishbonesExportFile = unifiedWellPathFile;
else
{
QString fileName =
QString("%1_Fishbones_MSW_%2")
.arg(wellPath->completions()->wellNameForExport(), exportSettings.caseToApply->caseUserDescription());
fishbonesExportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName);
}
exportWellSegmentsForFishbones(exportSettings.caseToApply,
fishbonesExportFile,
wellPath,
wellPath->fishbonesCollection()->activeFishbonesSubs());
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFractures(RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
const std::vector<RimWellPathFracture*>& fractures)
{
if (eclipseCase == nullptr)
{
RiaLogging::error("Export Fracture Well Segments: Cannot export completions data without specified eclipse case");
return;
}
RicMswExportInfo exportInfo = generateFracturesMswExportInfo(eclipseCase, wellPath, fractures);
QTextStream stream(exportFile.get());
RifEclipseDataTableFormatter formatter(stream);
generateWelsegsTable(formatter, exportInfo);
generateCompsegTables(formatter, exportInfo);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFishbones(
RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs)
{
if (eclipseCase == nullptr)
{
RiaLogging::error("Export Well Segments: Cannot export completions data without specified eclipse case");
return;
}
RicMswExportInfo exportInfo = generateFishbonesMswExportInfo(eclipseCase, wellPath, fishbonesSubs, true);
QTextStream stream(exportFile.get());
RifEclipseDataTableFormatter formatter(stream);
generateWelsegsTable(formatter, exportInfo);
generateCompsegTables(formatter, exportInfo);
generateWsegvalvTable(formatter, exportInfo);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForPerforations(
RimEclipseCase* eclipseCase,
std::shared_ptr<QFile> exportFile,
const RimWellPath* wellPath,
int timeStep,
const std::vector<const RimPerforationInterval*>& perforationIntervals)
{
if (eclipseCase == nullptr)
{
RiaLogging::error("Export Well Segments: Cannot export completions data without specified eclipse case");
return;
}
RicMswExportInfo exportInfo = generatePerforationsMswExportInfo(eclipseCase, wellPath, timeStep, perforationIntervals);
QTextStream stream(exportFile.get());
RifEclipseDataTableFormatter formatter(stream);
generateWelsegsTable(formatter, exportInfo);
generateCompsegTables(formatter, exportInfo);
generateWsegvalvTable(formatter, exportInfo);
generateWsegAicdTable(formatter, exportInfo);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWelsegsTable(RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo)
{
formatter.keyword("WELSEGS");
double startMD = exportInfo.initialMD();
double startTVD = exportInfo.initialTVD();
{
std::vector<RifEclipseOutputTableColumn> header = {
RifEclipseOutputTableColumn("Name"),
RifEclipseOutputTableColumn("Dep 1"),
RifEclipseOutputTableColumn("Tlen 1"),
RifEclipseOutputTableColumn("Vol 1"),
RifEclipseOutputTableColumn("Len&Dep"),
RifEclipseOutputTableColumn("PresDrop"),
};
formatter.header(header);
formatter.add(exportInfo.wellPath()->completions()->wellNameForExport());
formatter.add(startTVD);
formatter.add(startMD);
formatter.addValueOrDefaultMarker(exportInfo.topWellBoreVolume(), RicMswExportInfo::defaultDoubleValue());
formatter.add(exportInfo.lengthAndDepthText());
formatter.add(QString("'%1'").arg(exportInfo.pressureDropText()));
formatter.rowCompleted();
}
{
std::vector<RifEclipseOutputTableColumn> header = {
RifEclipseOutputTableColumn("First Seg"),
RifEclipseOutputTableColumn("Last Seg"),
RifEclipseOutputTableColumn("Branch Num"),
RifEclipseOutputTableColumn("Outlet Seg"),
RifEclipseOutputTableColumn("Length"),
RifEclipseOutputTableColumn("Depth Change"),
RifEclipseOutputTableColumn("Diam"),
RifEclipseOutputTableColumn("Rough"),
};
formatter.header(header);
}
{
double prevMD = exportInfo.initialMD();
double prevTVD = exportInfo.initialTVD();
formatter.comment("Main Stem Segments");
for (std::shared_ptr<RicMswSegment> location : exportInfo.wellSegmentLocations())
{
double depth = 0;
double length = 0;
if (exportInfo.lengthAndDepthText() == QString("INC"))
{
depth = location->endTVD() - prevTVD;
length = location->endMD() - prevMD;
}
else
{
depth = location->endTVD();
length = location->endMD();
}
if (location->subIndex() != cvf::UNDEFINED_SIZE_T)
{
QString comment = location->label() + QString(", sub %1").arg(location->subIndex());
formatter.comment(comment);
}
formatter.add(location->segmentNumber()).add(location->segmentNumber());
formatter.add(1); // All segments on main stem are branch 1
formatter.add(location->segmentNumber() - 1); // All main stem segments are connected to the segment below them
formatter.add(length);
formatter.add(depth);
formatter.add(exportInfo.linerDiameter());
formatter.add(exportInfo.roughnessFactor());
formatter.rowCompleted();
prevMD = location->endMD();
prevTVD = location->endTVD();
}
}
{
generateWelsegsSegments(formatter, exportInfo, {RigCompletionData::FISHBONES_ICD, RigCompletionData::FISHBONES});
generateWelsegsSegments(formatter, exportInfo, {RigCompletionData::FRACTURE});
generateWelsegsSegments(
formatter,
exportInfo,
{RigCompletionData::PERFORATION_ICD, RigCompletionData::PERFORATION_ICV, RigCompletionData::PERFORATION_AICD});
}
formatter.tableCompleted();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWelsegsSegments(
RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo,
const std::set<RigCompletionData::CompletionType>& exportCompletionTypes)
{
bool generatedHeader = false;
for (std::shared_ptr<RicMswSegment> segment : exportInfo.wellSegmentLocations())
{
for (std::shared_ptr<RicMswCompletion> completion : segment->completions())
{
if (exportCompletionTypes.count(completion->completionType()))
{
if (!generatedHeader)
{
generateWelsegsCompletionCommentHeader(formatter, completion->completionType());
generatedHeader = true;
}
if (RigCompletionData::isValve(completion->completionType()))
{
if (!completion->subSegments().empty())
{
formatter.comment(completion->label());
formatter.add(completion->subSegments().front()->segmentNumber());
formatter.add(completion->subSegments().front()->segmentNumber());
formatter.add(completion->branchNumber());
formatter.add(segment->segmentNumber());
formatter.add(0.1); // ICDs have 0.1 length
formatter.add(0); // Depth change
formatter.add(exportInfo.linerDiameter());
formatter.add(exportInfo.roughnessFactor());
formatter.rowCompleted();
}
}
else
{
if (completion->completionType() == RigCompletionData::FISHBONES)
{
formatter.comment(QString("%1 : Sub index %2 - %3")
.arg(segment->label())
.arg(segment->subIndex())
.arg(completion->label()));
}
else if (completion->completionType() == RigCompletionData::FRACTURE)
{
formatter.comment(QString("%1 connected to %2").arg(completion->label()).arg(segment->label()));
}
for (std::shared_ptr<RicMswSubSegment> subSegment : completion->subSegments())
{
double depth = 0;
double length = 0;
if (exportInfo.lengthAndDepthText() == QString("INC"))
{
depth = subSegment->deltaTVD();
length = subSegment->deltaMD();
}
else
{
depth = subSegment->endTVD();
length = subSegment->endMD();
}
double diameter = segment->effectiveDiameter();
formatter.add(subSegment->segmentNumber());
formatter.add(subSegment->segmentNumber());
formatter.add(completion->branchNumber());
formatter.add(subSegment->attachedSegmentNumber());
formatter.add(length);
formatter.add(depth);
formatter.add(diameter);
formatter.add(segment->openHoleRoughnessFactor());
formatter.rowCompleted();
}
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWelsegsCompletionCommentHeader(RifEclipseDataTableFormatter& formatter,
RigCompletionData::CompletionType completionType)
{
if (completionType == RigCompletionData::CT_UNDEFINED)
{
formatter.comment("Main stem");
}
else if (completionType == RigCompletionData::FISHBONES_ICD)
{
formatter.comment("Fishbone Laterals");
formatter.comment("Diam: MSW - Tubing Radius");
formatter.comment("Rough: MSW - Open Hole Roughness Factor");
}
else if (RigCompletionData::isPerforationValve(completionType))
{
formatter.comment("Perforation Valve Segments");
formatter.comment("Diam: MSW - Tubing Radius");
formatter.comment("Rough: MSW - Open Hole Roughness Factor");
}
else if (completionType == RigCompletionData::FRACTURE)
{
formatter.comment("Fracture Segments");
formatter.comment("Diam: MSW - Default Dummy");
formatter.comment("Rough: MSW - Default Dummy");
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegTables(RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo)
{
/*
* TODO: Creating the regular perforation COMPSEGS table should come in here, before the others
* should take precedence by appearing later in the output. See #3230.
*/
{
std::set<RigCompletionData::CompletionType> fishbonesTypes = {RigCompletionData::FISHBONES_ICD,
RigCompletionData::FISHBONES};
generateCompsegTable(formatter, exportInfo, false, fishbonesTypes);
if (exportInfo.hasSubGridIntersections())
{
generateCompsegTable(formatter, exportInfo, true, fishbonesTypes);
}
}
{
std::set<RigCompletionData::CompletionType> fractureTypes = {RigCompletionData::FRACTURE};
generateCompsegTable(formatter, exportInfo, false, fractureTypes);
if (exportInfo.hasSubGridIntersections())
{
generateCompsegTable(formatter, exportInfo, true, fractureTypes);
}
}
{
std::set<RigCompletionData::CompletionType> perforationTypes = {RigCompletionData::PERFORATION,
RigCompletionData::PERFORATION_ICD,
RigCompletionData::PERFORATION_ICV,
RigCompletionData::PERFORATION_AICD};
generateCompsegTable(formatter, exportInfo, false, perforationTypes);
if (exportInfo.hasSubGridIntersections())
{
generateCompsegTable(formatter, exportInfo, true, perforationTypes);
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegTable(
RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo,
bool exportSubGridIntersections,
const std::set<RigCompletionData::CompletionType>& exportCompletionTypes)
{
bool generatedHeader = false;
for (std::shared_ptr<RicMswSegment> location : exportInfo.wellSegmentLocations())
{
double startMD = location->startMD();
for (std::shared_ptr<RicMswCompletion> completion : location->completions())
{
if (!completion->subSegments().empty() && exportCompletionTypes.count(completion->completionType()))
{
if (!generatedHeader)
{
generateCompsegHeader(formatter, exportInfo, completion->completionType(), exportSubGridIntersections);
generatedHeader = true;
}
for (const std::shared_ptr<RicMswSubSegment>& subSegment : completion->subSegments())
{
if (completion->completionType() == RigCompletionData::FISHBONES_ICD)
{
startMD = subSegment->startMD();
}
for (const std::shared_ptr<RicMswSubSegmentCellIntersection>& intersection : subSegment->intersections())
{
bool isSubGridIntersection = !intersection->gridName().isEmpty();
if (isSubGridIntersection == exportSubGridIntersections)
{
if (exportSubGridIntersections)
{
formatter.add(intersection->gridName());
}
cvf::Vec3st ijk = intersection->gridLocalCellIJK();
formatter.addOneBasedCellIndex(ijk.x()).addOneBasedCellIndex(ijk.y()).addOneBasedCellIndex(ijk.z());
formatter.add(completion->branchNumber());
double startLength = subSegment->startMD();
if (exportInfo.lengthAndDepthText() == QString("INC") && completion->branchNumber() != 1)
{
startLength -= startMD;
}
formatter.add(startLength);
formatter.add(startLength + subSegment->deltaMD());
formatter.rowCompleted();
}
}
}
}
}
}
if (generatedHeader)
{
formatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateCompsegHeader(RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo,
RigCompletionData::CompletionType completionType,
bool exportSubGridIntersections)
{
if (exportSubGridIntersections)
{
formatter.keyword("COMPSEGL");
}
else
{
formatter.keyword("COMPSEGS");
}
if (completionType == RigCompletionData::FISHBONES_ICD)
{
formatter.comment("Fishbones");
}
else if (completionType == RigCompletionData::FRACTURE)
{
formatter.comment("Fractures");
}
{
std::vector<RifEclipseOutputTableColumn> header = {RifEclipseOutputTableColumn("Name")};
formatter.header(header);
formatter.add(exportInfo.wellPath()->completions()->wellNameForExport());
formatter.rowCompleted();
}
{
std::vector<RifEclipseOutputTableColumn> allHeaders;
if (exportSubGridIntersections)
{
allHeaders.push_back(RifEclipseOutputTableColumn("Grid"));
}
std::vector<RifEclipseOutputTableColumn> commonHeaders = {RifEclipseOutputTableColumn("I"),
RifEclipseOutputTableColumn("J"),
RifEclipseOutputTableColumn("K"),
RifEclipseOutputTableColumn("Branch no"),
RifEclipseOutputTableColumn("Start Length"),
RifEclipseOutputTableColumn("End Length"),
RifEclipseOutputTableColumn("Dir Pen"),
RifEclipseOutputTableColumn("End Range"),
RifEclipseOutputTableColumn("Connection Depth")};
allHeaders.insert(allHeaders.end(), commonHeaders.begin(), commonHeaders.end());
formatter.header(allHeaders);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWsegvalvTable(RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo)
{
bool foundValve = false;
for (std::shared_ptr<RicMswSegment> location : exportInfo.wellSegmentLocations())
{
for (std::shared_ptr<RicMswCompletion> completion : location->completions())
{
if (RigCompletionData::isWsegValveTypes(completion->completionType()))
{
if (!foundValve)
{
formatter.keyword("WSEGVALV");
std::vector<RifEclipseOutputTableColumn> header = {
RifEclipseOutputTableColumn("Well Name"),
RifEclipseOutputTableColumn("Seg No"),
RifEclipseOutputTableColumn("Cv"),
RifEclipseOutputTableColumn("Ac"),
};
formatter.header(header);
foundValve = true;
}
std::shared_ptr<RicMswWsegValve> icd = std::static_pointer_cast<RicMswWsegValve>(completion);
if (!icd->subSegments().empty())
{
CVF_ASSERT(icd->subSegments().size() == 1u);
if (icd->completionType() == RigCompletionData::PERFORATION_ICD ||
icd->completionType() == RigCompletionData::PERFORATION_ICV)
{
formatter.comment(icd->label());
}
formatter.add(exportInfo.wellPath()->completions()->wellNameForExport());
formatter.add(icd->subSegments().front()->segmentNumber());
formatter.add(icd->flowCoefficient());
formatter.add(QString("%1").arg(icd->area(), 8, 'g', 4));
formatter.rowCompleted();
}
}
}
}
if (foundValve)
{
formatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateWsegAicdTable(RifEclipseDataTableFormatter& formatter,
const RicMswExportInfo& exportInfo)
{
RifEclipseDataTableFormatter tighterFormatter(formatter);
tighterFormatter.setColumnSpacing(1);
tighterFormatter.setTableRowPrependText(" ");
bool foundValve = false;
for (std::shared_ptr<RicMswSegment> location : exportInfo.wellSegmentLocations())
{
for (std::shared_ptr<RicMswCompletion> completion : location->completions())
{
if (completion->completionType() == RigCompletionData::PERFORATION_AICD)
{
std::shared_ptr<RicMswPerforationAICD> aicd = std::static_pointer_cast<RicMswPerforationAICD>(completion);
if (!aicd->isValid())
{
RiaLogging::error(
QString("Export AICD Valve (%1): Valve is invalid. At least one required template parameter is not set.")
.arg(aicd->label()));
}
if (!foundValve)
{
std::vector<QString> columnDescriptions = {
"Well Name",
"Segment Number",
"Segment Number",
"Strength of AICD",
"Length of 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 flowrate 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"};
tighterFormatter.keyword("WSEGAICD");
tighterFormatter.comment("Column Overview:");
for (size_t i = 0; i < columnDescriptions.size(); ++i)
{
tighterFormatter.comment(QString("%1: %2").arg(i + 1, 2, 10, QChar('0')).arg(columnDescriptions[i]));
}
std::vector<RifEclipseOutputTableColumn> header;
for (size_t i = 1; i <= 21; ++i)
{
QString cName = QString("%1").arg(i, 2, 10, QChar('0'));
RifEclipseOutputTableColumn col(
cName, RifEclipseOutputTableDoubleFormatting(RifEclipseOutputTableDoubleFormat::RIF_CONSISE), RIGHT);
header.push_back(col);
}
tighterFormatter.header(header);
foundValve = true;
}
if (!aicd->subSegments().empty())
{
CVF_ASSERT(aicd->subSegments().size() == 1u);
tighterFormatter.comment(aicd->label());
tighterFormatter.add(exportInfo.wellPath()->completions()->wellNameForExport()); // 1
tighterFormatter.add(aicd->subSegments().front()->segmentNumber());
tighterFormatter.add(aicd->subSegments().front()->segmentNumber());
std::array<double, AICD_NUM_PARAMS> values = aicd->values();
tighterFormatter.add(values[AICD_STRENGTH]);
tighterFormatter.add(aicd->length()); // 5
tighterFormatter.add(values[AICD_DENSITY_CALIB_FLUID]);
tighterFormatter.add(values[AICD_VISCOSITY_CALIB_FLUID]);
tighterFormatter.addValueOrDefaultMarker(values[AICD_CRITICAL_WATER_IN_LIQUID_FRAC],
RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_EMULSION_VISC_TRANS_REGION],
RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_MAX_RATIO_EMULSION_VISC],
RicMswExportInfo::defaultDoubleValue()); // 10
tighterFormatter.add(1);
tighterFormatter.addValueOrDefaultMarker(values[AICD_MAX_FLOW_RATE], RicMswExportInfo::defaultDoubleValue());
tighterFormatter.add(values[AICD_VOL_FLOW_EXP]);
tighterFormatter.add(values[AICD_VISOSITY_FUNC_EXP]);
tighterFormatter.add(aicd->isOpen() ? "OPEN" : "SHUT"); // 15
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_OIL_FRAC_DENSITY], RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_WATER_FRAC_DENSITY],
RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_GAS_FRAC_DENSITY], RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_OIL_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue());
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_WATER_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue()); // 20
tighterFormatter.addValueOrDefaultMarker(values[AICD_EXP_GAS_FRAC_VISCOSITY],
RicMswExportInfo::defaultDoubleValue());
tighterFormatter.rowCompleted();
}
}
}
}
if (foundValve)
{
tighterFormatter.tableCompleted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(const RimEclipseCase* caseToApply,
const RimWellPath* wellPath,
bool enableSegmentSplitting)
{
std::vector<RimFishbonesMultipleSubs*> fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs();
return generateFishbonesMswExportInfo(caseToApply, wellPath, fishbonesSubs, enableSegmentSplitting);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(
const RimEclipseCase* caseToApply,
const RimWellPath* wellPath,
const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs,
bool enableSegmentSplitting)
{
RiaEclipseUnitTools::UnitSystem unitSystem = caseToApply->eclipseCaseData()->unitsType();
RicMswExportInfo exportInfo(wellPath,
unitSystem,
wellPath->fishbonesCollection()->startMD(),
wellPath->fishbonesCollection()->mswParameters()->lengthAndDepth().text(),
wellPath->fishbonesCollection()->mswParameters()->pressureDrop().text());
exportInfo.setLinerDiameter(wellPath->fishbonesCollection()->mswParameters()->linerDiameter(unitSystem));
exportInfo.setRoughnessFactor(wellPath->fishbonesCollection()->mswParameters()->roughnessFactor(unitSystem));
double maxSegmentLength = enableSegmentSplitting ? wellPath->fishbonesCollection()->mswParameters()->maxSegmentLength()
: std::numeric_limits<double>::infinity();
bool foundSubGridIntersections = false;
double subStartMD = wellPath->fishbonesCollection()->startMD();
for (RimFishbonesMultipleSubs* subs : fishbonesSubs)
{
for (auto& sub : subs->installedLateralIndices())
{
double subEndMD = subs->measuredDepth(sub.subIndex);
double subEndTVD = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath(subEndMD).z();
int subSegCount = SubSegmentIntersectionInfo::numberOfSplittedSegments(subStartMD, subEndMD, maxSegmentLength);
double subSegLen = (subEndMD - subStartMD) / subSegCount;
double startMd = subStartMD;
double startTvd = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath(startMd).z();
for (int ssi = 0; ssi < subSegCount; ssi++)
{
double endMd = startMd + subSegLen;
double endTvd = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath(endMd).z();
std::shared_ptr<RicMswSegment> location(
new RicMswSegment(subs->generatedName(), startMd, endMd, startTvd, endTvd, sub.subIndex));
location->setEffectiveDiameter(subs->effectiveDiameter(unitSystem));
location->setHoleDiameter(subs->holeDiameter(unitSystem));
location->setOpenHoleRoughnessFactor(subs->openHoleRoughnessFactor(unitSystem));
location->setSkinFactor(subs->skinFactor());
location->setSourcePdmObject(subs);
if (ssi == 0)
{
// Add completion for ICD
std::shared_ptr<RicMswFishbonesICD> icdCompletion(new RicMswFishbonesICD(QString("ICD"), nullptr));
std::shared_ptr<RicMswSubSegment> icdSegment(
new RicMswSubSegment(subEndMD, subEndMD + 0.1, subEndTVD, subEndTVD));
icdCompletion->setFlowCoefficient(subs->icdFlowCoefficient());
double icdOrificeRadius = subs->icdOrificeDiameter(unitSystem) / 2;
icdCompletion->setArea(icdOrificeRadius * icdOrificeRadius * cvf::PI_D * subs->icdCount());
icdCompletion->addSubSegment(icdSegment);
location->addCompletion(icdCompletion);
for (size_t lateralIndex : sub.lateralIndices)
{
QString label = QString("Lateral %1").arg(lateralIndex);
location->addCompletion(std::make_shared<RicMswFishbones>(label, lateralIndex));
}
assignFishbonesLateralIntersections(
caseToApply, subs, location, &foundSubGridIntersections, maxSegmentLength);
}
exportInfo.addWellSegment(location);
startMd = endMd;
startTvd = endTvd;
}
subStartMD = subEndMD;
}
}
exportInfo.setHasSubGridIntersections(foundSubGridIntersections);
exportInfo.sortLocations();
assignBranchAndSegmentNumbers(caseToApply, &exportInfo);
return exportInfo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo(RimEclipseCase* caseToApply,
const RimWellPath* wellPath)
{
std::vector<RimWellPathFracture*> fractures = wellPath->fractureCollection()->activeFractures();
return generateFracturesMswExportInfo(caseToApply, wellPath, fractures);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo
RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo(RimEclipseCase* caseToApply,
const RimWellPath* wellPath,
const std::vector<RimWellPathFracture*>& fractures)
{
const RigMainGrid* grid = caseToApply->eclipseCaseData()->mainGrid();
const RigActiveCellInfo* activeCellInfo = caseToApply->eclipseCaseData()->activeCellInfo(RiaDefines::MATRIX_MODEL);
RiaEclipseUnitTools::UnitSystem unitSystem = caseToApply->eclipseCaseData()->unitsType();
const RigWellPath* wellPathGeometry = wellPath->wellPathGeometry();
const std::vector<cvf::Vec3d>& coords = wellPathGeometry->wellPathPoints();
const std::vector<double>& mds = wellPathGeometry->measureDepths();
CVF_ASSERT(!coords.empty() && !mds.empty());
std::vector<WellPathCellIntersectionInfo> intersections =
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(caseToApply->eclipseCaseData(), coords, mds);
double maxSegmentLength = wellPath->fractureCollection()->mswParameters()->maxSegmentLength();
std::vector<SubSegmentIntersectionInfo> subSegIntersections =
SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(wellPathGeometry, intersections, maxSegmentLength);
double initialMD = 0.0;
if (wellPath->fractureCollection()->mswParameters()->referenceMDType() == RimMswCompletionParameters::MANUAL_REFERENCE_MD)
{
initialMD = wellPath->fractureCollection()->mswParameters()->manualReferenceMD();
}
else
{
for (WellPathCellIntersectionInfo intersection : intersections)
{
if (activeCellInfo->isActive(intersection.globCellIndex))
{
initialMD = intersection.startMD;
break;
}
}
}
RicMswExportInfo exportInfo(wellPath,
unitSystem,
initialMD,
wellPath->fractureCollection()->mswParameters()->lengthAndDepth().text(),
wellPath->fractureCollection()->mswParameters()->pressureDrop().text());
exportInfo.setLinerDiameter(wellPath->fractureCollection()->mswParameters()->linerDiameter(unitSystem));
exportInfo.setRoughnessFactor(wellPath->fractureCollection()->mswParameters()->roughnessFactor(unitSystem));
bool foundSubGridIntersections = false;
// Main bore
int mainBoreSegment = 1;
for (const auto& cellIntInfo : subSegIntersections)
{
double startTVD = cellIntInfo.startTVD;
double endTVD = cellIntInfo.endTVD;
size_t localGridIdx = 0u;
const RigGridBase* localGrid = grid->gridAndGridLocalIdxFromGlobalCellIdx(cellIntInfo.globCellIndex, &localGridIdx);
QString gridName;
if (localGrid != grid)
{
gridName = QString::fromStdString(localGrid->gridName());
foundSubGridIntersections = true;
}
size_t i = 0u, j = 0u, k = 0u;
localGrid->ijkFromCellIndex(localGridIdx, &i, &j, &k);
QString label = QString("Main stem segment %1").arg(++mainBoreSegment);
std::shared_ptr<RicMswSegment> location(
new RicMswSegment(label, cellIntInfo.startMD, cellIntInfo.endMD, startTVD, endTVD));
// Check if fractures are to be assigned to current main bore segment
for (RimWellPathFracture* fracture : fractures)
{
double fractureStartMD = fracture->fractureMD();
if (fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH)
{
double perforationLength = fracture->fractureTemplate()->perforationLength();
fractureStartMD -= 0.5 * perforationLength;
}
if (cvf::Math::valueInRange(fractureStartMD, cellIntInfo.startMD, cellIntInfo.endMD))
{
std::vector<RigCompletionData> completionData =
RicExportFractureCompletionsImpl::generateCompdatValues(caseToApply,
wellPath->completions()->wellNameForExport(),
wellPath->wellPathGeometry(),
{fracture},
nullptr,
nullptr);
assignFractureIntersections(caseToApply, fracture, completionData, location, &foundSubGridIntersections);
}
}
exportInfo.addWellSegment(location);
}
exportInfo.setHasSubGridIntersections(foundSubGridIntersections);
exportInfo.sortLocations();
assignBranchAndSegmentNumbers(caseToApply, &exportInfo);
return exportInfo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicMswExportInfo RicWellPathExportMswCompletionsImpl::generatePerforationsMswExportInfo(
RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
int timeStep,
const std::vector<const RimPerforationInterval*>& perforationIntervals)
{
const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo(RiaDefines::MATRIX_MODEL);
RiaEclipseUnitTools::UnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
const RigWellPath* wellPathGeometry = wellPath->wellPathGeometry();
const std::vector<cvf::Vec3d>& coords = wellPathGeometry->wellPathPoints();
const std::vector<double>& mds = wellPathGeometry->measureDepths();
CVF_ASSERT(!coords.empty() && !mds.empty());
std::vector<WellPathCellIntersectionInfo> intersections =
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(eclipseCase->eclipseCaseData(), coords, mds);
double maxSegmentLength = wellPath->perforationIntervalCollection()->mswParameters()->maxSegmentLength();
std::vector<SubSegmentIntersectionInfo> subSegIntersections =
SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(wellPathGeometry, intersections, maxSegmentLength);
double initialMD = 0.0;
if (wellPath->perforationIntervalCollection()->mswParameters()->referenceMDType() == RimMswCompletionParameters::MANUAL_REFERENCE_MD)
{
initialMD = wellPath->perforationIntervalCollection()->mswParameters()->manualReferenceMD();
}
else
{
for (WellPathCellIntersectionInfo intersection : intersections)
{
if (activeCellInfo->isActive(intersection.globCellIndex))
{
initialMD = intersection.startMD;
break;
}
}
}
RicMswExportInfo exportInfo(wellPath,
unitSystem,
initialMD,
wellPath->perforationIntervalCollection()->mswParameters()->lengthAndDepth().text(),
wellPath->perforationIntervalCollection()->mswParameters()->pressureDrop().text());
exportInfo.setLinerDiameter(wellPath->perforationIntervalCollection()->mswParameters()->linerDiameter(unitSystem));
exportInfo.setRoughnessFactor(wellPath->perforationIntervalCollection()->mswParameters()->roughnessFactor(unitSystem));
bool foundSubGridIntersections = false;
MainBoreSegments mainBoreSegments = createMainBoreSegmentsForPerforations(
subSegIntersections, perforationIntervals, wellPath, timeStep, eclipseCase, &foundSubGridIntersections);
createValveCompletions(mainBoreSegments, perforationIntervals, unitSystem);
assignValveContributionsToSuperICDsOrAICDs(mainBoreSegments, perforationIntervals, unitSystem);
moveIntersectionsToICVs(mainBoreSegments, perforationIntervals, unitSystem);
moveIntersectionsToSuperICDsOrAICDs(mainBoreSegments);
for (std::shared_ptr<RicMswSegment> segment : mainBoreSegments)
{
exportInfo.addWellSegment(segment);
}
exportInfo.setHasSubGridIntersections(foundSubGridIntersections);
exportInfo.sortLocations();
assignBranchAndSegmentNumbers(eclipseCase, &exportInfo);
return exportInfo;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicWellPathExportMswCompletionsImpl::MainBoreSegments RicWellPathExportMswCompletionsImpl::createMainBoreSegmentsForPerforations(
const std::vector<SubSegmentIntersectionInfo>& subSegIntersections,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
const RimWellPath* wellPath,
int timeStep,
RimEclipseCase* eclipseCase,
bool* foundSubGridIntersections)
{
MainBoreSegments mainBoreSegments;
for (const auto& cellIntInfo : subSegIntersections)
{
if (std::fabs(cellIntInfo.endMD - cellIntInfo.startMD) > 1.0e-8)
{
QString label = QString("Main stem segment %1").arg(mainBoreSegments.size() + 2);
std::shared_ptr<RicMswSegment> segment(
new RicMswSegment(label, cellIntInfo.startMD, cellIntInfo.endMD, cellIntInfo.startTVD, cellIntInfo.endTVD));
for (const RimPerforationInterval* interval : perforationIntervals)
{
double overlapStart = std::max(interval->startMD(), segment->startMD());
double overlapEnd = std::min(interval->endMD(), segment->endMD());
double overlap = std::max(0.0, overlapEnd - overlapStart);
if (overlap > 0.0)
{
std::shared_ptr<RicMswCompletion> intervalCompletion(new RicMswPerforation(interval->name()));
std::vector<RigCompletionData> completionData =
generatePerforationIntersections(wellPath, interval, timeStep, eclipseCase);
assignPerforationIntersections(
completionData, intervalCompletion, cellIntInfo, overlapStart, overlapEnd, foundSubGridIntersections);
segment->addCompletion(intervalCompletion);
}
}
mainBoreSegments.push_back(segment);
}
}
return mainBoreSegments;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::createValveCompletions(
std::vector<std::shared_ptr<RicMswSegment>>& mainBoreSegments,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
RiaEclipseUnitTools::UnitSystem unitSystem)
{
for (size_t nMainSegment = 0u; nMainSegment < mainBoreSegments.size(); ++nMainSegment)
{
std::shared_ptr<RicMswSegment> segment = mainBoreSegments[nMainSegment];
std::shared_ptr<RicMswPerforationICV> ICV;
std::shared_ptr<RicMswPerforationICD> superICD;
std::shared_ptr<RicMswPerforationAICD> superAICD;
double totalICDOverlap = 0.0;
double totalAICDOverlap = 0.0;
for (const RimPerforationInterval* interval : perforationIntervals)
{
if (!interval->isChecked()) continue;
std::vector<const RimWellPathValve*> perforationValves;
interval->descendantsIncludingThisOfType(perforationValves);
for (const RimWellPathValve* valve : perforationValves)
{
if (!valve->isChecked()) continue;
for (size_t nSubValve = 0u; nSubValve < valve->valveLocations().size(); ++nSubValve)
{
double valveMD = valve->valveLocations()[nSubValve];
std::pair<double, double> valveSegment = valve->valveSegments()[nSubValve];
double overlapStart = std::max(valveSegment.first, segment->startMD());
double overlapEnd = std::min(valveSegment.second, segment->endMD());
double overlap = std::max(0.0, overlapEnd - overlapStart);
if (segment->startMD() <= valveMD && valveMD < segment->endMD())
{
if (valve->componentType() == RiaDefines::AICD)
{
QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2);
std::shared_ptr<RicMswSubSegment> subSegment(new RicMswSubSegment(valveMD, valveMD + 0.1, 0.0, 0.0));
superAICD = std::make_shared<RicMswPerforationAICD>(valveLabel, valve);
superAICD->addSubSegment(subSegment);
}
else if (valve->componentType() == RiaDefines::ICD)
{
QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2);
std::shared_ptr<RicMswSubSegment> subSegment(new RicMswSubSegment(valveMD, valveMD + 0.1, 0.0, 0.0));
superICD = std::make_shared<RicMswPerforationICD>(valveLabel, valve);
superICD->addSubSegment(subSegment);
}
else if (valve->componentType() == RiaDefines::ICV)
{
QString valveLabel = QString("ICV %1 at segment #%2").arg(valve->name()).arg(nMainSegment + 2);
std::shared_ptr<RicMswSubSegment> subSegment(new RicMswSubSegment(valveMD, valveMD + 0.1, 0.0, 0.0));
ICV = std::make_shared<RicMswPerforationICV>(valveLabel, valve);
ICV->addSubSegment(subSegment);
ICV->setFlowCoefficient(valve->flowCoefficient());
double orificeRadius = valve->orificeDiameter(unitSystem) / 2;
ICV->setArea(orificeRadius * orificeRadius * cvf::PI_D);
}
}
else if (overlap > 0.0 && (valve->componentType() == RiaDefines::ICD && !superICD))
{
QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2);
std::shared_ptr<RicMswSubSegment> subSegment(
new RicMswSubSegment(overlapStart, overlapStart + 0.1, 0.0, 0.0));
superICD = std::make_shared<RicMswPerforationICD>(valveLabel, valve);
superICD->addSubSegment(subSegment);
}
else if (overlap > 0.0 && (valve->componentType() == RiaDefines::AICD && !superAICD))
{
QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2);
std::shared_ptr<RicMswSubSegment> subSegment(
new RicMswSubSegment(overlapStart, overlapStart + 0.1, 0.0, 0.0));
superAICD = std::make_shared<RicMswPerforationAICD>(valveLabel, valve);
superAICD->addSubSegment(subSegment);
}
if (valve->componentType() == RiaDefines::AICD)
{
totalAICDOverlap += overlap;
}
else if (valve->componentType() == RiaDefines::ICD)
{
totalICDOverlap += overlap;
}
}
}
}
if (ICV)
{
segment->addCompletion(ICV);
}
else
{
if (totalICDOverlap > 0.0 || totalAICDOverlap > 0.0)
{
if (totalAICDOverlap > totalICDOverlap)
{
segment->addCompletion(superAICD);
}
else
{
segment->addCompletion(superICD);
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignValveContributionsToSuperICDsOrAICDs(
const std::vector<std::shared_ptr<RicMswSegment>>& mainBoreSegments,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
RiaEclipseUnitTools::UnitSystem unitSystem)
{
ValveContributionMap assignedRegularValves;
for (std::shared_ptr<RicMswSegment> segment : mainBoreSegments)
{
std::shared_ptr<RicMswValve> superValve;
for (auto completion : segment->completions())
{
std::shared_ptr<RicMswValve> valve = std::dynamic_pointer_cast<RicMswValve>(completion);
if (valve)
{
superValve = valve;
break;
}
}
std::shared_ptr<RicMswValveAccumulator> accumulator;
if (std::dynamic_pointer_cast<const RicMswPerforationICD>(superValve))
{
accumulator = std::make_shared<RicMswICDAccumulator>(unitSystem);
}
else if (std::dynamic_pointer_cast<const RicMswPerforationAICD>(superValve))
{
accumulator = std::make_shared<RicMswAICDAccumulator>(unitSystem);
}
if (!accumulator) continue;
for (const RimPerforationInterval* interval : perforationIntervals)
{
if (!interval->isChecked()) continue;
std::vector<const RimWellPathValve*> perforationValves;
interval->descendantsIncludingThisOfType(perforationValves);
for (const RimWellPathValve* valve : perforationValves)
{
if (!valve->isChecked()) continue;
for (size_t nSubValve = 0u; nSubValve < valve->valveSegments().size(); ++nSubValve)
{
std::pair<double, double> valveSegment = valve->valveSegments()[nSubValve];
double valveSegmentLength = valveSegment.second - valveSegment.first;
double overlapStart = std::max(valveSegment.first, segment->startMD());
double overlapEnd = std::min(valveSegment.second, segment->endMD());
double overlap = std::max(0.0, overlapEnd - overlapStart);
if (overlap > 0.0 && accumulator)
{
if (accumulator->accumulateValveParameters(valve, nSubValve, overlap / valveSegmentLength))
{
assignedRegularValves[superValve].insert(std::make_pair(valve, nSubValve));
}
}
}
}
}
if (superValve && accumulator)
{
accumulator->applyToSuperValve(superValve);
}
}
for (auto regularValvePair : assignedRegularValves)
{
if (!regularValvePair.second.empty())
{
QStringList valveLabels;
for (std::pair<const RimWellPathValve*, size_t> regularValve : regularValvePair.second)
{
QString valveLabel = QString("%1 #%2").arg(regularValve.first->name()).arg(regularValve.second + 1);
valveLabels.push_back(valveLabel);
}
QString valveContribLabel = QString(" with contribution from: %1").arg(valveLabels.join(", "));
regularValvePair.first->setLabel(regularValvePair.first->label() + valveContribLabel);
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToICVs(
const std::vector<std::shared_ptr<RicMswSegment>>& mainBoreSegments,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
RiaEclipseUnitTools::UnitSystem unitSystem)
{
std::map<const RimWellPathValve*, std::shared_ptr<RicMswPerforationICV>> icvCompletionMap;
for (std::shared_ptr<RicMswSegment> segment : mainBoreSegments)
{
for (auto completion : segment->completions())
{
std::shared_ptr<RicMswPerforationICV> icv = std::dynamic_pointer_cast<RicMswPerforationICV>(completion);
if (icv)
{
icvCompletionMap[icv->wellPathValve()] = icv;
}
}
}
for (std::shared_ptr<RicMswSegment> segment : mainBoreSegments)
{
std::vector<std::shared_ptr<RicMswCompletion>> perforations;
for (auto completionPtr : segment->completions())
{
if (completionPtr->completionType() == RigCompletionData::PERFORATION)
{
perforations.push_back(completionPtr);
}
}
for (const RimPerforationInterval* interval : perforationIntervals)
{
if (!interval->isChecked()) continue;
std::vector<const RimWellPathValve*> perforationValves;
interval->descendantsIncludingThisOfType(perforationValves);
for (const RimWellPathValve* valve : perforationValves)
{
if (!valve->isChecked()) continue;
if (valve->componentType() != RiaDefines::ICV) continue;
auto icvIt = icvCompletionMap.find(valve);
if (icvIt == icvCompletionMap.end()) continue;
std::shared_ptr<RicMswPerforationICV> icvCompletion = icvIt->second;
CVF_ASSERT(icvCompletion);
std::pair<double, double> valveSegment = valve->valveSegments().front();
double overlapStart = std::max(valveSegment.first, segment->startMD());
double overlapEnd = std::min(valveSegment.second, segment->endMD());
double overlap = std::max(0.0, overlapEnd - overlapStart);
if (overlap > 0.0)
{
CVF_ASSERT(icvCompletion->subSegments().size() == 1u);
for (auto perforationPtr : perforations)
{
for (auto subSegmentPtr : perforationPtr->subSegments())
{
for (auto intersectionPtr : subSegmentPtr->intersections())
{
icvCompletion->subSegments()[0]->addIntersection(intersectionPtr);
}
}
segment->removeCompletion(perforationPtr);
}
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToSuperICDsOrAICDs(MainBoreSegments mainBoreSegments)
{
for (auto segmentPtr : mainBoreSegments)
{
std::shared_ptr<RicMswCompletion> superValve;
std::vector<std::shared_ptr<RicMswCompletion>> perforations;
for (auto completionPtr : segmentPtr->completions())
{
if (RigCompletionData::isPerforationValve(completionPtr->completionType()))
{
superValve = completionPtr;
}
else
{
CVF_ASSERT(completionPtr->completionType() == RigCompletionData::PERFORATION);
perforations.push_back(completionPtr);
}
}
if (superValve == nullptr) continue;
CVF_ASSERT(superValve->subSegments().size() == 1u);
segmentPtr->completions().clear();
segmentPtr->addCompletion(superValve);
for (auto perforationPtr : perforations)
{
for (auto subSegmentPtr : perforationPtr->subSegments())
{
for (auto intersectionPtr : subSegmentPtr->intersections())
{
superValve->subSegments()[0]->addIntersection(intersectionPtr);
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignFishbonesLateralIntersections(const RimEclipseCase* caseToApply,
const RimFishbonesMultipleSubs* fishbonesSubs,
std::shared_ptr<RicMswSegment> location,
bool* foundSubGridIntersections,
double maxSegmentLength)
{
CVF_ASSERT(foundSubGridIntersections != nullptr);
const RigMainGrid* grid = caseToApply->eclipseCaseData()->mainGrid();
for (std::shared_ptr<RicMswCompletion> completion : location->completions())
{
if (completion->completionType() != RigCompletionData::FISHBONES)
{
continue;
}
std::vector<std::pair<cvf::Vec3d, double>> lateralCoordMDPairs =
fishbonesSubs->coordsAndMDForLateral(location->subIndex(), completion->index());
if (lateralCoordMDPairs.empty())
{
continue;
}
std::vector<cvf::Vec3d> lateralCoords;
std::vector<double> lateralMDs;
lateralCoords.reserve(lateralCoordMDPairs.size());
lateralMDs.reserve(lateralCoordMDPairs.size());
for (auto& coordMD : lateralCoordMDPairs)
{
lateralCoords.push_back(coordMD.first);
lateralMDs.push_back(coordMD.second);
}
std::vector<WellPathCellIntersectionInfo> intersections =
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(
caseToApply->eclipseCaseData(), lateralCoords, lateralMDs);
RigWellPath pathGeometry;
pathGeometry.m_wellPathPoints = lateralCoords;
pathGeometry.m_measuredDepths = lateralMDs;
std::vector<SubSegmentIntersectionInfo> subSegIntersections =
SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(&pathGeometry, intersections, maxSegmentLength);
double previousExitMD = lateralMDs.front();
double previousExitTVD = -lateralCoords.front().z();
for (const auto& cellIntInfo : subSegIntersections)
{
size_t localGridIdx = 0u;
const RigGridBase* localGrid = grid->gridAndGridLocalIdxFromGlobalCellIdx(cellIntInfo.globCellIndex, &localGridIdx);
QString gridName;
if (localGrid != grid)
{
gridName = QString::fromStdString(localGrid->gridName());
*foundSubGridIntersections = true;
}
size_t i = 0u, j = 0u, k = 0u;
localGrid->ijkFromCellIndex(localGridIdx, &i, &j, &k);
std::shared_ptr<RicMswSubSegment> subSegment(
new RicMswSubSegment(previousExitMD, cellIntInfo.endMD, previousExitTVD, cellIntInfo.endTVD));
std::shared_ptr<RicMswSubSegmentCellIntersection> intersection(new RicMswSubSegmentCellIntersection(
gridName, cellIntInfo.globCellIndex, cvf::Vec3st(i, j, k), cellIntInfo.intersectionLengthsInCellCS));
subSegment->addIntersection(intersection);
completion->addSubSegment(subSegment);
previousExitMD = cellIntInfo.endMD;
previousExitTVD = cellIntInfo.endTVD;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignFractureIntersections(const RimEclipseCase* caseToApply,
const RimWellPathFracture* fracture,
const std::vector<RigCompletionData>& completionData,
std::shared_ptr<RicMswSegment> location,
bool* foundSubGridIntersections)
{
CVF_ASSERT(foundSubGridIntersections != nullptr);
std::shared_ptr<RicMswFracture> fractureCompletion(new RicMswFracture(fracture->name()));
double position = fracture->fractureMD();
double width = fracture->fractureTemplate()->computeFractureWidth(fracture);
if (fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH)
{
double perforationLength = fracture->fractureTemplate()->perforationLength();
position -= 0.5 * perforationLength;
width = perforationLength;
}
std::shared_ptr<RicMswSubSegment> subSegment(new RicMswSubSegment(position, position + width, 0.0, 0.0));
for (const RigCompletionData& compIntersection : completionData)
{
const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();
cvf::Vec3st localIJK(cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK());
std::shared_ptr<RicMswSubSegmentCellIntersection> intersection(
new RicMswSubSegmentCellIntersection(cell.lgrName(), cell.globalCellIndex(), localIJK, cvf::Vec3d::ZERO));
subSegment->addIntersection(intersection);
}
fractureCompletion->addSubSegment(subSegment);
location->addCompletion(fractureCompletion);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigCompletionData>
RicWellPathExportMswCompletionsImpl::generatePerforationIntersections(const RimWellPath* wellPath,
const RimPerforationInterval* perforationInterval,
int timeStep,
RimEclipseCase* eclipseCase)
{
std::vector<RigCompletionData> completionData;
const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo(RiaDefines::MATRIX_MODEL);
if (wellPath->perforationIntervalCollection()->isChecked() && perforationInterval->isChecked() &&
perforationInterval->isActiveOnDate(eclipseCase->timeStepDates()[timeStep]))
{
std::pair<std::vector<cvf::Vec3d>, std::vector<double>> perforationPointsAndMD =
wellPath->wellPathGeometry()->clippedPointSubset(perforationInterval->startMD(), perforationInterval->endMD());
std::vector<WellPathCellIntersectionInfo> intersectedCells =
RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(
eclipseCase->eclipseCaseData(), perforationPointsAndMD.first, perforationPointsAndMD.second);
for (auto& cell : intersectedCells)
{
bool cellIsActive = activeCellInfo->isActive(cell.globCellIndex);
if (!cellIsActive) continue;
RigCompletionData completion(wellPath->completions()->wellNameForExport(),
RigCompletionDataGridCell(cell.globCellIndex, eclipseCase->mainGrid()),
cell.startMD);
completion.setSourcePdmObject(perforationInterval);
completionData.push_back(completion);
}
}
return completionData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignPerforationIntersections(const std::vector<RigCompletionData>& completionData,
std::shared_ptr<RicMswCompletion> perforationCompletion,
const SubSegmentIntersectionInfo& cellIntInfo,
double overlapStart,
double overlapEnd,
bool* foundSubGridIntersections)
{
size_t currCellId = cellIntInfo.globCellIndex;
std::shared_ptr<RicMswSubSegment> subSegment(
new RicMswSubSegment(overlapStart, overlapEnd, cellIntInfo.startTVD, cellIntInfo.endTVD));
for (const RigCompletionData& compIntersection : completionData)
{
const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();
if (!cell.isMainGridCell())
{
*foundSubGridIntersections = true;
}
if (cell.globalCellIndex() != currCellId) continue;
cvf::Vec3st localIJK(cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK());
std::shared_ptr<RicMswSubSegmentCellIntersection> intersection(new RicMswSubSegmentCellIntersection(
cell.lgrName(), cell.globalCellIndex(), localIJK, cellIntInfo.intersectionLengthsInCellCS));
subSegment->addIntersection(intersection);
}
perforationCompletion->addSubSegment(subSegment);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchAndSegmentNumbers(const RimEclipseCase* caseToApply,
std::shared_ptr<RicMswSegment> location,
int* branchNum,
int* segmentNum)
{
int icdSegmentNumber = cvf::UNDEFINED_INT;
for (std::shared_ptr<RicMswCompletion> completion : location->completions())
{
if (completion->completionType() == RigCompletionData::PERFORATION)
{
completion->setBranchNumber(1);
}
else if (completion->completionType() != RigCompletionData::FISHBONES_ICD)
{
++(*branchNum);
completion->setBranchNumber(*branchNum);
}
int attachedSegmentNumber = location->segmentNumber();
if (icdSegmentNumber != cvf::UNDEFINED_INT)
{
attachedSegmentNumber = icdSegmentNumber;
}
for (auto subSegment : completion->subSegments())
{
if (completion->completionType() == RigCompletionData::FISHBONES_ICD)
{
subSegment->setSegmentNumber(location->segmentNumber() + 1);
icdSegmentNumber = subSegment->segmentNumber();
}
else if (completion->completionType() != RigCompletionData::PERFORATION)
{
++(*segmentNum);
subSegment->setSegmentNumber(*segmentNum);
}
subSegment->setAttachedSegmentNumber(attachedSegmentNumber);
attachedSegmentNumber = *segmentNum;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchAndSegmentNumbers(const RimEclipseCase* caseToApply,
RicMswExportInfo* exportInfo)
{
int segmentNumber = 1;
int branchNumber = 1;
// First loop over the locations so that each segment on the main stem is an incremental number
for (auto location : exportInfo->wellSegmentLocations())
{
location->setSegmentNumber(++segmentNumber);
for (auto completion : location->completions())
{
if (completion->completionType() == RigCompletionData::FISHBONES_ICD)
{
++segmentNumber; // Skip a segment number because we need one for the ICD
if (completion->completionType() == RigCompletionData::FISHBONES_ICD)
{
completion->setBranchNumber(++branchNumber);
}
}
}
}
// Then assign branch and segment numbers to each completion sub segment
for (auto location : exportInfo->wellSegmentLocations())
{
assignBranchAndSegmentNumbers(caseToApply, location, &branchNumber, &segmentNumber);
}
}
SubSegmentIntersectionInfo::SubSegmentIntersectionInfo(size_t globCellIndex,
double startTVD,
double endTVD,
double startMD,
double endMD,
cvf::Vec3d lengthsInCell)
: globCellIndex(globCellIndex)
, startTVD(startTVD)
, endTVD(endTVD)
, startMD(startMD)
, endMD(endMD)
, intersectionLengthsInCellCS(lengthsInCell)
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<SubSegmentIntersectionInfo> SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(
const RigWellPath* pathGeometry,
const std::vector<WellPathCellIntersectionInfo>& intersections,
double maxSegmentLength)
{
std::vector<SubSegmentIntersectionInfo> out;
if (!pathGeometry) return out;
for (const auto& intersection : intersections)
{
double segLen = intersection.endMD - intersection.startMD;
int segCount = (int)std::trunc(segLen / maxSegmentLength) + 1;
// Calc effective max length
double effectiveMaxSegLen = segLen / segCount;
if (segCount == 1)
{
out.emplace_back(intersection.globCellIndex,
-intersection.startPoint.z(),
-intersection.endPoint.z(),
intersection.startMD,
intersection.endMD,
intersection.intersectionLengthsInCellCS);
}
else
{
double currStartMd = intersection.startMD;
double currEndMd = currStartMd;
double lastTvd = -intersection.startPoint.z();
for (int segIndex = 0; segIndex < segCount; segIndex++)
{
bool lasti = segIndex == (segCount - 1);
currEndMd = currStartMd + effectiveMaxSegLen;
cvf::Vec3d segEndPoint = pathGeometry->interpolatedPointAlongWellPath(currEndMd);
out.emplace_back(intersection.globCellIndex,
lastTvd,
lasti ? -intersection.endPoint.z() : -segEndPoint.z(),
currStartMd,
lasti ? intersection.endMD : currEndMd,
intersection.intersectionLengthsInCellCS / segCount);
currStartMd = currEndMd;
lastTvd = -segEndPoint.z();
}
}
}
return out;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int SubSegmentIntersectionInfo::numberOfSplittedSegments(double startMd, double endMd, double maxSegmentLength)
{
return (int)(std::trunc((endMd - startMd) / maxSegmentLength) + 1);
}