///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2017- Statoil 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 "RicExportFractureCompletionsImpl.h" #include "RiaLogging.h" #include "RicExportCompletionDataSettingsUi.h" #include "RimEclipseCase.h" #include "RimFracture.h" #include "RimWellPath.h" #include "RimFractureTemplate.h" #include "RimEclipseView.h" #include "RimEclipseWell.h" #include "RimSimWellFractureCollection.h" #include "RimStimPlanFractureTemplate.h" #include "RimSimWellFracture.h" #include "RimWellPathCompletions.h" #include "RigEclipseCaseData.h" #include "RigTransmissibilityCondenser.h" #include "RigFractureCell.h" #include "RigFractureGrid.h" #include "RigEclipseToStimPlanCellTransmissibilityCalculator.h" #include "RigFractureTransmissibilityEquations.h" #include "RigWellPathStimplanIntersector.h" #include "RigMainGrid.h" #include "RigSingleWellResultsData.h" #include "RigSimulationWellCoordsAndMD.h" #include "RigWellPath.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicExportFractureCompletionsImpl::generateCompdatValuesForWellPath(RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings, QTextStream* outputStreamForIntermediateResultsText) { RimEclipseCase* caseToApply = settings.caseToApply(); std::vector fracturesAlongWellPath; wellPath->descendantsIncludingThisOfType(fracturesAlongWellPath); return generateCompdatValues(caseToApply, wellPath->completions()->wellNameForExport(), wellPath->wellPathGeometry(), fracturesAlongWellPath, outputStreamForIntermediateResultsText); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicExportFractureCompletionsImpl::generateCompdatValuesForSimWell(RimEclipseCase* eclipseCase, const RimEclipseWell* well, QTextStream* outputStreamForIntermediateResultsText) { std::vector completionData; std::vector< std::vector > pipeBranchesCLCoords; std::vector< std::vector > pipeBranchesCellIds; RimEclipseView* view = nullptr; well->firstAncestorOrThisOfTypeAsserted(view); size_t timeStep = view->currentTimeStep(); well->calculateWellPipeDynamicCenterLine(timeStep, pipeBranchesCLCoords, pipeBranchesCellIds); for (size_t branchIndex = 0; branchIndex < pipeBranchesCLCoords.size(); ++branchIndex) { RigSimulationWellCoordsAndMD coordsAndMD(pipeBranchesCLCoords[branchIndex]); RigWellPath wellPathGeometry; wellPathGeometry.m_wellPathPoints = coordsAndMD.wellPathPoints(); wellPathGeometry.m_measuredDepths = coordsAndMD.measuredDepths(); std::vector fractures; for (RimSimWellFracture* fracture : well->simwellFractureCollection->simwellFractures()) { if (static_cast(fracture->branchIndex()) == branchIndex) { fractures.push_back(fracture); } } std::vector branchCompletions = generateCompdatValues(eclipseCase, well->name(), &wellPathGeometry, fractures, outputStreamForIntermediateResultsText); completionData.insert(completionData.end(), branchCompletions.begin(), branchCompletions.end()); } return completionData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RicExportFractureCompletionsImpl::generateCompdatValues(RimEclipseCase* caseToApply, const QString& wellPathName, const RigWellPath* wellPathGeometry, const std::vector fractures, QTextStream* outputStreamForIntermediateResultsText) { double cDarcyInCorrectUnit = RiaEclipseUnitTools::darcysConstant(caseToApply->eclipseCaseData()->unitsType()); const RigMainGrid* mainGrid = caseToApply->eclipseCaseData()->mainGrid(); // To handle several fractures in the same eclipse cell we need to keep track of the transmissibility // to the well from each fracture intersecting the cell and sum these transmissibilities at the end. // std::map > std::map > eclCellIdxToTransPrFractureMap; std::vector fractureCompletions; for (RimFracture* fracture : fractures) { bool useFiniteConductivityInFracture = (fracture->fractureTemplate()->conductivityType() == RimFractureTemplate::FINITE_CONDUCTIVITY); RimFractureTemplate* fracTemplate = fracture->fractureTemplate(); const RigFractureGrid* fractureGrid = fracTemplate->fractureGrid(); //If finite cond chosen and conductivity not present in stimplan file, do not calculate trans for this fracture if (useFiniteConductivityInFracture) { if (dynamic_cast(fracTemplate)) { RimStimPlanFractureTemplate* fracTemplateStimPlan = dynamic_cast(fracTemplate); bool conductivityInFile = false; for (auto parameterUnit : fracTemplateStimPlan->resultNamesWithUnit()) { if (parameterUnit.first == "CONDUCTIVITY") conductivityInFile = true; } if (!conductivityInFile) { RiaLogging::error("Trying to export completion data for stimPlan fracture without conductivity data for " + fracture->name()); RiaLogging::error("No transmissibilities will be calculated for " + fracture->name()); continue; } } } using CellIdxSpace = RigTransmissibilityCondenser::CellAddress; RigTransmissibilityCondenser transCondenser; ////// // Calculate Matrix To Fracture Trans const std::vector& fractureCells = fractureGrid->fractureCells(); for (const RigFractureCell fractureCell : fractureCells) { if (!fractureCell.hasNonZeroConductivity()) continue; RigEclipseToStimPlanCellTransmissibilityCalculator eclToFractureTransCalc(caseToApply, fracture->transformMatrix(), fracture->fractureTemplate()->skinFactor, cDarcyInCorrectUnit, fractureCell); const std::vector& fractureCellContributingEclipseCells = eclToFractureTransCalc.globalIndeciesToContributingEclipseCells(); const std::vector& fractureCellContributingEclipseCellTransmissibilities = eclToFractureTransCalc.contributingEclipseCellTransmissibilities(); size_t stimPlanCellIndex = fractureGrid->getGlobalIndexFromIJ(fractureCell.getI(), fractureCell.getJ()); bool hasAnyContribEclCellWithinContainment = false; for ( size_t i = 0; i < fractureCellContributingEclipseCells.size(); i++ ) { if ( fracture->isEclipseCellWithinContainment(caseToApply->eclipseCaseData()->mainGrid(), fractureCellContributingEclipseCells[i]) ) { hasAnyContribEclCellWithinContainment = true; if ( useFiniteConductivityInFracture ) { transCondenser.addNeighborTransmissibility({ true, CellIdxSpace::ECLIPSE, fractureCellContributingEclipseCells[i] }, { false, CellIdxSpace::STIMPLAN, stimPlanCellIndex }, fractureCellContributingEclipseCellTransmissibilities[i]); } else { transCondenser.addNeighborTransmissibility({ true, CellIdxSpace::ECLIPSE, fractureCellContributingEclipseCells[i] }, { true, CellIdxSpace::WELL, 1 }, fractureCellContributingEclipseCellTransmissibilities[i]); } } } } ////// // Calculate Transmissibility in the fracture: From one StimPlan Cell to the other if (useFiniteConductivityInFracture) { for (size_t i = 0; i < fractureGrid->iCellCount(); i++) { for (size_t j = 0; j < fractureGrid->jCellCount(); j++) { size_t fractureCellIndex = fractureGrid->getGlobalIndexFromIJ(i, j); const RigFractureCell& fractureCell = fractureGrid->cellFromIndex(fractureCellIndex); if (!fractureCell.hasNonZeroConductivity()) continue; if ( i < fractureGrid->iCellCount() - 1 ) { size_t fractureCellNeighbourXIndex = fractureGrid->getGlobalIndexFromIJ(i + 1, j); const RigFractureCell& fractureCellNeighbourX = fractureGrid->cellFromIndex(fractureCellNeighbourXIndex); double horizontalTransToXneigbour = RigFractureTransmissibilityEquations::centerToCenterFractureCellTrans(fractureCell.getConductivtyValue(), fractureCell.cellSizeX(), fractureCell.cellSizeZ(), fractureCellNeighbourX.getConductivtyValue(), fractureCellNeighbourX.cellSizeX(), fractureCellNeighbourX.cellSizeZ(), cDarcyInCorrectUnit); transCondenser.addNeighborTransmissibility({ false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, fractureCellIndex }, { false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, fractureCellNeighbourXIndex }, horizontalTransToXneigbour); } if ( j < fractureGrid->jCellCount() - 1 ) { size_t fractureCellNeighbourZIndex = fractureGrid->getGlobalIndexFromIJ(i, j + 1); const RigFractureCell& fractureCellNeighbourZ = fractureGrid->cellFromIndex(fractureCellNeighbourZIndex); double verticalTransToZneigbour = RigFractureTransmissibilityEquations::centerToCenterFractureCellTrans(fractureCell.getConductivtyValue(), fractureCell.cellSizeZ(), fractureCell.cellSizeX(), fractureCellNeighbourZ.getConductivtyValue(), fractureCellNeighbourZ.cellSizeZ(), fractureCellNeighbourZ.cellSizeX(), cDarcyInCorrectUnit); transCondenser.addNeighborTransmissibility({ false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, fractureCellIndex }, { false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, fractureCellNeighbourZIndex }, verticalTransToZneigbour); } } } } ///// // Calculate transmissibility into the well if (useFiniteConductivityInFracture) { //// //If fracture has orientation Azimuth or Transverse, assume only radial inflow if ( fracture->fractureTemplate()->orientationType() == RimFractureTemplate::AZIMUTH || fracture->fractureTemplate()->orientationType() == RimFractureTemplate::TRANSVERSE_WELL_PATH) { const RigFractureGrid* fracGrid = fracture->fractureTemplate()->fractureGrid(); std::pair wellCellIJ = fracGrid->fractureCellAtWellCenter(); size_t wellCellIndex = fracGrid->getGlobalIndexFromIJ(wellCellIJ.first, wellCellIJ.second); const RigFractureCell& wellCell = fractureGrid->cellFromIndex(wellCellIndex); double radialTrans = RigFractureTransmissibilityEquations::fractureCellToWellRadialTrans(wellCell.getConductivtyValue(), wellCell.cellSizeX(), wellCell.cellSizeZ(), fracture->wellRadius(caseToApply->eclipseCaseData()->unitsType()), fracTemplate->skinFactor(), cDarcyInCorrectUnit); transCondenser.addNeighborTransmissibility({ true, RigTransmissibilityCondenser::CellAddress::WELL, 1 }, { false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, wellCellIndex }, radialTrans); } else if (fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH) { //// //If fracture has orientation along well, linear inflow along well and radial flow at endpoints RigWellPathStimplanIntersector wellFractureIntersector(wellPathGeometry, fracture); const std::map& fractureWellCells = wellFractureIntersector.intersections(); for (const auto& fracCellIdxIsectDataPair : fractureWellCells) { size_t fracWellCellIdx = fracCellIdxIsectDataPair.first; RigWellPathStimplanIntersector::WellCellIntersection intersection = fracCellIdxIsectDataPair.second; const RigFractureCell& fractureWellCell = fractureGrid->cellFromIndex(fracWellCellIdx); double linearTrans = 0.0; if (intersection.hlength > 0.0 || intersection.vlength > 0.0) { linearTrans = RigFractureTransmissibilityEquations::fractureCellToWellLinearTrans(fractureWellCell.getConductivtyValue(), fractureWellCell.cellSizeX(), fractureWellCell.cellSizeZ(), intersection.vlength, intersection.hlength, fracture->perforationEfficiency, fracTemplate->skinFactor(), cDarcyInCorrectUnit); } transCondenser.addNeighborTransmissibility({ true, RigTransmissibilityCondenser::CellAddress::WELL, 1 }, { false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, fracWellCellIdx }, linearTrans); } } } ///// // Insert total transmissibility from eclipse-cell to well for this fracture into the map std::set externalCells = transCondenser.externalCells(); for (RigTransmissibilityCondenser::CellAddress externalCell : externalCells) { if (externalCell.m_cellIndexSpace == RigTransmissibilityCondenser::CellAddress::ECLIPSE) { double trans = transCondenser.condensedTransmissibility(externalCell, { true, RigTransmissibilityCondenser::CellAddress::WELL, 1 }); eclCellIdxToTransPrFractureMap[externalCell.m_globalCellIdx][fracture] = trans; size_t i, j, k; mainGrid->ijkFromCellIndex(externalCell.m_globalCellIdx, &i, &j, &k); RigCompletionData compDat(wellPathName, {i,j,k} ); compDat.setFromFracture(trans, fracture->fractureTemplate()->skinFactor()); compDat.addMetadata(fracture->name(), QString::number(trans)); fractureCompletions.push_back(compDat); } } if ( outputStreamForIntermediateResultsText ) { (*outputStreamForIntermediateResultsText) << "\n" << "\n" << "\n----------- All Transimissibilities " << fracture->name() << " -------------------- \n\n"; (*outputStreamForIntermediateResultsText) << QString::fromStdString(transCondenser.neighborTransDebugOutput(mainGrid, fractureGrid)); (*outputStreamForIntermediateResultsText) << "\n" << "\n" << "\n----------- Condensed Results " << fracture->name() << " -------------------- \n\n"; (*outputStreamForIntermediateResultsText) << QString::fromStdString(transCondenser.condensedTransDebugOutput(mainGrid, fractureGrid)); (*outputStreamForIntermediateResultsText) << "\n" ; } } return fractureCompletions; }