diff --git a/ApplicationCode/Commands/RicExportSelectedWellPathFractureWellCompletionFeature.cpp b/ApplicationCode/Commands/RicExportSelectedWellPathFractureWellCompletionFeature.cpp index 282a1e2646..9bc4bcde70 100644 --- a/ApplicationCode/Commands/RicExportSelectedWellPathFractureWellCompletionFeature.cpp +++ b/ApplicationCode/Commands/RicExportSelectedWellPathFractureWellCompletionFeature.cpp @@ -52,21 +52,9 @@ CAF_CMD_SOURCE_INIT(RicExportSelectedWellPathFractureWellCompletionFeature, "Ric //-------------------------------------------------------------------------------------------------- void RicExportSelectedWellPathFractureWellCompletionFeature::onActionTriggered(bool isChecked) { - std::vector selection; caf::SelectionManager::instance()->objectsByType(&selection); - - - std::vector fractures; - for (RimWellPath* well : selection) - { - std::vector fracListForWell; - well->descendantsIncludingThisOfType(fracListForWell); - for (RimFracture* fracture : fracListForWell) - { - fractures.push_back(fracture); - } - } + if (!selection.size()) return; RimFractureExportSettings exportSettings; @@ -74,20 +62,15 @@ void RicExportSelectedWellPathFractureWellCompletionFeature::onActionTriggered(b QString projectFolder = app->currentProjectPath(); RimView* view = app->activeReservoirView(); - caf::PdmObjectHandle* objHandle = dynamic_cast(view); - if (!objHandle) return; RimEclipseCase* caseToApply; - objHandle->firstAncestorOrThisOfType(caseToApply); - exportSettings.caseToApply = caseToApply; + view->firstAncestorOrThisOfType(caseToApply); + if (!caseToApply) return; + exportSettings.caseToApply = caseToApply; if (projectFolder.isEmpty()) { - RimView* activeView = RiaApplication::instance()->activeReservoirView(); - if (!activeView) return; - RimEclipseView * activeRiv = dynamic_cast(activeView); - if (!activeRiv) return; - projectFolder = activeRiv->eclipseCase()->locationOnDisc(); + projectFolder = caseToApply->locationOnDisc(); } QString defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallback("FRACTURE_EXPORT_DIR", projectFolder); @@ -100,15 +83,8 @@ void RicExportSelectedWellPathFractureWellCompletionFeature::onActionTriggered(b { RiaApplication::instance()->setLastUsedDialogDirectory("FRACTURE_EXPORT_DIR", QFileInfo(exportSettings.fileName).absolutePath()); - bool isOk = RifFractureExportTools::exportFracturesToEclipseDataInputFile(exportSettings.fileName, fractures, exportSettings.caseToApply); - - if (!isOk) - { - QMessageBox::critical(NULL, "File export", "Failed to exported current result to " + exportSettings.fileName); - } + RifFractureExportTools::exportWellPathFracturesToEclipseDataInputFile(exportSettings.fileName, selection[0], exportSettings.caseToApply); } - - } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/FileInterface/RifFractureExportTools.cpp b/ApplicationCode/FileInterface/RifFractureExportTools.cpp index 3ebb99dbea..ea9f17ab86 100644 --- a/ApplicationCode/FileInterface/RifFractureExportTools.cpp +++ b/ApplicationCode/FileInterface/RifFractureExportTools.cpp @@ -338,7 +338,7 @@ void RifFractureExportTools::exportWellPathFracturesToEclipseDataInputFile(const double totalWellTrans = 0.5 * intersection.endpointCount * radialTrans + linearTrans; - transCondenser.addNeighborTransmissibility( { true, RigTransmissibilityCondenser::CellAddress::WELL, stpWellCellIdx}, + transCondenser.addNeighborTransmissibility( { true, RigTransmissibilityCondenser::CellAddress::WELL, 1}, { false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, stpWellCellIdx }, totalWellTrans); } diff --git a/ApplicationCode/ReservoirDataModel/RigCellGeometryTools.cpp b/ApplicationCode/ReservoirDataModel/RigCellGeometryTools.cpp index 8a8588c31d..2839faaca1 100644 --- a/ApplicationCode/ReservoirDataModel/RigCellGeometryTools.cpp +++ b/ApplicationCode/ReservoirDataModel/RigCellGeometryTools.cpp @@ -341,25 +341,39 @@ void fillInterpolatedSubjectZ(ClipperLib::IntPoint& e1bot, ClipperLib::IntPoint& e2top, ClipperLib::IntPoint& pt) { - int e2XRange = (e2top.X - e2bot.X); - int e2YRange = (e2top.Y - e2bot.Y); + ClipperLib::IntPoint ePLbot; + ClipperLib::IntPoint ePLtop; - double e2Length = sqrt(e2XRange*e2XRange + e2YRange*e2YRange); - - if (e2Length <= 1) + if (e1top.Z == std::numeric_limits::max()) { - pt.Z = e2bot.Z; + ePLtop = e2top; + ePLbot = e2bot; + } + else + { + ePLtop = e1top; + ePLbot = e1bot; + } + + double ePLXRange = (ePLtop.X - ePLbot.X); + double ePLYRange = (ePLtop.Y - ePLbot.Y); + + double ePLLength = sqrt(ePLXRange*ePLXRange + ePLYRange*ePLYRange); + + if (ePLLength <= 1) + { + pt.Z = ePLbot.Z; return; } - int e2BotPtXRange = pt.X - e2bot.X; - int e2BotPtYRange = pt.Y - e2bot.Y; + double ePLBotPtXRange = pt.X - ePLbot.X; + double ePLBotPtYRange = pt.Y - ePLbot.Y; - double e2BotPtLength = sqrt(e2BotPtXRange*e2BotPtXRange + e2BotPtYRange*e2BotPtYRange); + double ePLBotPtLength = sqrt(ePLBotPtXRange*ePLBotPtXRange + ePLBotPtYRange*ePLBotPtYRange); - double fraction = e2BotPtLength/e2Length; + double fraction = ePLBotPtLength/ePLLength; - pt.Z = std::nearbyint( e2bot.Z + fraction*(e2top.Z - e2bot.Z) ); + pt.Z = std::nearbyint( ePLbot.Z + fraction*(ePLtop.Z - ePLbot.Z) ); } //-------------------------------------------------------------------------------------------------- @@ -375,7 +389,7 @@ void fillUndefinedZ(ClipperLib::IntPoint& e1bot, } //-------------------------------------------------------------------------------------------------- -/// +/// Assumes x.y plane polygon. Polyline might have a Z, and the returned Z is the polyline Z, interpolated if it is clipped. //-------------------------------------------------------------------------------------------------- std::vector > RigCellGeometryTools::clipPolylineByPolygon(const std::vector& polyLine, const std::vector& polygon, @@ -393,7 +407,9 @@ std::vector > RigCellGeometryTools::clipPolylineByPolygo ClipperLib::Path polygonPath; for (const cvf::Vec3d& v : polygon) { - polygonPath.push_back(toClipperPoint(v)); + ClipperLib::IntPoint intp = toClipperPoint(v); + intp.Z = std::numeric_limits::max(); + polygonPath.push_back(intp); } ClipperLib::Clipper clpr; diff --git a/ApplicationCode/ReservoirDataModel/RigTransmissibilityCondenser.cpp b/ApplicationCode/ReservoirDataModel/RigTransmissibilityCondenser.cpp index 67f02644d4..431563ad52 100644 --- a/ApplicationCode/ReservoirDataModel/RigTransmissibilityCondenser.cpp +++ b/ApplicationCode/ReservoirDataModel/RigTransmissibilityCondenser.cpp @@ -27,9 +27,10 @@ //-------------------------------------------------------------------------------------------------- void RigTransmissibilityCondenser::addNeighborTransmissibility(CellAddress cell1, CellAddress cell2, double transmissibility) { + if (transmissibility < 1e-9) return; + m_condensedTransmissibilities.clear(); m_externalCellAddrSet.clear(); - if ( cell1 < cell2 ) m_neighborTransmissibilities[cell1][cell2] = transmissibility; else diff --git a/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.cpp b/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.cpp index cc133f8253..563b4b118d 100644 --- a/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.cpp +++ b/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.cpp @@ -10,31 +10,55 @@ #include + +RigWellPathStimplanIntersector::RigWellPathStimplanIntersector(const RigWellPath* wellpathGeom, const RimFracture * rimFracture) +{ + std::vector wellPathPoints = wellpathGeom->m_wellPathPoints; + cvf::Mat4f fractureXf = rimFracture->transformMatrix(); + double wellRadius = rimFracture->wellRadius(); + std::vector fracturePolygonf ; + std::vector > stpCellPolygons; + { + auto stimPlanFractureTemplate = dynamic_cast (rimFracture->attachedFractureDefinition()); + + CVF_ASSERT(stimPlanFractureTemplate); + + fracturePolygonf = stimPlanFractureTemplate->fracturePolygon(rimFracture->fractureUnit()); + { + const std::vector& stpCells = stimPlanFractureTemplate->getStimPlanCells(); + for ( const auto& stpCell: stpCells ) stpCellPolygons.push_back(stpCell.getPolygon()); + } + } + + calculate(fractureXf, fracturePolygonf, wellPathPoints, wellRadius, stpCellPolygons, m_stimPlanCellIdxToIntersectionInfoMap); + +} + //-------------------------------------------------------------------------------------------------- /// Todo: Use only the perforated parts of the well path //-------------------------------------------------------------------------------------------------- -RigWellPathStimplanIntersector::RigWellPathStimplanIntersector(const RigWellPath* wellpathGeom, const RimFracture * rimFracture) + +void RigWellPathStimplanIntersector::calculate(const cvf::Mat4f &fractureXf, + const std::vector& fracturePolygonf, + const std::vector& wellPathPointsOrg, + double wellRadius, + const std::vector >& stpCellPolygons, + std::map& m_stimPlanCellIdxToIntersectionInfoMap) { - auto stimPlanFractureTemplate = dynamic_cast (rimFracture->attachedFractureDefinition()); + cvf::Mat4d toFractureXf = cvf::Mat4d(fractureXf.getInverted()); - CVF_ASSERT(stimPlanFractureTemplate); - - std::vector wellPathPoints = wellpathGeom->m_wellPathPoints; - cvf::Mat4d toFractureXf = cvf::Mat4d (rimFracture->transformMatrix().getInverted()); - double wellRadius = rimFracture->wellRadius(); std::vector fracturePolygon; - { - std::vector fracturePolygonf = stimPlanFractureTemplate->fracturePolygon(rimFracture->fractureUnit()); - for ( auto fpv: fracturePolygonf ) fracturePolygon.push_back(cvf::Vec3d(fpv)); - } + for ( auto fpv: fracturePolygonf ) fracturePolygon.push_back(cvf::Vec3d(fpv)); // Convert well path to fracture template system - for ( auto & wellPPoint : wellPathPoints ) wellPPoint.transformPoint(toFractureXf); + + std::vector fractureRelativeWellPathPoints; + for ( auto & wellPPoint : wellPathPointsOrg ) fractureRelativeWellPathPoints.push_back(wellPPoint.getTransformedPoint( toFractureXf)); // Clip well path to fracture domain std::vector > wellPathPartsWithinFracture = - RigCellGeometryTools::clipPolylineByPolygon(wellPathPoints, fracturePolygon, RigCellGeometryTools::INTERPOLATE_LINE_Z); + RigCellGeometryTools::clipPolylineByPolygon(fractureRelativeWellPathPoints, fracturePolygon, RigCellGeometryTools::INTERPOLATE_LINE_Z); // Remove the part of the well path that is more than well radius away from the fracture plane @@ -45,22 +69,57 @@ RigWellPathStimplanIntersector::RigWellPathStimplanIntersector(const RigWellPath std::vector< cvf::Vec3d > currentIntersectingWpPart; for ( size_t vxIdx = 0; vxIdx < part.size() -1; ++vxIdx ) { - double thisZ = fabs(wellPathPoints[vxIdx].z()); - double nextZ = fabs(wellPathPoints[vxIdx + 1].z()); + double thisAbsZ = fabs(part[vxIdx].z()); + double nextAbsZ = fabs(part[vxIdx + 1].z()); + double thisZ = part[vxIdx].z(); + double nextZ = part[vxIdx + 1].z(); - if ( thisZ >= wellRadius && nextZ >= wellRadius ) continue; - - if ( thisZ < wellRadius && nextZ < wellRadius ) + if ( thisAbsZ >= wellRadius && nextAbsZ >= wellRadius ) { - currentIntersectingWpPart.push_back(wellPathPoints[vxIdx]); + if ( (thisZ >= 0 && nextZ >= 0) + || (thisZ <= 0 && nextZ <= 0 ) ) + { + continue; // Outside + } + else // In and out + { + { + double wellRadiusDistFromPlane = thisZ > 0 ? wellRadius: -wellRadius; + + double fraction = (wellRadiusDistFromPlane - thisZ)/ (nextZ - thisZ); + + cvf::Vec3d intersectPoint = part[vxIdx] + fraction * (part[vxIdx+1] - part[vxIdx]); + currentIntersectingWpPart.push_back(intersectPoint); + } + { + double wellRadiusDistFromPlane = nextZ > 0 ? wellRadius: -wellRadius; + + double fraction = (wellRadiusDistFromPlane - thisZ)/ (nextZ - thisZ); + + cvf::Vec3d intersectPoint = part[vxIdx] + fraction * (part[vxIdx+1] - part[vxIdx]); + currentIntersectingWpPart.push_back(intersectPoint); + + intersectingWellPathParts.push_back(currentIntersectingWpPart); + currentIntersectingWpPart.clear(); + } + continue; + } + } + if ( thisAbsZ < wellRadius && nextAbsZ < wellRadius ) // Inside + { + currentIntersectingWpPart.push_back(part[vxIdx]); continue; } - if ( thisZ < wellRadius && nextZ >= wellRadius ) + if ( thisAbsZ < wellRadius && nextAbsZ >= wellRadius ) // Going out { - currentIntersectingWpPart.push_back(wellPathPoints[vxIdx]); - double fraction = (wellRadius - thisZ)/ (nextZ - thisZ); - cvf::Vec3d intersectPoint = wellPathPoints[vxIdx] + fraction * (wellPathPoints[vxIdx+1] - wellPathPoints[vxIdx]); + currentIntersectingWpPart.push_back(part[vxIdx]); + + double wellRadiusDistFromPlane = nextZ > 0 ? wellRadius: -wellRadius; + + double fraction = (wellRadiusDistFromPlane - thisZ)/ (nextZ - thisZ); + + cvf::Vec3d intersectPoint = part[vxIdx] + fraction * (part[vxIdx+1] - part[vxIdx]); currentIntersectingWpPart.push_back(intersectPoint); intersectingWellPathParts.push_back(currentIntersectingWpPart); @@ -68,13 +127,24 @@ RigWellPathStimplanIntersector::RigWellPathStimplanIntersector(const RigWellPath continue; } - if ( thisZ >= wellRadius && nextZ < wellRadius ) + if ( thisAbsZ >= wellRadius && nextAbsZ < wellRadius ) // Going in { - double fraction = (wellRadius - thisZ)/ (nextZ - thisZ); - cvf::Vec3d intersectPoint = wellPathPoints[vxIdx] + fraction * (wellPathPoints[vxIdx+1] - wellPathPoints[vxIdx]); + double wellRadiusDistFromPlane = thisZ > 0 ? wellRadius: -wellRadius; + + double fraction = (wellRadiusDistFromPlane - thisZ)/ (nextZ - thisZ); + + cvf::Vec3d intersectPoint = part[vxIdx] + fraction * (part[vxIdx+1] - part[vxIdx]); currentIntersectingWpPart.push_back(intersectPoint); continue; } + + } + + // Add last point if it is within the radius + + if (part.size() > 1 && fabs(part.back().z()) < wellRadius) + { + currentIntersectingWpPart.push_back(part.back()); } if ( currentIntersectingWpPart.size() ) @@ -85,12 +155,9 @@ RigWellPathStimplanIntersector::RigWellPathStimplanIntersector(const RigWellPath // Find the StimPlan cells touched by the intersecting well path parts - const std::vector& stpCells = stimPlanFractureTemplate->getStimPlanCells(); - - - for ( size_t cIdx = 0; cIdx < stpCells.size(); ++ cIdx ) + for ( size_t cIdx = 0; cIdx < stpCellPolygons.size(); ++ cIdx ) { - std::vector cellPolygon = stpCells[cIdx].getPolygon(); + const std::vector& cellPolygon = stpCellPolygons[cIdx]; for ( const auto& wellpathPart :intersectingWellPathParts ) { std::vector > wellPathPartsInPolygon = diff --git a/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.h b/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.h index 0151866b94..66b66b07f2 100644 --- a/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.h +++ b/ApplicationCode/ReservoirDataModel/RigWellPathStimplanIntersector.h @@ -18,9 +18,14 @@ #pragma once #include +#include +#include "cvfBase.h" +#include "cvfMatrix4.h" class RigWellPath; class RimFracture; +class RigWellPathStimplanIntersectorTester; + class RigWellPathStimplanIntersector { @@ -38,8 +43,29 @@ public: const std::map& intersections() { return m_stimPlanCellIdxToIntersectionInfoMap; } private: - std::map m_stimPlanCellIdxToIntersectionInfoMap; + friend class RigWellPathStimplanIntersectorTester; + static void calculate(const cvf::Mat4f& fractureXf, + const std::vector& fracturePolygon, + const std::vector& wellPathPoints, + double wellRadius, + const std::vector >& stpCellPolygons, + std::map& stimPlanCellIdxToIntersectionInfoMap); + + std::map m_stimPlanCellIdxToIntersectionInfoMap; }; +class RigWellPathStimplanIntersectorTester +{ +public: + static void testCalculate(const cvf::Mat4f& fractureXf, + const std::vector& fracturePolygon, + const std::vector& wellPathPoints, + double wellRadius, + const std::vector >& stpCellPolygons, + std::map& stimPlanCellIdxToIntersectionInfoMap) + { + RigWellPathStimplanIntersector::calculate(fractureXf, fracturePolygon, wellPathPoints, wellRadius, stpCellPolygons, stimPlanCellIdxToIntersectionInfoMap); + } +}; \ No newline at end of file diff --git a/ApplicationCode/UnitTests/RigCellGeometryTools-Test.cpp b/ApplicationCode/UnitTests/RigCellGeometryTools-Test.cpp index 97f4bc2a96..1b008f0a35 100644 --- a/ApplicationCode/UnitTests/RigCellGeometryTools-Test.cpp +++ b/ApplicationCode/UnitTests/RigCellGeometryTools-Test.cpp @@ -301,4 +301,121 @@ TEST(RigCellGeometryTools, polylinePolygonIntersectionTest) EXPECT_EQ(HUGE_VAL, clippedLines.front()[1].z()); } +} + +#include "RigWellPathStimplanIntersector.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST(RigWellPathStimplanIntersector, intersection) +{ + + { + cvf::Mat4f fractureXf = cvf::Mat4f::IDENTITY; + fractureXf.setTranslation({ 50.0f, 0.0f, 0.0f }); + + std::vector fracturePolygon ={ {0.0f, 0.0f, 0.0f}, {5.0f, 10.0f, 0.0f}, {10.0f, 0.0f, 0.0f} }; + std::vector wellPathPoints ={ {50.0f-4.0f, 6.0f, 10.0f}, {50.0f+6.0f, 6.0f, 0.0f}, {50.0f+10.0f, 10.0f, -100.0f} }; + double wellRadius = 1.5; + std::vector > stpCellPolygons = + { + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 5.0f, 0.0f }, { 5.0f, 5.0f, 0.0f }, { 5.0f, 0.0f, 0.0f } }, + { { 0.5f, 0.0f, 0.0f }, { 0.5f, 5.0f, 0.0f }, {10.0f, 5.0f, 0.0f }, {10.0f, 0.0f, 0.0f } }, + { { 0.0f, 5.0f, 0.0f }, { 0.0f,10.0f, 0.0f }, { 5.0f,10.0f, 0.0f }, { 5.0f, 5.0f, 0.0f } }, + { { 5.0f, 5.0f, 0.0f }, { 5.0f,10.0f, 0.0f }, {10.0f,10.0f, 0.0f }, {10.0f, 5.0f, 0.0f } }, + }; + + std::map stimPlanCellIdxToIntersectionInfoMap; + + RigWellPathStimplanIntersectorTester::testCalculate(fractureXf, + fracturePolygon, + wellPathPoints, + wellRadius, + stpCellPolygons, + stimPlanCellIdxToIntersectionInfoMap); + + EXPECT_EQ(2, stimPlanCellIdxToIntersectionInfoMap.size()); + auto it = stimPlanCellIdxToIntersectionInfoMap.begin(); + EXPECT_EQ(2, it->first); + EXPECT_EQ(1, it->second.endpointCount); + ++it; + EXPECT_EQ(3, it->first); + EXPECT_EQ(1, it->second.endpointCount); + } + + + { + cvf::Mat4f fractureXf = cvf::Mat4f::IDENTITY; + + std::vector fracturePolygon ={ {0.0f, 0.0f, 0.0f}, {5.0f, 10.0f, 0.0f}, {10.0f, 0.0f, 0.0f} }; + double wellRadius = 1.5; + std::vector > stpCellPolygons = + { + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 5.0f, 0.0f }, { 5.0f, 5.0f, 0.0f }, { 5.0f, 0.0f, 0.0f } }, + { { 5.0f, 0.0f, 0.0f }, { 5.0f, 5.0f, 0.0f }, {10.0f, 5.0f, 0.0f }, {10.0f, 0.0f, 0.0f } }, + { { 0.0f, 5.0f, 0.0f }, { 0.0f,10.0f, 0.0f }, { 5.0f,10.0f, 0.0f }, { 5.0f, 5.0f, 0.0f } }, + { { 5.0f, 5.0f, 0.0f }, { 5.0f,10.0f, 0.0f }, {10.0f,10.0f, 0.0f }, {10.0f, 5.0f, 0.0f } }, + }; + + + { + std::map stimPlanCellIdxToIntersectionInfoMap; + std::vector wellPathPoints ={ {1.0f, 0.5f, 10.0f}, {1.0f, 1.5f, -10.0f} }; + + RigWellPathStimplanIntersectorTester::testCalculate(fractureXf, + fracturePolygon, + wellPathPoints, + wellRadius, + stpCellPolygons, + stimPlanCellIdxToIntersectionInfoMap); + + + + EXPECT_EQ(1, stimPlanCellIdxToIntersectionInfoMap.size()); + auto it = stimPlanCellIdxToIntersectionInfoMap.begin(); + EXPECT_EQ(0, it->first); + EXPECT_EQ(2, it->second.endpointCount); + } + + { + std::map stimPlanCellIdxToIntersectionInfoMap; + std::vector wellPathPoints ={ {1.0f, 0.5f, 10.0f}, {1.0f, 1.0f, 0.5f} }; + + RigWellPathStimplanIntersectorTester::testCalculate(fractureXf, + fracturePolygon, + wellPathPoints, + wellRadius, + stpCellPolygons, + stimPlanCellIdxToIntersectionInfoMap); + + + + EXPECT_EQ(1, stimPlanCellIdxToIntersectionInfoMap.size()); + auto it = stimPlanCellIdxToIntersectionInfoMap.begin(); + EXPECT_EQ(0, it->first); + EXPECT_EQ(2, it->second.endpointCount); + } + + { + std::map stimPlanCellIdxToIntersectionInfoMap; + std::vector wellPathPoints ={ {1.0f, 0.5f, 10.0f}, {1.0f, 1.0f, 0.5f}, {1.0f, 1.5f, -0.5f}, {1.0f, 2.0f, -10.0f}}; + + RigWellPathStimplanIntersectorTester::testCalculate(fractureXf, + fracturePolygon, + wellPathPoints, + wellRadius, + stpCellPolygons, + stimPlanCellIdxToIntersectionInfoMap); + + + + EXPECT_EQ(1, stimPlanCellIdxToIntersectionInfoMap.size()); + auto it = stimPlanCellIdxToIntersectionInfoMap.begin(); + EXPECT_EQ(0, it->first); + EXPECT_EQ(2, it->second.endpointCount); + } + + } + } \ No newline at end of file