///////////////////////////////////////////////////////////////////////////////// // // 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 "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 #include //-------------------------------------------------------------------------------------------------- /// Internal definitions //-------------------------------------------------------------------------------------------------- class SubSegmentIntersectionInfo { public: SubSegmentIntersectionInfo(size_t globCellIndex, double startTVD, double endTVD, double startMD, double endMD, cvf::Vec3d lengthsInCell); static std::vector spiltIntersectionSegmentsToMaxLength(const RigWellPath* pathGeometry, const std::vector& 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& wellPaths) { if (exportSettings.includeFractures()) { bool anyActiveFractures = false; for (const auto& wellPath : wellPaths) { if (!wellPath->fractureCollection()->activeFractures().empty()) { anyActiveFractures = true; } } if (anyActiveFractures) { QString fileName = QString("%1-Fracture-Welsegs").arg(exportSettings.caseToApply->caseUserDescription()); std::shared_ptr exportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName); for (const auto wellPath : wellPaths) { auto fractures = wellPath->fractureCollection()->activeFractures(); if (!fractures.empty()) { exportWellSegmentsForFractures(exportSettings.caseToApply, exportFile, wellPath, fractures); } } exportFile->close(); } } if (exportSettings.includeFishbones()) { bool anyFishbones = false; for (const auto& wellPath : wellPaths) { if (!wellPath->fishbonesCollection()->activeFishbonesSubs().empty()) { anyFishbones = true; } } if (anyFishbones) { QString fileName = QString("%1-Fishbone-Welsegs").arg(exportSettings.caseToApply->caseUserDescription()); std::shared_ptr exportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName); for (const auto wellPath : wellPaths) { auto fishbones = wellPath->fishbonesCollection()->activeFishbonesSubs(); if (!fishbones.empty()) { exportWellSegmentsForFishbones(exportSettings.caseToApply, exportFile, wellPath, fishbones); } } exportFile->close(); } } if (exportSettings.includePerforations()) { bool anyPerforations = false; for (const auto& wellPath : wellPaths) { if (!wellPath->perforationIntervalCollection()->activePerforations().empty()) { anyPerforations = true; } } if (anyPerforations) { QString fileName = QString("%1-Perforation-Welsegs").arg(exportSettings.caseToApply->caseUserDescription()); std::shared_ptr exportFile = RicWellPathExportCompletionsFileTools::openFileForExport(exportSettings.folder, fileName); for (const auto wellPath : wellPaths) { auto perforations = wellPath->perforationIntervalCollection()->activePerforations(); exportWellSegmentsForPerforations(exportSettings.caseToApply, exportFile, wellPath, exportSettings.timeStep, perforations); } exportFile->close(); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFractures(RimEclipseCase* eclipseCase, std::shared_ptr exportFile, const RimWellPath* wellPath, const std::vector& 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 exportFile, const RimWellPath* wellPath, const std::vector& 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 exportFile, const RimWellPath* wellPath, int timeStep, const std::vector& 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 header = { RifEclipseOutputTableColumn("Name"), RifEclipseOutputTableColumn("Dep 1"), RifEclipseOutputTableColumn("Tlen 1"), RifEclipseOutputTableColumn("Vol 1"), RifEclipseOutputTableColumn("Len&Dep"), RifEclipseOutputTableColumn("PresDrop"), }; formatter.header(header); formatter.add(exportInfo.wellPath()->name()); formatter.add(startTVD); formatter.add(startMD); formatter.addValueOrDefaultMarker(exportInfo.topWellBoreVolume(), RicMswExportInfo::defaultDoubleValue()); formatter.add(exportInfo.lengthAndDepthText()); formatter.add(exportInfo.pressureDropText()); formatter.rowCompleted(); } { std::vector 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 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_AICD}); } formatter.tableCompleted(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportMswCompletionsImpl::generateWelsegsSegments( RifEclipseDataTableFormatter& formatter, const RicMswExportInfo& exportInfo, const std::set& exportCompletionTypes) { bool generatedHeader = false; for (std::shared_ptr segment : exportInfo.wellSegmentLocations()) { for (std::shared_ptr completion : segment->completions()) { if (exportCompletionTypes.count(completion->completionType())) { if (!generatedHeader) { generateWelsegsCompletionCommentHeader(formatter, completion->completionType()); generatedHeader = true; } if (completion->completionType() == RigCompletionData::FISHBONES_ICD || completion->completionType() == RigCompletionData::PERFORATION_ICD || completion->completionType() == RigCompletionData::PERFORATION_AICD) // Found ICD { 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 subSegment : completion->subSegments()) { double depth = 0; double length = 0; if (exportInfo.lengthAndDepthText() == QString("INC")) { depth = subSegment->deltaTVD(); length = subSegment->deltaMD(); } else { depth = subSegment->startTVD() + subSegment->deltaTVD(); length = subSegment->startMD() + subSegment->deltaMD(); } 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 (completionType == RigCompletionData::PERFORATION_ICD || completionType == RigCompletionData::PERFORATION_AICD) { 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 fishbonesTypes = {RigCompletionData::FISHBONES_ICD, RigCompletionData::FISHBONES}; generateCompsegTable(formatter, exportInfo, false, fishbonesTypes); if (exportInfo.hasSubGridIntersections()) { generateCompsegTable(formatter, exportInfo, true, fishbonesTypes); } } { std::set fractureTypes = {RigCompletionData::FRACTURE}; generateCompsegTable(formatter, exportInfo, false, fractureTypes); if (exportInfo.hasSubGridIntersections()) { generateCompsegTable(formatter, exportInfo, true, fractureTypes); } } { std::set perforationTypes = {RigCompletionData::PERFORATION, RigCompletionData::PERFORATION_ICD, 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& exportCompletionTypes) { bool generatedHeader = false; for (std::shared_ptr location : exportInfo.wellSegmentLocations()) { double startMD = location->startMD(); for (std::shared_ptr completion : location->completions()) { if (!completion->subSegments().empty() && exportCompletionTypes.count(completion->completionType())) { if (!generatedHeader) { generateCompsegHeader(formatter, exportInfo, completion->completionType(), exportSubGridIntersections); generatedHeader = true; } for (std::shared_ptr segment : completion->subSegments()) { if (completion->completionType() == RigCompletionData::FISHBONES_ICD) { startMD = segment->startMD(); } for (std::shared_ptr intersection : segment->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 = segment->startMD(); if (exportInfo.lengthAndDepthText() == QString("INC") && completion->completionType() != RigCompletionData::PERFORATION) { startLength -= startMD; } formatter.add(startLength); formatter.add(startLength + segment->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 header = {RifEclipseOutputTableColumn("Name")}; formatter.header(header); formatter.add(exportInfo.wellPath()->name()); formatter.rowCompleted(); } { std::vector allHeaders; if (exportSubGridIntersections) { allHeaders.push_back(RifEclipseOutputTableColumn("Grid")); } std::vector 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 location : exportInfo.wellSegmentLocations()) { for (std::shared_ptr completion : location->completions()) { if (completion->completionType() == RigCompletionData::FISHBONES_ICD || completion->completionType() == RigCompletionData::PERFORATION_ICD) { if (!foundValve) { formatter.keyword("WSEGVALV"); std::vector header = { RifEclipseOutputTableColumn("Well Name"), RifEclipseOutputTableColumn("Seg No"), RifEclipseOutputTableColumn("Cv"), RifEclipseOutputTableColumn("Ac"), }; formatter.header(header); foundValve = true; } if (completion->completionType() == RigCompletionData::FISHBONES_ICD || completion->completionType() == RigCompletionData::PERFORATION_ICD) { std::shared_ptr icd = std::static_pointer_cast(completion); if (!icd->subSegments().empty()) { CVF_ASSERT(icd->subSegments().size() == 1u); if (icd->completionType() == RigCompletionData::PERFORATION_ICD) { formatter.comment(icd->label()); } formatter.add(exportInfo.wellPath()->name()); 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) { bool foundValve = false; for (std::shared_ptr location : exportInfo.wellSegmentLocations()) { for (std::shared_ptr completion : location->completions()) { if (completion->completionType() == RigCompletionData::PERFORATION_AICD) { if (!foundValve) { formatter.keyword("WSEGAICD"); std::vector header = { RifEclipseOutputTableColumn("Well"), RifEclipseOutputTableColumn("Seg1"), RifEclipseOutputTableColumn("Seg2"), RifEclipseOutputTableColumn("str"), RifEclipseOutputTableColumn("len"), RifEclipseOutputTableColumn("rho"), RifEclipseOutputTableColumn("mu"), RifEclipseOutputTableColumn("#8"), RifEclipseOutputTableColumn("#9"), RifEclipseOutputTableColumn("#10"), RifEclipseOutputTableColumn("#11"), RifEclipseOutputTableColumn("x"), RifEclipseOutputTableColumn("y"), RifEclipseOutputTableColumn("o"), RifEclipseOutputTableColumn("#15"), RifEclipseOutputTableColumn("#16"), RifEclipseOutputTableColumn("#17"), RifEclipseOutputTableColumn("#18"), RifEclipseOutputTableColumn("#19"), RifEclipseOutputTableColumn("#20"), RifEclipseOutputTableColumn("#21"), }; formatter.header(header); foundValve = true; } { std::shared_ptr aicd = std::static_pointer_cast(completion); if (!aicd->subSegments().empty()) { CVF_ASSERT(aicd->subSegments().size() == 1u); formatter.comment(aicd->label()); formatter.add(exportInfo.wellPath()->name()); // 1 formatter.add(aicd->subSegments().front()->segmentNumber()); formatter.add(aicd->subSegments().front()->segmentNumber()); std::array values = aicd->values(); formatter.add(values[AICD_STRENGTH]); formatter.add(values[AICD_LENGTH]); // 5 formatter.add(values[AICD_DENSITY_CALIB_FLUID]); formatter.add(values[AICD_VISCOSITY_CALIB_FLUID]); formatter.addValueOrDefaultMarker(values[AICD_CRITICAL_WATER_IN_LIQUID_FRAC], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_EMULSION_VISC_TRANS_REGION], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_MAX_RATIO_EMULSION_VISC], RicMswExportInfo::defaultDoubleValue()); // 10 formatter.add(1); formatter.addValueOrDefaultMarker(values[AICD_MAX_FLOW_RATE], RicMswExportInfo::defaultDoubleValue()); formatter.add(values[AICD_VOL_FLOW_EXP]); formatter.add(values[AICD_VISOSITY_FUNC_EXP]); formatter.add(aicd->isOpen() ? "OPEN" : "SHUT"); // 15 formatter.addValueOrDefaultMarker(values[AICD_EXP_OIL_FRAC_DENSITY], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_EXP_WATER_FRAC_DENSITY], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_EXP_GAS_FRAC_DENSITY], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_EXP_OIL_FRAC_VISCOSITY], RicMswExportInfo::defaultDoubleValue()); formatter.addValueOrDefaultMarker(values[AICD_EXP_WATER_FRAC_VISCOSITY], RicMswExportInfo::defaultDoubleValue()); // 20 formatter.addValueOrDefaultMarker(values[AICD_EXP_GAS_FRAC_VISCOSITY], RicMswExportInfo::defaultDoubleValue()); formatter.rowCompleted(); } } } } } if (foundValve) { formatter.tableCompleted(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo(const RimEclipseCase* caseToApply, const RimWellPath* wellPath, bool enableSegmentSplitting) { std::vector fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs(); return generateFishbonesMswExportInfo(caseToApply, wellPath, fishbonesSubs, enableSegmentSplitting); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo( const RimEclipseCase* caseToApply, const RimWellPath* wellPath, const std::vector& 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::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 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 icdCompletion(new RicMswFishbonesICD(QString("ICD"))); std::shared_ptr icdSegment(new RicMswSubSegment(subEndMD, 0.1, subEndTVD, 0.0)); 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(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 fractures = wellPath->fractureCollection()->activeFractures(); return generateFracturesMswExportInfo(caseToApply, wellPath, fractures); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RicMswExportInfo RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo(RimEclipseCase* caseToApply, const RimWellPath* wellPath, const std::vector& 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& coords = wellPathGeometry->wellPathPoints(); const std::vector& mds = wellPathGeometry->measureDepths(); CVF_ASSERT(!coords.empty() && !mds.empty()); std::vector intersections = RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(caseToApply->eclipseCaseData(), coords, mds); double maxSegmentLength = wellPath->fractureCollection()->mswParameters()->maxSegmentLength(); std::vector subSegIntersections = SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(wellPathGeometry, intersections, maxSegmentLength); double initialMD = 0.0; if (wellPath->fractureCollection()->referenceMDType() == RimWellPathFractureCollection::MANUAL_REFERENCE_MD) { initialMD = wellPath->fractureCollection()->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 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 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& perforationIntervals) { const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo(RiaDefines::MATRIX_MODEL); RiaEclipseUnitTools::UnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType(); const RigWellPath* wellPathGeometry = wellPath->wellPathGeometry(); const std::vector& coords = wellPathGeometry->wellPathPoints(); const std::vector& mds = wellPathGeometry->measureDepths(); CVF_ASSERT(!coords.empty() && !mds.empty()); std::vector intersections = RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath(eclipseCase->eclipseCaseData(), coords, mds); double maxSegmentLength = wellPath->perforationIntervalCollection()->mswParameters()->maxSegmentLength(); std::vector subSegIntersections = SubSegmentIntersectionInfo::spiltIntersectionSegmentsToMaxLength(wellPathGeometry, intersections, maxSegmentLength); double initialMD = 0.0; 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); assignSuperValveCompletions(mainBoreSegments, perforationIntervals); assignValveContributionsToSuperValves(mainBoreSegments, perforationIntervals, unitSystem); moveIntersectionsToSuperValves(mainBoreSegments); for (std::shared_ptr segment : mainBoreSegments) { exportInfo.addWellSegment(segment); } exportInfo.setHasSubGridIntersections(foundSubGridIntersections); exportInfo.sortLocations(); assignBranchAndSegmentNumbers(eclipseCase, &exportInfo); return exportInfo; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RicWellPathExportMswCompletionsImpl::MainBoreSegments RicWellPathExportMswCompletionsImpl::createMainBoreSegmentsForPerforations( const std::vector& subSegIntersections, const std::vector& 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 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 intervalCompletion(new RicMswPerforation(interval->name())); std::vector completionData = generatePerforationIntersections(wellPath, interval, timeStep, eclipseCase); assignPerforationIntersections(completionData, intervalCompletion, cellIntInfo, foundSubGridIntersections); segment->addCompletion(intervalCompletion); } } mainBoreSegments.push_back(segment); } } return mainBoreSegments; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportMswCompletionsImpl::assignSuperValveCompletions( std::vector>& mainBoreSegments, const std::vector& perforationIntervals) { for (size_t nMainSegment = 0u; nMainSegment < mainBoreSegments.size(); ++nMainSegment) { std::shared_ptr segment = mainBoreSegments[nMainSegment]; std::shared_ptr superICD; std::shared_ptr superAICD; double totalICDOverlap = 0.0; double totalAICDOverlap = 0.0; for (const RimPerforationInterval* interval : perforationIntervals) { std::vector perforationValves; interval->descendantsIncludingThisOfType(perforationValves); for (const RimWellPathValve* valve : perforationValves) { bool isAicd = valve->componentType() == RiaDefines::AICD; for (size_t nSubValve = 0u; nSubValve < valve->valveLocations().size(); ++nSubValve) { double valveMD = valve->valveLocations()[nSubValve]; std::pair 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()) { QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2); std::shared_ptr subSegment(new RicMswSubSegment(valveMD, 0.1, 0.0, 0.0)); if (isAicd) { superAICD.reset(new RicMswPerforationAICD(valveLabel)); superAICD->addSubSegment(subSegment); } else { superICD.reset(new RicMswPerforationICD(valveLabel)); superICD->addSubSegment(subSegment); } } else if (overlap > 0.0 && (!isAicd && !superICD)) { QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2); std::shared_ptr subSegment(new RicMswSubSegment(overlapStart, 0.1, 0.0, 0.0)); superICD.reset(new RicMswPerforationICD(valveLabel)); superICD->addSubSegment(subSegment); } else if (overlap > 0.0 && (isAicd && !superAICD)) { QString valveLabel = QString("%1 #%2").arg("Combined Valve for segment").arg(nMainSegment + 2); std::shared_ptr subSegment(new RicMswSubSegment(overlapStart, 0.1, 0.0, 0.0)); superAICD.reset(new RicMswPerforationAICD(valveLabel)); superAICD->addSubSegment(subSegment); } if (isAicd) { totalAICDOverlap += overlap; } else { totalICDOverlap += overlap; } } } } if (totalICDOverlap > 0.0 || totalAICDOverlap > 0.0) { if (totalAICDOverlap > totalICDOverlap) { segment->addCompletion(superAICD); } else { segment->addCompletion(superICD); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RicWellPathExportMswCompletionsImpl::assignValveContributionsToSuperValves( const std::vector>& mainBoreSegments, const std::vector& perforationIntervals, RiaEclipseUnitTools::UnitSystem unitSystem) { ValveContributionMap assignedRegularValves; for (std::shared_ptr segment : mainBoreSegments) { std::shared_ptr superValve; for (auto completion : segment->completions()) { std::shared_ptr valve = std::dynamic_pointer_cast(completion); if (valve) { superValve = valve; break; } } if (!superValve) continue; std::shared_ptr accumulator; if (std::dynamic_pointer_cast(superValve)) { accumulator.reset(new RicMswICDAccumulator(unitSystem)); } else if (std::dynamic_pointer_cast(superValve)) { accumulator.reset(new RicMswAICDAccumulator(unitSystem)); } for (const RimPerforationInterval* interval : perforationIntervals) { std::vector perforationValves; interval->descendantsIncludingThisOfType(perforationValves); for (const RimWellPathValve* valve : perforationValves) { for (size_t nSubValve = 0u; nSubValve < valve->valveSegments().size(); ++nSubValve) { std::pair 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, overlap / valveSegmentLength)) { assignedRegularValves[superValve].insert(std::make_pair(valve, nSubValve)); } } } } } if (superValve && accumulator) { accumulator->applyToSuperValve(superValve); } } for (auto regularValvePair : assignedRegularValves) { if (regularValvePair.second.size()) { QStringList valveLabels; for (std::pair 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::moveIntersectionsToSuperValves(MainBoreSegments mainBoreSegments) { for (auto segmentPtr : mainBoreSegments) { std::shared_ptr superValve; std::vector> perforations; for (auto completionPtr : segmentPtr->completions()) { if (completionPtr->completionType() == RigCompletionData::PERFORATION_ICD || completionPtr->completionType() == RigCompletionData::PERFORATION_AICD) { 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 location, bool* foundSubGridIntersections, double maxSegmentLength) { CVF_ASSERT(foundSubGridIntersections != nullptr); const RigMainGrid* grid = caseToApply->eclipseCaseData()->mainGrid(); for (std::shared_ptr completion : location->completions()) { if (completion->completionType() != RigCompletionData::FISHBONES) { continue; } std::vector> lateralCoordMDPairs = fishbonesSubs->coordsAndMDForLateral(location->subIndex(), completion->index()); if (lateralCoordMDPairs.empty()) { continue; } std::vector lateralCoords; std::vector 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 intersections = RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( caseToApply->eclipseCaseData(), lateralCoords, lateralMDs); RigWellPath pathGeometry; pathGeometry.m_wellPathPoints = lateralCoords; pathGeometry.m_measuredDepths = lateralMDs; std::vector 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 subSegment(new RicMswSubSegment( previousExitMD, cellIntInfo.endMD - previousExitMD, previousExitTVD, cellIntInfo.endTVD - previousExitTVD)); std::shared_ptr 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& completionData, std::shared_ptr location, bool* foundSubGridIntersections) { CVF_ASSERT(foundSubGridIntersections != nullptr); std::shared_ptr 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 subSegment(new RicMswSubSegment(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 intersection( new RicMswSubSegmentCellIntersection(cell.lgrName(), cell.globalCellIndex(), localIJK, cvf::Vec3d::ZERO)); subSegment->addIntersection(intersection); } fractureCompletion->addSubSegment(subSegment); location->addCompletion(fractureCompletion); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicWellPathExportMswCompletionsImpl::generatePerforationIntersections(const RimWellPath* wellPath, const RimPerforationInterval* perforationInterval, int timeStep, RimEclipseCase* eclipseCase) { std::vector 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> perforationPointsAndMD = wellPath->wellPathGeometry()->clippedPointSubset(perforationInterval->startMD(), perforationInterval->endMD()); std::vector 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& completionData, std::shared_ptr perforationCompletion, const SubSegmentIntersectionInfo& cellIntInfo, bool* foundSubGridIntersections) { size_t currCellId = cellIntInfo.globCellIndex; std::shared_ptr subSegment(new RicMswSubSegment(cellIntInfo.startMD, cellIntInfo.endMD - cellIntInfo.startMD, cellIntInfo.startTVD, cellIntInfo.endTVD - cellIntInfo.startTVD)); 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 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 location, int* branchNum, int* segmentNum) { int icdSegmentNumber = cvf::UNDEFINED_INT; for (std::shared_ptr 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::spiltIntersectionSegmentsToMaxLength( const RigWellPath* pathGeometry, const std::vector& intersections, double maxSegmentLength) { std::vector out; if (!pathGeometry) return out; for (size_t i = 0; i < intersections.size(); i++) { const auto& intersection = intersections[i]; 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.push_back(SubSegmentIntersectionInfo(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.push_back(SubSegmentIntersectionInfo(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); }