Files
ResInsight/ApplicationLibCode/Commands/CompletionExportCommands/MswExport/RicMswBranchBuilder.h
T
Magne Sjaastad 6e8e658f6d #14980: Remove global max segment length from segment collection
The global max segment length no longer exists, so segment subdivision is defined only by the segment intervals.
2026-10-19 21:13:13 +02:00

232 lines
15 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "CompletionsMsw/RigMswSegment.h"
#include "RiaDefines.h"
#include "RicMswTableDataTools.h"
#include "RigActiveCellInfo.h"
#include "Well/RigWellLogExtractor.h"
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <QDateTime>
class RimEclipseCase;
class RimWellPath;
class RigMainGrid;
class RimPerforationInterval;
//--------------------------------------------------------------------------------------------------
/// Free functions that build RigMswSegment lists directly from well-path geometry and Rim
/// completion objects.
///
/// Extracted from the former `internal` anonymous namespace in RicWellPathExportMswGeometryPath.cpp
/// so that they can be called from other translation units and unit-tested in isolation.
//--------------------------------------------------------------------------------------------------
namespace RicMswBranchBuilder
{
//--------------------------------------------------------------------------------------------------
/// Mapping from a main-bore segment's MD span to its segment number and node MD.
/// The span is the part of the branch served by the segment node. Without segmentation rules one
/// entry is emitted per sub-segment (a cell split into N sub-segments produces N entries), and the
/// node is at the centre of the span.
/// Used by valve, fracture, and lateral builders to locate the outlet segment for a given MD.
//--------------------------------------------------------------------------------------------------
struct CellSegmentEntry
{
double cellStartMD;
double cellEndMD;
int lastSubSegmentNumber;
double nodeMD;
};
//--------------------------------------------------------------------------------------------------
/// MD span served by one segment node, nodeIndex refers to the node list.
//--------------------------------------------------------------------------------------------------
struct NodeSpan
{
double startMD;
double endMD;
size_t nodeIndex;
};
//--------------------------------------------------------------------------------------------------
/// One grid-cell intersection of a fishbones lateral, recorded while the lateral branches are built
/// so that effective diameters can be computed once all laterals of the well are known.
///
/// A cell intersection longer than the max segment length is split into several WELSEGS rows. They
/// all belong to the same intersection and share one effective diameter, so the segment numbers are
/// kept together here.
//--------------------------------------------------------------------------------------------------
struct FishbonesLateralSegment
{
std::vector<int> segmentNumbers;
size_t globalCellIndex;
double equivalentDiameter;
};
//--------------------------------------------------------------------------------------------------
/// The cell intersections of one fishbones lateral, in order from the ICD sub and outwards.
//--------------------------------------------------------------------------------------------------
struct FishbonesLateral
{
std::vector<FishbonesLateralSegment> segments;
};
//--------------------------------------------------------------------------------------------------
/// One fishbones ICD sub segment, recorded with the grid cells it connects to.
//--------------------------------------------------------------------------------------------------
struct FishbonesIcdSegment
{
int segmentNumber;
std::set<size_t> globalCellIndices;
};
//--------------------------------------------------------------------------------------------------
/// Data collected across all fishbones of a well, used by the apply functions below.
//--------------------------------------------------------------------------------------------------
struct FishbonesExportContext
{
std::vector<FishbonesLateral> laterals;
std::vector<FishbonesIcdSegment> icdSegments;
};
//--------------------------------------------------------------------------------------------------
/// Replace the WELSEGS diameter of fishbones lateral segments with an effective diameter.
///
/// Several laterals may run through the same grid cell. The combined flow area is represented by
/// Deff = sqrt(d1^2 + d2^2 + ..) over the lateral segments in that cell, see
/// https://github.com/OPM/ResInsight/issues/7686
///
/// The first cell intersection of a lateral shares its cell with the main bore and with the first
/// intersection of every other lateral on the same sub, which inflates Deff. It therefore inherits
/// the effective diameter of the second intersection of the same lateral, see
/// https://github.com/OPM/ResInsight/issues/7731
//--------------------------------------------------------------------------------------------------
void applyEffectiveDiameters( const FishbonesExportContext& context, RigMswWellExportData& exportData );
//--------------------------------------------------------------------------------------------------
/// Replace the WSEGVALV area of fishbones ICD subs connected to the same grid cell with the sum of
/// their areas, so that the cell sees the total flow area of the ICDs completing it.
///
/// The sums are computed from the original areas only. An ICD sub reaching into more than one cell
/// reports the largest of the sums it takes part in.
//--------------------------------------------------------------------------------------------------
void applyIcdAreaPerCell( const FishbonesExportContext& context, RigMswWellExportData& exportData );
//--------------------------------------------------------------------------------------------------
/// Convert a WellPathCellIntersectionInfo global-cell index to a RigMswCellIntersection (1-based i,j,k).
/// Returns std::nullopt for gap-segments (globCellIndex >= totalCellCount).|
//--------------------------------------------------------------------------------------------------
std::optional<RigMswCellIntersection>
toMswCellIntersection( const WellPathCellIntersectionInfo& cellInfo, const RigMainGrid* mainGrid, double distanceStart, double distanceEnd );
//--------------------------------------------------------------------------------------------------
/// Find the main-bore outlet segment number for a given measured depth.
/// Returns 1 (heel) if cellSegMap is empty; returns the last entry's segment number if md is
/// beyond the end of all mapped cells.
//--------------------------------------------------------------------------------------------------
int findOutletSegmentForMD( const std::vector<CellSegmentEntry>& cellSegMap, double md );
//--------------------------------------------------------------------------------------------------
/// Split the cell pieces of a branch into the spans served by each segment node. Without
/// segmentation rules each piece is served by its own centre node. Otherwise the pieces are split
/// at the nodes, and each part is served by the nearest node upstream of it (the most upstream node
/// for parts above all nodes). Consecutive parts served by the same node are merged.
//--------------------------------------------------------------------------------------------------
std::vector<NodeSpan> segmentSpans( const std::vector<std::pair<double, double>>& cellPieces, const std::vector<double>& nodes );
//--------------------------------------------------------------------------------------------------
/// Build main-bore WELSEGS segments directly from well-path geometry.
/// Segment nodes are placed at cell centres and adjusted by the segmentation intervals (min/max
/// segment length). A COMPSEGS entry is embedded for each grid-cell intersection overlapping a bare
/// perforation (no active valve), on the segment whose node is nearest the entry centre.
/// Optionally fills cellSegMap for later valve outlet-segment lookups.
//--------------------------------------------------------------------------------------------------
RigMswBranch buildMainBoreBranch( const RimWellPath* wellPath,
const std::vector<WellPathCellIntersectionInfo>& filteredIntersections,
const RigMainGrid* mainGrid,
const RimEclipseCase* eclipseCase,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
const std::set<const RimPerforationInterval*>& valvedIntervals,
const std::string& infoType,
double heelMD,
double heelTVD,
int branchNumber,
int& segmentNumber,
int outletSegmentNumber,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
const RicMswTableDataTools::SegmentationSettings& segmentationSettings,
const std::optional<QDateTime>& exportDate,
RiaDefines::EclipseUnitSystem unitSystem,
std::vector<CellSegmentEntry>* cellSegMap,
const RigActiveCellInfo* activeCellInfo = nullptr );
//--------------------------------------------------------------------------------------------------
/// Build WELSEGS + COMPSEGS segments for ICD/ICV/AICD/SICD valve completions.
//--------------------------------------------------------------------------------------------------
std::vector<RigMswBranch> buildValveBranches( const RimWellPath* wellPath,
const std::vector<WellPathCellIntersectionInfo>& filteredIntersections,
const RigMainGrid* mainGrid,
const RimEclipseCase* eclipseCase,
const std::vector<const RimPerforationInterval*>& perforationIntervals,
const std::vector<CellSegmentEntry>& cellSegMap,
const std::string& infoType,
const std::string& wellNameForExport,
int& segmentNumber,
int& branchNumber,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
const std::optional<QDateTime>& exportDate,
RiaDefines::EclipseUnitSystem unitSystem );
//--------------------------------------------------------------------------------------------------
/// Build WELSEGS + COMPSEGS segments for fracture completions.
//--------------------------------------------------------------------------------------------------
std::vector<RigMswBranch> buildFractureBranches( RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const RigMainGrid* mainGrid,
const std::vector<CellSegmentEntry>& cellSegMap,
const std::string& infoType,
int& segmentNumber,
int& branchNumber );
//--------------------------------------------------------------------------------------------------
/// Build WELSEGS + COMPSEGS + WSEGVALV segments for fishbones completions.
//--------------------------------------------------------------------------------------------------
std::vector<RigMswBranch> buildFishbonesBranches( const RimEclipseCase* eclipseCase,
const RimWellPath* wellPath,
const RigMainGrid* mainGrid,
const std::vector<WellPathCellIntersectionInfo>& filteredIntersections,
const std::vector<CellSegmentEntry>& cellSegMap,
const std::string& infoType,
const std::string& wellNameForExport,
int& segmentNumber,
int& branchNumber,
const std::vector<std::pair<double, double>>& customSegmentIntervals,
RiaDefines::EclipseUnitSystem unitSystem,
FishbonesExportContext& fishbonesContext );
} // namespace RicMswBranchBuilder