mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Merge branch 'fishbones' into pre-proto
This commit is contained in:
@@ -322,3 +322,46 @@ exceptions:
|
||||
CRAVA is a software package for seismic inversion and conditioning of
|
||||
geological reservoir models. CRAVA is copyrighted by the Norwegian
|
||||
Computing Center and Statoil and licensed under GPLv3+.
|
||||
|
||||
===============================================================================
|
||||
Notice for opm-flowdiagnostics and opm-flowdiagnostics-applications libraries
|
||||
===============================================================================
|
||||
|
||||
Copyright 2016, 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
===============================================================================
|
||||
Notice for the NightCharts code
|
||||
===============================================================================
|
||||
|
||||
NightCharts
|
||||
Copyright (C) 2010 by Alexander A. Avdonin, Artem N. Ivanov / ITGears Co.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Please contact gordos.kund@gmail.com with any questions on this license.
|
||||
|
||||
@@ -47,9 +47,9 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterface
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Summary
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Completions
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Flow
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Fishbones
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Summary
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ResultStatisticsCache
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ReservoirDataModel
|
||||
@@ -108,7 +108,7 @@ list( APPEND REFERENCED_CMAKE_FILES
|
||||
ProjectDataModel/CMakeLists_files.cmake
|
||||
ProjectDataModel/Summary/CMakeLists_files.cmake
|
||||
ProjectDataModel/Flow/CMakeLists_files.cmake
|
||||
ProjectDataModel/Fishbones/CMakeLists_files.cmake
|
||||
ProjectDataModel/Completions/CMakeLists_files.cmake
|
||||
|
||||
GeoMech/GeoMechVisualization/CMakeLists_files.cmake
|
||||
|
||||
@@ -120,15 +120,15 @@ list( APPEND REFERENCED_CMAKE_FILES
|
||||
|
||||
Commands/CMakeLists_files.cmake
|
||||
Commands/ApplicationCommands/CMakeLists_files.cmake
|
||||
Commands/CompletionCommands/CMakeLists_files.cmake
|
||||
Commands/CrossSectionCommands/CMakeLists_files.cmake
|
||||
Commands/EclipseCommands/CMakeLists_files.cmake
|
||||
Commands/EclipseCommands/EclipseWell/CMakeLists_files.cmake
|
||||
Commands/FishbonesCommands/CMakeLists_files.cmake
|
||||
Commands/PerforationCommands/CMakeLists_files.cmake
|
||||
Commands/FlowCommands/CMakeLists_files.cmake
|
||||
Commands/IntersectionBoxCommands/CMakeLists_files.cmake
|
||||
Commands/OctaveScriptCommands/CMakeLists_files.cmake
|
||||
Commands/OperationsUsingObjReferences/CMakeLists_files.cmake
|
||||
Commands/OperationsUsingObjReferences/CMakeLists_files.cmake
|
||||
Commands/SummaryPlotCommands/CMakeLists_files.cmake
|
||||
Commands/ToggleCommands/CMakeLists_files.cmake
|
||||
Commands/ViewLink/CMakeLists_files.cmake
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
# Use this workaround until we're on 2.8.3 on all platforms and can use CMAKE_CURRENT_LIST_DIR directly
|
||||
if (${CMAKE_VERSION} VERSION_GREATER "2.8.2")
|
||||
set(CEE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_DIR}/)
|
||||
endif()
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicCaseAndFileExportSettingsUi.h
|
||||
${CEE_CURRENT_LIST_DIR}RicExportCompletionDataSettingsUi.h
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesLateralsFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesWellSegmentsFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicExportWellSegmentsSettingsUi.h
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsAtMeasuredDepthFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathExportCompletionDataFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportCompletionsFileFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportPerforationIntervalsFeature.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicCaseAndFileExportSettingsUi.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicExportCompletionDataSettingsUi.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesLateralsFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesWellSegmentsFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicExportWellSegmentsSettingsUi.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsAtMeasuredDepthFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathExportCompletionDataFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportCompletionsFileFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportPerforationIntervalsFeature.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
${SOURCE_GROUP_HEADER_FILES}
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES
|
||||
${SOURCE_GROUP_SOURCE_FILES}
|
||||
)
|
||||
|
||||
source_group( "CommandFeature\\Completion" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CEE_CURRENT_LIST_DIR}CMakeLists_files.cmake )
|
||||
@@ -16,22 +16,22 @@
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimCaseAndFileExportSettings.h"
|
||||
#include "RicCaseAndFileExportSettingsUi.h"
|
||||
|
||||
#include "RimTools.h"
|
||||
|
||||
#include "cafPdmUiFilePathEditor.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RimCaseAndFileExportSettings, "RimCaseAndFileExportSettings");
|
||||
CAF_PDM_SOURCE_INIT(RicCaseAndFileExportSettingsUi, "RicCaseAndFileExportSettingsUi");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimCaseAndFileExportSettings::RimCaseAndFileExportSettings()
|
||||
RicCaseAndFileExportSettingsUi::RicCaseAndFileExportSettingsUi()
|
||||
{
|
||||
CAF_PDM_InitObject("RimCaseAndFileExportSettings", "", "", "");
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&fileName, "Filename", "Export filename", "", "", "");
|
||||
CAF_PDM_InitFieldNoDefault(&fileName, "Filename", "Export Filename", "", "", "");
|
||||
fileName.uiCapability()->setUiEditorTypeName(caf::PdmUiFilePathEditor::uiEditorTypeName());
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&caseToApply, "CaseToApply", "Case to Apply", "", "", "");
|
||||
@@ -40,7 +40,7 @@ RimCaseAndFileExportSettings::RimCaseAndFileExportSettings()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QList<caf::PdmOptionItemInfo> RimCaseAndFileExportSettings::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly)
|
||||
QList<caf::PdmOptionItemInfo> RicCaseAndFileExportSettingsUi::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly)
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
|
||||
@@ -55,7 +55,7 @@ QList<caf::PdmOptionItemInfo> RimCaseAndFileExportSettings::calculateValueOption
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimCaseAndFileExportSettings::defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute)
|
||||
void RicCaseAndFileExportSettingsUi::defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute)
|
||||
{
|
||||
if (field == &fileName)
|
||||
{
|
||||
@@ -1,7 +1,6 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
// Copyright (C) 2017- Ceetron Solutions AS
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -29,12 +28,12 @@
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimCaseAndFileExportSettings : public caf::PdmObject
|
||||
class RicCaseAndFileExportSettingsUi : public caf::PdmObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
public:
|
||||
|
||||
RimCaseAndFileExportSettings();
|
||||
RicCaseAndFileExportSettingsUi();
|
||||
|
||||
caf::PdmField<QString> fileName;
|
||||
caf::PdmPtrField<RimEclipseCase*> caseToApply;
|
||||
@@ -0,0 +1,65 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicExportCompletionDataSettingsUi.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RicExportCompletionDataSettingsUi, "RicExportCompletionDataSettingsUi");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RicExportCompletionDataSettingsUi::RicExportCompletionDataSettingsUi()
|
||||
{
|
||||
CAF_PDM_InitObject("RimExportCompletionDataSettings", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&timeStep, "TimeStepIndex", 0, "Time Step", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&includePerforations, "IncludePerforations", true, "Include Perforations", "", "", "");
|
||||
CAF_PDM_InitField(&includeFishbones, "IncludeFishbones", true, "Include Fishbones", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&includeWpimult, "IncludeWPIMULT", true, "Include WPIMLUT", "", "", "");
|
||||
CAF_PDM_InitField(&removeLateralsInMainBoreCells, "RemoveLateralsInMainBoreCells", false, "Remove Laterals in Main Bore Cells", "", "", "");
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QList<caf::PdmOptionItemInfo> RicExportCompletionDataSettingsUi::calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly)
|
||||
{
|
||||
QList<caf::PdmOptionItemInfo> options;
|
||||
if (fieldNeedingOptions == &timeStep)
|
||||
{
|
||||
QStringList timeStepNames;
|
||||
|
||||
if (caseToApply)
|
||||
{
|
||||
timeStepNames = caseToApply->timeStepStrings();
|
||||
}
|
||||
|
||||
for (int i = 0; i < timeStepNames.size(); i++)
|
||||
{
|
||||
options.push_back(caf::PdmOptionItemInfo(timeStepNames[i], i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options = RicCaseAndFileExportSettingsUi::calculateValueOptions(fieldNeedingOptions, useOptionsOnly);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RimCaseAndFileExportSettings.h"
|
||||
#include "RicCaseAndFileExportSettingsUi.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
|
||||
@@ -26,13 +26,21 @@
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimExportCompletionDataSettings : public RimCaseAndFileExportSettings
|
||||
class RicExportCompletionDataSettingsUi : public RicCaseAndFileExportSettingsUi
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
public:
|
||||
RicExportCompletionDataSettingsUi();
|
||||
|
||||
RimExportCompletionDataSettings();
|
||||
|
||||
caf::PdmField<bool> includePerforations;
|
||||
caf::PdmField<bool> includeFishbones;
|
||||
|
||||
caf::PdmField<bool> includeWpimult;
|
||||
caf::PdmField<bool> removeLateralsInMainBoreCells;
|
||||
|
||||
caf::PdmField<int> timeStep;
|
||||
|
||||
protected:
|
||||
virtual QList<caf::PdmOptionItemInfo> calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool * useOptionsOnly) override;
|
||||
};
|
||||
@@ -0,0 +1,366 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicExportFishbonesWellSegmentsFeature.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimProject.h"
|
||||
#include "RimFishboneWellPathCollection.h"
|
||||
#include "RimFishbonesCollection.h"
|
||||
#include "RimFishbonesMultipleSubs.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimEclipseCase.h"
|
||||
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
|
||||
#include "RiuMainWindow.h"
|
||||
|
||||
#include "cafSelectionManager.h"
|
||||
#include "cafPdmUiPropertyViewDialog.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
|
||||
CAF_CMD_SOURCE_INIT(RicExportFishbonesWellSegmentsFeature, "RicExportFishbonesWellSegmentsFeature");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportFishbonesWellSegmentsFeature::onActionTriggered(bool isChecked)
|
||||
{
|
||||
RimFishbonesCollection* fishbonesCollection = selectedFishbonesCollection();
|
||||
RimWellPath* wellPath = selectedWellPath();
|
||||
CVF_ASSERT(fishbonesCollection);
|
||||
CVF_ASSERT(wellPath);
|
||||
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
|
||||
QString projectFolder = app->currentProjectPath();
|
||||
QString defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallback("COMPLETIONS", projectFolder);
|
||||
|
||||
RicExportWellSegmentsSettingsUi exportSettings;
|
||||
std::vector<RimCase*> cases;
|
||||
app->project()->allCases(cases);
|
||||
for (auto c : cases)
|
||||
{
|
||||
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>(c);
|
||||
if (eclipseCase != nullptr)
|
||||
{
|
||||
exportSettings.caseToApply = eclipseCase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exportSettings.fileName = QDir(defaultDir).filePath("WellSegments");
|
||||
|
||||
caf::PdmUiPropertyViewDialog propertyDialog(RiuMainWindow::instance(), &exportSettings, "Export Completion Data", "");
|
||||
if (propertyDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
RiaApplication::instance()->setLastUsedDialogDirectory("COMPLETIONS", QFileInfo(exportSettings.fileName).absolutePath());
|
||||
|
||||
std::vector<RimFishbonesMultipleSubs*> fishbonesSubs;
|
||||
for (RimFishbonesMultipleSubs* subs : fishbonesCollection->fishbonesSubs)
|
||||
{
|
||||
fishbonesSubs.push_back(subs);
|
||||
}
|
||||
|
||||
exportWellSegments(wellPath, fishbonesSubs, exportSettings);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimFishbonesCollection* RicExportFishbonesWellSegmentsFeature::selectedFishbonesCollection()
|
||||
{
|
||||
RimFishbonesCollection* objToFind = nullptr;
|
||||
|
||||
caf::PdmUiItem* pdmUiItem = caf::SelectionManager::instance()->selectedItem();
|
||||
|
||||
caf::PdmObjectHandle* objHandle = dynamic_cast<caf::PdmObjectHandle*>(pdmUiItem);
|
||||
if (objHandle)
|
||||
{
|
||||
objHandle->firstAncestorOrThisOfType(objToFind);
|
||||
}
|
||||
|
||||
return objToFind;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellPath* RicExportFishbonesWellSegmentsFeature::selectedWellPath()
|
||||
{
|
||||
RimWellPath* objToFind = nullptr;
|
||||
|
||||
caf::PdmUiItem* pdmUiItem = caf::SelectionManager::instance()->selectedItem();
|
||||
|
||||
caf::PdmObjectHandle* objHandle = dynamic_cast<caf::PdmObjectHandle*>(pdmUiItem);
|
||||
if (objHandle)
|
||||
{
|
||||
objHandle->firstAncestorOrThisOfType(objToFind);
|
||||
}
|
||||
|
||||
return objToFind;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportFishbonesWellSegmentsFeature::setupActionLook(QAction* actionToSetup)
|
||||
{
|
||||
actionToSetup->setText("Export Well Segments");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicExportFishbonesWellSegmentsFeature::isCommandEnabled()
|
||||
{
|
||||
if (selectedFishbonesCollection())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportFishbonesWellSegmentsFeature::exportWellSegments(const RimWellPath* wellPath, const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs, const RicExportWellSegmentsSettingsUi& settings)
|
||||
{
|
||||
QFile exportFile(settings.fileName());
|
||||
|
||||
if (settings.caseToApply() == nullptr)
|
||||
{
|
||||
RiaLogging::error("Export Well Segments: Cannot export completions data without specified eclipse case");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!exportFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
RiaLogging::error(QString("Export Well Segments: Could not open the file: %1").arg(settings.fileName()));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<WellSegmentLocation> locations = RicWellPathExportCompletionDataFeature::findWellSegmentLocations(settings.caseToApply, wellPath, fishbonesSubs);
|
||||
|
||||
QTextStream stream(&exportFile);
|
||||
RifEclipseDataTableFormatter formatter(stream);
|
||||
generateWelsegsTable(formatter, wellPath, settings, locations);
|
||||
generateCompsegsTable(formatter, wellPath, settings, locations);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportFishbonesWellSegmentsFeature::generateWelsegsTable(RifEclipseDataTableFormatter& formatter, const RimWellPath* wellPath, const RicExportWellSegmentsSettingsUi& settings, const std::vector<WellSegmentLocation>& locations)
|
||||
{
|
||||
formatter.keyword("WELSEGS");
|
||||
|
||||
const WellSegmentLocation& firstLocation = locations[0];
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Name"),
|
||||
RifEclipseOutputTableColumn("Dep 1"),
|
||||
RifEclipseOutputTableColumn("Tlen 1"),
|
||||
RifEclipseOutputTableColumn("Vol 1"),
|
||||
RifEclipseOutputTableColumn("Len&Dep"),
|
||||
RifEclipseOutputTableColumn("PresDrop"),
|
||||
};
|
||||
formatter.header(header);
|
||||
|
||||
formatter.add(wellPath->name());
|
||||
formatter.add(firstLocation.trueVerticalDepth);
|
||||
formatter.add(firstLocation.measuredDepth);
|
||||
formatter.add("1*");
|
||||
formatter.add(settings.lengthAndDepth().text());
|
||||
formatter.add(settings.pressureDrop().text());
|
||||
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("First Seg"),
|
||||
RifEclipseOutputTableColumn("Last Seg"),
|
||||
RifEclipseOutputTableColumn("Branch Num"),
|
||||
RifEclipseOutputTableColumn("Outlet Seg"),
|
||||
RifEclipseOutputTableColumn("Length"),
|
||||
RifEclipseOutputTableColumn("Depth Change"),
|
||||
RifEclipseOutputTableColumn("Diam"),
|
||||
RifEclipseOutputTableColumn("Rough"),
|
||||
};
|
||||
formatter.header(header);
|
||||
}
|
||||
|
||||
{
|
||||
WellSegmentLocation previousLocation = firstLocation;
|
||||
formatter.comment("Main stem");
|
||||
|
||||
double depth = 0;
|
||||
double length = 0;
|
||||
|
||||
for (size_t i = 0; i < locations.size(); ++i)
|
||||
{
|
||||
const WellSegmentLocation& location = locations[i];
|
||||
|
||||
if (settings.lengthAndDepth() == RicExportWellSegmentsSettingsUi::INC)
|
||||
{
|
||||
depth = location.trueVerticalDepth - previousLocation.trueVerticalDepth;
|
||||
length = location.fishbonesSubs->locationOfSubs()[location.subIndex] - previousLocation.fishbonesSubs->locationOfSubs()[previousLocation.subIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
depth += location.trueVerticalDepth - previousLocation.trueVerticalDepth;
|
||||
length += location.fishbonesSubs->locationOfSubs()[location.subIndex] - previousLocation.fishbonesSubs->locationOfSubs()[previousLocation.subIndex];
|
||||
}
|
||||
|
||||
formatter.comment(QString("Segment for sub %1").arg(location.subIndex));
|
||||
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(-1.0); // FIXME : Diam of main stem?
|
||||
formatter.add(-1.0); // FIXME : Rough of main stem?
|
||||
formatter.rowCompleted();
|
||||
|
||||
previousLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
formatter.comment("Laterals");
|
||||
formatter.comment("Diam: MSW - Tubing Radius");
|
||||
formatter.comment("Rough: MSW - Open Hole Roughness Factor");
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
formatter.comment(QString("%1 : Sub index %2 - Lateral %3").arg(location.fishbonesSubs->name()).arg(location.subIndex).arg(lateral.lateralIndex));
|
||||
|
||||
double depth = 0;
|
||||
double length = 0;
|
||||
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (settings.lengthAndDepth() == RicExportWellSegmentsSettingsUi::INC)
|
||||
{
|
||||
depth = intersection.depth;
|
||||
length = intersection.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth += intersection.depth;
|
||||
length += intersection.length;
|
||||
}
|
||||
formatter.add(intersection.segmentNumber);
|
||||
formatter.add(intersection.segmentNumber);
|
||||
formatter.add(lateral.branchNumber);
|
||||
formatter.add(intersection.attachedSegmentNumber);
|
||||
formatter.add(length);
|
||||
formatter.add(depth);
|
||||
formatter.add(location.fishbonesSubs->tubingDiameter());
|
||||
formatter.add(location.fishbonesSubs->openHoleRoughnessFactor());
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportFishbonesWellSegmentsFeature::generateCompsegsTable(RifEclipseDataTableFormatter& formatter, const RimWellPath* wellPath, const RicExportWellSegmentsSettingsUi& settings, const std::vector<WellSegmentLocation>& locations)
|
||||
{
|
||||
RigMainGrid* grid = settings.caseToApply->eclipseCaseData()->mainGrid();
|
||||
formatter.keyword("COMPSEGS");
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Name")
|
||||
};
|
||||
formatter.header(header);
|
||||
formatter.add(wellPath->name());
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K"),
|
||||
RifEclipseOutputTableColumn("Branch no"),
|
||||
RifEclipseOutputTableColumn("Start Length"),
|
||||
RifEclipseOutputTableColumn("End Length"),
|
||||
RifEclipseOutputTableColumn("Dir Pen"),
|
||||
RifEclipseOutputTableColumn("End Range"),
|
||||
RifEclipseOutputTableColumn("Connection Depth")
|
||||
};
|
||||
formatter.header(header);
|
||||
}
|
||||
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
double length = 0;
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
length += intersection.length;
|
||||
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex(intersection.cellIndex, &i, &j, &k);
|
||||
|
||||
formatter.addZeroBasedCellIndex(i).addZeroBasedCellIndex(j).addZeroBasedCellIndex(k);
|
||||
formatter.add(lateral.branchNumber);
|
||||
formatter.add(length);
|
||||
formatter.add("1*");
|
||||
switch (intersection.direction)
|
||||
{
|
||||
case POS_I:
|
||||
case NEG_I:
|
||||
formatter.add("I");
|
||||
break;
|
||||
case POS_J:
|
||||
case NEG_J:
|
||||
formatter.add("J");
|
||||
break;
|
||||
case POS_K:
|
||||
case NEG_K:
|
||||
formatter.add("K");
|
||||
break;
|
||||
}
|
||||
formatter.add(-1);
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RifEclipseDataTableFormatter.h"
|
||||
|
||||
#include "RicExportWellSegmentsSettingsUi.h"
|
||||
|
||||
#include "RicWellPathExportCompletionDataFeature.h"
|
||||
|
||||
#include "cafCmdFeature.h"
|
||||
|
||||
class RimFishbonesCollection;
|
||||
class RimFishbonesMultipleSubs;
|
||||
class RimWellPath;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RicExportFishbonesWellSegmentsFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
|
||||
protected:
|
||||
virtual void onActionTriggered(bool isChecked) override;
|
||||
virtual void setupActionLook(QAction* actionToSetup) override;
|
||||
virtual bool isCommandEnabled() override;
|
||||
|
||||
private:
|
||||
static RimFishbonesCollection* selectedFishbonesCollection();
|
||||
static RimWellPath* selectedWellPath();
|
||||
|
||||
static void exportWellSegments(const RimWellPath* wellPath, const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs, const RicExportWellSegmentsSettingsUi& settings);
|
||||
static void generateWelsegsTable(RifEclipseDataTableFormatter& formatter, const RimWellPath* wellPath, const RicExportWellSegmentsSettingsUi& settings, const std::vector<WellSegmentLocation>& locations);
|
||||
static void generateCompsegsTable(RifEclipseDataTableFormatter& formatter, const RimWellPath* wellPath, const RicExportWellSegmentsSettingsUi& settings, const std::vector<WellSegmentLocation>& locations);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicExportWellSegmentsSettingsUi.h"
|
||||
|
||||
namespace caf {
|
||||
template<>
|
||||
void RicExportWellSegmentsSettingsUi::PressureDropEnum::setUp()
|
||||
{
|
||||
addItem(RicExportWellSegmentsSettingsUi::HYDROSTATIC, "H--", "Hydrostatic");
|
||||
addItem(RicExportWellSegmentsSettingsUi::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction");
|
||||
addItem(RicExportWellSegmentsSettingsUi::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration");
|
||||
setDefault(RicExportWellSegmentsSettingsUi::HYDROSTATIC);
|
||||
}
|
||||
|
||||
template<>
|
||||
void RicExportWellSegmentsSettingsUi::LengthAndDepthEnum::setUp()
|
||||
{
|
||||
addItem(RicExportWellSegmentsSettingsUi::INC, "INC", "Incremental");
|
||||
addItem(RicExportWellSegmentsSettingsUi::ABS, "ABS", "Absolute");
|
||||
setDefault(RicExportWellSegmentsSettingsUi::INC);
|
||||
}
|
||||
}
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RicExportWellSegmentsSettingsUi, "RicExportWellSegmentsSettingsUi");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RicExportWellSegmentsSettingsUi::RicExportWellSegmentsSettingsUi()
|
||||
{
|
||||
CAF_PDM_InitObject("RimExportWellSegmentsSettings", "", "", "");
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&pressureDrop, "PressureDrop", "Pressure Drop", "", "", "");
|
||||
CAF_PDM_InitFieldNoDefault(&lengthAndDepth, "LengthAndDepth", "Length and Depth", "", "", "");
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RicCaseAndFileExportSettingsUi.h"
|
||||
|
||||
#include "cafPdmField.h"
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RicExportWellSegmentsSettingsUi : public RicCaseAndFileExportSettingsUi
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
public:
|
||||
|
||||
enum PressureDropType {
|
||||
HYDROSTATIC,
|
||||
HYDROSTATIC_FRICTION,
|
||||
HYDROSTATIC_FRICTION_ACCELERATION
|
||||
};
|
||||
|
||||
typedef caf::AppEnum<RicExportWellSegmentsSettingsUi::PressureDropType> PressureDropEnum;
|
||||
|
||||
enum LengthAndDepthType {
|
||||
ABS,
|
||||
INC
|
||||
};
|
||||
|
||||
typedef caf::AppEnum<RicExportWellSegmentsSettingsUi::LengthAndDepthType> LengthAndDepthEnum;
|
||||
|
||||
RicExportWellSegmentsSettingsUi();
|
||||
|
||||
caf::PdmField<PressureDropEnum> pressureDrop;
|
||||
caf::PdmField<LengthAndDepthEnum> lengthAndDepth;
|
||||
};
|
||||
@@ -47,7 +47,7 @@ void RicNewFishbonesSubsAtMeasuredDepthFeature::onActionTriggered(bool isChecked
|
||||
CVF_ASSERT(wellPath);
|
||||
|
||||
RimFishbonesMultipleSubs* obj = new RimFishbonesMultipleSubs;
|
||||
wellPath->fishbonesCollection()->fishbonesSubs.push_back(obj);
|
||||
wellPath->fishbonesCollection()->appendFishbonesSubs(obj);
|
||||
|
||||
obj->setName(QString("Fishbones Subs (%1)").arg(wellPath->fishbonesCollection()->fishbonesSubs.size()));
|
||||
int integerValue = wellPathSelItem->m_measuredDepth;
|
||||
@@ -46,7 +46,7 @@ void RicNewFishbonesSubsFeature::onActionTriggered(bool isChecked)
|
||||
|
||||
RimFishbonesMultipleSubs* obj = new RimFishbonesMultipleSubs;
|
||||
obj->setName(QString("Fishbones Subs (%1)").arg(fishbonesCollection->fishbonesSubs.size()));
|
||||
fishbonesCollection->fishbonesSubs.push_back(obj);
|
||||
fishbonesCollection->appendFishbonesSubs(obj);
|
||||
|
||||
RicNewFishbonesSubsFeature::askUserToSetUsefulScaling(fishbonesCollection);
|
||||
|
||||
@@ -0,0 +1,626 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicWellPathExportCompletionDataFeature.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCollection.h"
|
||||
#include "RimFishbonesMultipleSubs.h"
|
||||
#include "RimFishbonesCollection.h"
|
||||
#include "RimFishboneWellPath.h"
|
||||
#include "RimFishboneWellPathCollection.h"
|
||||
#include "RimPerforationInterval.h"
|
||||
#include "RimPerforationCollection.h"
|
||||
#include "RicExportCompletionDataSettingsUi.h"
|
||||
|
||||
#include "RiuMainWindow.h"
|
||||
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
#include "RigWellPathIntersectionTools.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigWellPath.h"
|
||||
|
||||
#include "cafSelectionManager.h"
|
||||
#include "cafPdmUiPropertyViewDialog.h"
|
||||
|
||||
#include "cvfPlane.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
CAF_CMD_SOURCE_INIT(RicWellPathExportCompletionDataFeature, "RicWellPathExportCompletionDataFeature");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::isCommandEnabled()
|
||||
{
|
||||
return !selectedWellPaths().empty();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::onActionTriggered(bool isChecked)
|
||||
{
|
||||
std::vector<RimWellPath*> wellPaths = selectedWellPaths();
|
||||
|
||||
CVF_ASSERT(wellPaths.size() > 0);
|
||||
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
|
||||
QString projectFolder = app->currentProjectPath();
|
||||
QString defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallback("COMPLETIONS", projectFolder);
|
||||
|
||||
RicExportCompletionDataSettingsUi exportSettings;
|
||||
std::vector<RimCase*> cases;
|
||||
app->project()->allCases(cases);
|
||||
for (auto c : cases)
|
||||
{
|
||||
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>(c);
|
||||
if (eclipseCase != nullptr)
|
||||
{
|
||||
exportSettings.caseToApply = eclipseCase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exportSettings.fileName = QDir(defaultDir).filePath("Completions");
|
||||
|
||||
caf::PdmUiPropertyViewDialog propertyDialog(RiuMainWindow::instance(), &exportSettings, "Export Completion Data", "");
|
||||
if (propertyDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
RiaApplication::instance()->setLastUsedDialogDirectory("COMPLETIONS", QFileInfo(exportSettings.fileName).absolutePath());
|
||||
|
||||
exportCompletions(wellPaths, exportSettings);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::setupActionLook(QAction* actionToSetup)
|
||||
{
|
||||
actionToSetup->setText("Export Completion Data");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimWellPath*> RicWellPathExportCompletionDataFeature::selectedWellPaths()
|
||||
{
|
||||
std::vector<RimWellPath*> wellPaths;
|
||||
caf::SelectionManager::instance()->objectsByType(&wellPaths);
|
||||
|
||||
std::vector<RimWellPathCollection*> wellPathCollections;
|
||||
caf::SelectionManager::instance()->objectsByType(&wellPathCollections);
|
||||
|
||||
for (auto wellPathCollection : wellPathCollections)
|
||||
{
|
||||
for (auto wellPath : wellPathCollection->wellPaths())
|
||||
{
|
||||
wellPaths.push_back(wellPath);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<RimWellPath*> uniqueWellPaths(wellPaths.begin(), wellPaths.end());
|
||||
wellPaths.assign(uniqueWellPaths.begin(), uniqueWellPaths.end());
|
||||
return wellPaths;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::exportCompletions(const std::vector<RimWellPath*>& wellPaths, const RicExportCompletionDataSettingsUi& exportSettings)
|
||||
{
|
||||
QFile exportFile(exportSettings.fileName());
|
||||
|
||||
if (exportSettings.caseToApply() == nullptr)
|
||||
{
|
||||
RiaLogging::error("Export Completions Data: Cannot export completions data without specified eclipse case");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!exportFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
RiaLogging::error(QString("Export Completions Data: Could not open the file: %1").arg(exportSettings.fileName()));
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream stream(&exportFile);
|
||||
RifEclipseDataTableFormatter formatter(stream);
|
||||
|
||||
for (auto wellPath : wellPaths)
|
||||
{
|
||||
// Generate completion data
|
||||
std::map<IJKCellIndex, RigCompletionData> completionData;
|
||||
|
||||
if (exportSettings.includePerforations)
|
||||
{
|
||||
std::vector<RigCompletionData> perforationCompletionData = generatePerforationsCompdatValues(wellPath, exportSettings);
|
||||
appendCompletionData(&completionData, perforationCompletionData);
|
||||
}
|
||||
if (exportSettings.includeFishbones)
|
||||
{
|
||||
std::vector<RigCompletionData> fishbonesCompletionData = generateFishbonesCompdatValues(wellPath, exportSettings);
|
||||
appendCompletionData(&completionData, fishbonesCompletionData);
|
||||
std::vector<RigCompletionData> fishbonesWellPathCompletionData = generateFishbonesWellPathCompdatValues(wellPath, exportSettings);
|
||||
appendCompletionData(&completionData, fishbonesWellPathCompletionData);
|
||||
}
|
||||
|
||||
// Merge map into a vector of values
|
||||
std::vector<RigCompletionData> completions;
|
||||
for (auto& data : completionData)
|
||||
{
|
||||
completions.push_back(data.second);
|
||||
}
|
||||
// Sort by well name / cell index
|
||||
std::sort(completions.begin(), completions.end());
|
||||
|
||||
// Print completion data
|
||||
generateCompdatTable(formatter, completions);
|
||||
if (exportSettings.includeWpimult)
|
||||
{
|
||||
generateWpimultTable(formatter, completions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateCompdatTable(RifEclipseDataTableFormatter& formatter, const std::vector<RigCompletionData>& completionData)
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Well"),
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K1"),
|
||||
RifEclipseOutputTableColumn("K2"),
|
||||
RifEclipseOutputTableColumn("Status"),
|
||||
RifEclipseOutputTableColumn("SAT"),
|
||||
RifEclipseOutputTableColumn("TR"),
|
||||
RifEclipseOutputTableColumn("DIAM"),
|
||||
RifEclipseOutputTableColumn("KH"),
|
||||
RifEclipseOutputTableColumn("S"),
|
||||
RifEclipseOutputTableColumn("Df"),
|
||||
RifEclipseOutputTableColumn("DIR"),
|
||||
RifEclipseOutputTableColumn("r0")
|
||||
};
|
||||
|
||||
formatter.keyword("COMPDAT");
|
||||
formatter.header(header);
|
||||
|
||||
for (const RigCompletionData& data : completionData)
|
||||
{
|
||||
for (const RigCompletionMetaData& metadata : data.metadata())
|
||||
{
|
||||
formatter.comment(QString("%1 : %2").arg(metadata.name).arg(metadata.comment));
|
||||
}
|
||||
formatter.add(data.wellName());
|
||||
formatter.addZeroBasedCellIndex(data.cellIndex().i).addZeroBasedCellIndex(data.cellIndex().j).addZeroBasedCellIndex(data.cellIndex().k).addZeroBasedCellIndex(data.cellIndex().k);
|
||||
switch (data.connectionState())
|
||||
{
|
||||
case OPEN:
|
||||
formatter.add("OPEN");
|
||||
break;
|
||||
case SHUT:
|
||||
formatter.add("SHUT");
|
||||
break;
|
||||
case AUTO:
|
||||
formatter.add("AUTO");
|
||||
break;
|
||||
}
|
||||
if (RigCompletionData::isDefaultValue(data.saturation())) formatter.add("1*"); else formatter.add(data.saturation());
|
||||
if (RigCompletionData::isDefaultValue(data.transmissibility()))
|
||||
{
|
||||
formatter.add("1*"); // Transmissibility
|
||||
|
||||
if (RigCompletionData::isDefaultValue(data.diameter())) formatter.add("1*"); else formatter.add(data.diameter());
|
||||
if (RigCompletionData::isDefaultValue(data.kh())) formatter.add("1*"); else formatter.add(data.kh());
|
||||
if (RigCompletionData::isDefaultValue(data.skinFactor())) formatter.add("1*"); else formatter.add(data.skinFactor());
|
||||
if (RigCompletionData::isDefaultValue(data.dFactor())) formatter.add("1*"); else formatter.add(data.dFactor());
|
||||
|
||||
switch (data.direction())
|
||||
{
|
||||
case DIR_I:
|
||||
formatter.add("'X'");
|
||||
break;
|
||||
case DIR_J:
|
||||
formatter.add("'Y'");
|
||||
break;
|
||||
case DIR_K:
|
||||
formatter.add("'Z'");
|
||||
break;
|
||||
default:
|
||||
formatter.add("'Z'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
formatter.add(data.transmissibility());
|
||||
}
|
||||
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateWpimultTable(RifEclipseDataTableFormatter& formatter, const std::vector<RigCompletionData>& completionData)
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Well"),
|
||||
RifEclipseOutputTableColumn("Mult"),
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K"),
|
||||
};
|
||||
formatter.keyword("WPIMULT");
|
||||
formatter.header(header);
|
||||
|
||||
for (auto& completion : completionData)
|
||||
{
|
||||
formatter.add(completion.wellName());
|
||||
formatter.add(completion.count());
|
||||
formatter.addZeroBasedCellIndex(completion.cellIndex().i).addZeroBasedCellIndex(completion.cellIndex().j).addZeroBasedCellIndex(completion.cellIndex().k);
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigCompletionData> RicWellPathExportCompletionDataFeature::generateFishbonesCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings)
|
||||
{
|
||||
// Generate data
|
||||
const RigEclipseCaseData* caseData = settings.caseToApply()->eclipseCaseData();
|
||||
std::vector<WellSegmentLocation> locations = findWellSegmentLocations(settings.caseToApply, wellPath);
|
||||
|
||||
// Filter out cells where main bore is present
|
||||
if (settings.removeLateralsInMainBoreCells())
|
||||
{
|
||||
std::vector<size_t> wellPathCells = findIntersectingCells(caseData, wellPath->wellPathGeometry()->m_wellPathPoints);
|
||||
markWellPathCells(wellPathCells, &locations);
|
||||
}
|
||||
|
||||
RigMainGrid* grid = settings.caseToApply->eclipseCaseData()->mainGrid();
|
||||
|
||||
std::vector<RigCompletionData> completionData;
|
||||
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (intersection.mainBoreCell && settings.removeLateralsInMainBoreCells()) continue;
|
||||
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex(intersection.cellIndex, &i, &j, &k);
|
||||
RigCompletionData completion(wellPath->name(), IJKCellIndex(i, j, k));
|
||||
completion.addMetadata(location.fishbonesSubs->name(), QString("Sub: %1 Lateral: %2").arg(location.subIndex).arg(lateral.lateralIndex));
|
||||
double diameter = location.fishbonesSubs->holeDiameter() / 1000;
|
||||
CellDirection direction = wellPathCellDirectionToCellDirection(intersection.direction);
|
||||
completion.setFromFishbone(diameter, direction);
|
||||
completionData.push_back(completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return completionData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigCompletionData> RicWellPathExportCompletionDataFeature::generateFishbonesWellPathCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi & settings)
|
||||
{
|
||||
std::vector<RigCompletionData> completionData;
|
||||
|
||||
double diameter = wellPath->fishbonesCollection()->wellPathCollection()->holeDiameter() / 1000;
|
||||
for (const RimFishboneWellPath* fishbonesPath : wellPath->fishbonesCollection()->wellPathCollection()->wellPaths())
|
||||
{
|
||||
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(), fishbonesPath->coordinates());
|
||||
for (auto& cell : intersectedCells)
|
||||
{
|
||||
size_t i, j, k;
|
||||
settings.caseToApply->eclipseCaseData()->mainGrid()->ijkFromCellIndex(cell.cellIndex, &i, &j, &k);
|
||||
RigCompletionData completion(wellPath->name(), IJKCellIndex(i, j, k));
|
||||
completion.addMetadata(fishbonesPath->name(), "");
|
||||
CellDirection direction = wellPathCellDirectionToCellDirection(cell.direction);
|
||||
completion.setFromFishbone(diameter, direction);
|
||||
completionData.push_back(completion);
|
||||
}
|
||||
}
|
||||
|
||||
return completionData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigCompletionData> RicWellPathExportCompletionDataFeature::generatePerforationsCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings)
|
||||
{
|
||||
std::vector<RigCompletionData> completionData;
|
||||
|
||||
for (const RimPerforationInterval* interval : wellPath->perforationIntervalCollection()->perforations())
|
||||
{
|
||||
if (!interval->isActiveOnDate(settings.caseToApply->timeStepDates()[settings.timeStep])) continue;
|
||||
|
||||
std::vector<cvf::Vec3d> perforationPoints = wellPath->wellPathGeometry()->clippedPointSubset(interval->startMD(), interval->endMD());
|
||||
std::vector<WellPathCellIntersectionInfo> intersectedCells = RigWellPathIntersectionTools::findCellsIntersectedByPath(settings.caseToApply->eclipseCaseData(), perforationPoints);
|
||||
for (auto& cell : intersectedCells)
|
||||
{
|
||||
size_t i, j, k;
|
||||
settings.caseToApply->eclipseCaseData()->mainGrid()->ijkFromCellIndex(cell.cellIndex, &i, &j, &k);
|
||||
RigCompletionData completion(wellPath->name(), IJKCellIndex(i, j, k));
|
||||
completion.addMetadata("Perforation", QString("StartMD: %1 - EndMD: %2").arg(interval->startMD()).arg(interval->endMD()));
|
||||
double diameter = interval->diameter();
|
||||
CellDirection direction = wellPathCellDirectionToCellDirection(cell.direction);
|
||||
completion.setFromPerforation(diameter, direction);
|
||||
completionData.push_back(completion);
|
||||
}
|
||||
}
|
||||
|
||||
return completionData;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RicWellPathExportCompletionDataFeature::findIntersectingCells(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = caseData->mainGrid()->nodes();
|
||||
std::set<size_t> cells;
|
||||
|
||||
std::vector<HexIntersectionInfo> intersections = RigWellPathIntersectionTools::getIntersectedCells(caseData->mainGrid(), coords);
|
||||
for (auto intersection : intersections)
|
||||
{
|
||||
cells.insert(intersection.m_hexIndex);
|
||||
}
|
||||
|
||||
// Ensure only unique cells are included
|
||||
std::vector<size_t> cellsVector;
|
||||
cellsVector.assign(cells.begin(), cells.end());
|
||||
// Sort cells
|
||||
std::sort(cellsVector.begin(), cellsVector.end());
|
||||
return cellsVector;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::markWellPathCells(const std::vector<size_t>& wellPathCells, std::vector<WellSegmentLocation>* locations)
|
||||
{
|
||||
std::set<size_t> wellPathCellSet(wellPathCells.begin(), wellPathCells.end());
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
for (WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
for (WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (wellPathCellSet.find(intersection.cellIndex) != wellPathCellSet.end())
|
||||
{
|
||||
intersection.mainBoreCell = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<size_t, double> RicWellPathExportCompletionDataFeature::computeLateralsPerCell(const std::vector<WellSegmentLocation>& segmentLocations, bool removeMainBoreCells)
|
||||
{
|
||||
std::map<size_t, double> lateralsPerCell;
|
||||
for (const WellSegmentLocation& location : segmentLocations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (removeMainBoreCells && intersection.mainBoreCell) continue;
|
||||
|
||||
auto match = lateralsPerCell.find(intersection.cellIndex);
|
||||
if (match == lateralsPerCell.end())
|
||||
{
|
||||
lateralsPerCell[intersection.cellIndex] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lateralsPerCell[intersection.cellIndex] = match->second + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lateralsPerCell;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::wellSegmentLocationOrdering(const WellSegmentLocation& first, const WellSegmentLocation& second)
|
||||
{
|
||||
return first.measuredDepth < second.measuredDepth;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::isPointBetween(const cvf::Vec3d& pointA, const cvf::Vec3d& pointB, const cvf::Vec3d& needle)
|
||||
{
|
||||
cvf::Plane plane;
|
||||
plane.setFromPointAndNormal(needle, pointB - pointA);
|
||||
return plane.side(pointA) != plane.side(pointB);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<WellSegmentLocation> RicWellPathExportCompletionDataFeature::findWellSegmentLocations(const RimEclipseCase* caseToApply, const RimWellPath* wellPath)
|
||||
{
|
||||
std::vector<RimFishbonesMultipleSubs*> fishbonesSubs;
|
||||
for (RimFishbonesMultipleSubs* subs : wellPath->fishbonesCollection()->fishbonesSubs())
|
||||
{
|
||||
fishbonesSubs.push_back(subs);
|
||||
}
|
||||
return findWellSegmentLocations(caseToApply, wellPath, fishbonesSubs);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<WellSegmentLocation> RicWellPathExportCompletionDataFeature::findWellSegmentLocations(const RimEclipseCase* caseToApply, const RimWellPath* wellPath, const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs)
|
||||
{
|
||||
std::vector<WellSegmentLocation> wellSegmentLocations;
|
||||
for (RimFishbonesMultipleSubs* subs : fishbonesSubs)
|
||||
{
|
||||
for (size_t subIndex = 0; subIndex < subs->locationOfSubs().size(); ++subIndex)
|
||||
{
|
||||
double measuredDepth = subs->locationOfSubs()[subIndex];
|
||||
cvf::Vec3d position = wellPath->wellPathGeometry()->interpolatedPointAlongWellPath(measuredDepth);
|
||||
WellSegmentLocation location = WellSegmentLocation(subs, measuredDepth, -position.z(), subIndex);
|
||||
for (size_t lateralIndex = 0; lateralIndex < subs->lateralLengths().size(); ++lateralIndex)
|
||||
{
|
||||
location.laterals.push_back(WellSegmentLateral(lateralIndex));
|
||||
}
|
||||
wellSegmentLocations.push_back(location);
|
||||
}
|
||||
}
|
||||
std::sort(wellSegmentLocations.begin(), wellSegmentLocations.end(), wellSegmentLocationOrdering);
|
||||
|
||||
assignBranchAndSegmentNumbers(caseToApply, &wellSegmentLocations);
|
||||
|
||||
return wellSegmentLocations;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::calculateLateralIntersections(const RimEclipseCase* caseToApply, WellSegmentLocation* location, int* branchNum, int* segmentNum)
|
||||
{
|
||||
for (WellSegmentLateral& lateral : location->laterals)
|
||||
{
|
||||
lateral.branchNumber = ++(*branchNum);
|
||||
std::vector<cvf::Vec3d> coords = location->fishbonesSubs->coordsForLateral(location->subIndex, lateral.lateralIndex);
|
||||
std::vector<WellPathCellIntersectionInfo> intersections = RigWellPathIntersectionTools::findCellsIntersectedByPath(caseToApply->eclipseCaseData(), coords);
|
||||
|
||||
auto intersection = intersections.cbegin();
|
||||
double length = 0;
|
||||
double depth = 0;
|
||||
cvf::Vec3d startPoint = coords[0];
|
||||
int attachedSegmentNumber = location->segmentNumber;
|
||||
|
||||
for (size_t i = 1; i < coords.size() && intersection != intersections.cend(); ++i)
|
||||
{
|
||||
if (isPointBetween(startPoint, coords[i], intersection->endPoint))
|
||||
{
|
||||
length += (intersection->endPoint - startPoint).length();
|
||||
depth += intersection->endPoint.z() - startPoint.z();
|
||||
|
||||
WellSegmentLateralIntersection lateralIntersection(++(*segmentNum), attachedSegmentNumber, intersection->cellIndex, length, depth);
|
||||
lateralIntersection.direction = intersection->direction;
|
||||
lateral.intersections.push_back(lateralIntersection);
|
||||
|
||||
length = 0;
|
||||
depth = 0;
|
||||
startPoint = intersection->startPoint;
|
||||
attachedSegmentNumber = *segmentNum;
|
||||
++intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += (coords[i] - startPoint).length();
|
||||
depth += coords[i].z() - startPoint.z();
|
||||
startPoint = coords[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::assignBranchAndSegmentNumbers(const RimEclipseCase* caseToApply, std::vector<WellSegmentLocation>* locations)
|
||||
{
|
||||
int segmentNumber = 1;
|
||||
int branchNumber = 1;
|
||||
// First loop over the locations so that each segment on the main stem is an incremental number
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
location.segmentNumber = ++segmentNumber;
|
||||
}
|
||||
// Then assign branch and segment numbers to each lateral parts
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
calculateLateralIntersections(caseToApply, &location, &branchNumber, &segmentNumber);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::appendCompletionData(std::map<IJKCellIndex, RigCompletionData>* completionData, const std::vector<RigCompletionData>& data)
|
||||
{
|
||||
for (auto& completion : data)
|
||||
{
|
||||
auto it = completionData->find(completion.cellIndex());
|
||||
if (it != completionData->end())
|
||||
{
|
||||
it->second = RigCompletionData::combine(it->second, completion);
|
||||
}
|
||||
else
|
||||
{
|
||||
completionData->insert(std::pair<IJKCellIndex, RigCompletionData>(completion.cellIndex(), completion));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
CellDirection RicWellPathExportCompletionDataFeature::wellPathCellDirectionToCellDirection(WellPathCellDirection direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case POS_I:
|
||||
case NEG_I:
|
||||
return CellDirection::DIR_I;
|
||||
case POS_J:
|
||||
case NEG_J:
|
||||
return CellDirection::DIR_J;
|
||||
case POS_K:
|
||||
case NEG_K:
|
||||
return CellDirection::DIR_K;
|
||||
default:
|
||||
return CellDirection::DIR_UNDEF;
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RifEclipseOutputTableFormatter.h"
|
||||
#include "RifEclipseDataTableFormatter.h"
|
||||
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
#include "RigWellPathIntersectionTools.h"
|
||||
#include "RigCompletionData.h"
|
||||
|
||||
#include "RimExportCompletionDataSettings.h"
|
||||
#include "RicExportCompletionDataSettingsUi.h"
|
||||
|
||||
#include "cafCmdFeature.h"
|
||||
|
||||
@@ -36,18 +38,6 @@ class RigMainGrid;
|
||||
class RigCell;
|
||||
class RimFishbonesMultipleSubs;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum WellSegmentCellDirection {
|
||||
POS_I,
|
||||
NEG_I,
|
||||
POS_J,
|
||||
NEG_J,
|
||||
POS_K,
|
||||
NEG_K
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
@@ -59,7 +49,6 @@ struct WellSegmentLateralIntersection {
|
||||
length(length),
|
||||
depth(depth),
|
||||
direction(POS_I),
|
||||
directionLength(-1.0),
|
||||
mainBoreCell(false)
|
||||
{}
|
||||
|
||||
@@ -69,8 +58,7 @@ struct WellSegmentLateralIntersection {
|
||||
bool mainBoreCell;
|
||||
double length;
|
||||
double depth;
|
||||
WellSegmentCellDirection direction;
|
||||
double directionLength;
|
||||
WellPathCellDirection direction;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
@@ -133,34 +121,31 @@ protected:
|
||||
virtual void onActionTriggered(bool isChecked) override;
|
||||
virtual void setupActionLook(QAction* actionToSetup) override;
|
||||
|
||||
private:
|
||||
static void exportToFolder(RimWellPath* wellPath, const RimExportCompletionDataSettings& exportSettings);
|
||||
std::vector<RimWellPath*> selectedWellPaths();
|
||||
|
||||
static void generateCompdatTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations);
|
||||
static void generateWpimultTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::map<size_t, double>& lateralsPerCell);
|
||||
static void generateWelsegsTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations);
|
||||
static void generateCompsegsTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations);
|
||||
public:
|
||||
static std::vector<WellSegmentLocation> findWellSegmentLocations(const RimEclipseCase* caseToApply, const RimWellPath* wellPath);
|
||||
static std::vector<WellSegmentLocation> findWellSegmentLocations(const RimEclipseCase* caseToApply, const RimWellPath* wellPath, const std::vector<RimFishbonesMultipleSubs*>& fishbonesSubs);
|
||||
|
||||
private:
|
||||
static void exportCompletions(const std::vector<RimWellPath*>& wellPaths, const RicExportCompletionDataSettingsUi& exportSettings);
|
||||
|
||||
static void generateCompdatTable(RifEclipseDataTableFormatter& formatter, const std::vector<RigCompletionData>& completionData);
|
||||
static void generateWpimultTable(RifEclipseDataTableFormatter& formatter, const std::vector<RigCompletionData>& completionData);
|
||||
|
||||
static std::vector<RigCompletionData> generateFishbonesCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings);
|
||||
static std::vector<RigCompletionData> generateFishbonesWellPathCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings);
|
||||
static std::vector<RigCompletionData> generatePerforationsCompdatValues(const RimWellPath* wellPath, const RicExportCompletionDataSettingsUi& settings);
|
||||
|
||||
static std::map<size_t, double> computeLateralsPerCell(const std::vector<WellSegmentLocation>& segmentLocations, bool removeMainBoreCells);
|
||||
|
||||
static std::vector<size_t> findCloseCells(const RigEclipseCaseData* caseData, const cvf::BoundingBox& bb);
|
||||
static size_t findCellFromCoords(const RigEclipseCaseData* caseData, const cvf::Vec3d& coords);
|
||||
|
||||
static std::vector<EclipseCellIndexRange> getCellIndexRange(const RigMainGrid* grid, const std::vector<size_t>& cellIndices);
|
||||
static bool cellOrdering(const EclipseCellIndex& cell1, const EclipseCellIndex& cell2);
|
||||
static std::vector<size_t> findIntersectingCells(const RigEclipseCaseData* grid, const std::vector<cvf::Vec3d>& coords);
|
||||
static void setHexCorners(const RigCell& cell, const std::vector<cvf::Vec3d>& nodeCoords, cvf::Vec3d* hexCorners);
|
||||
static void markWellPathCells(const std::vector<size_t>& wellPathCells, std::vector<WellSegmentLocation>* locations);
|
||||
static bool wellSegmentLocationOrdering(const WellSegmentLocation& first, const WellSegmentLocation& second);
|
||||
static std::vector<HexIntersectionInfo> findIntersections(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords);
|
||||
static bool isPointBetween(const cvf::Vec3d& pointA, const cvf::Vec3d& pointB, const cvf::Vec3d& needle);
|
||||
static void filterIntersections(std::vector<HexIntersectionInfo>* intersections);
|
||||
static std::vector<WellSegmentLocation> findWellSegmentLocations(const RimEclipseCase* caseToApply, RimWellPath* wellPath);
|
||||
static void calculateLateralIntersections(const RimEclipseCase* caseToApply, WellSegmentLocation* location, int* branchNum, int* segmentNum);
|
||||
static void assignBranchAndSegmentNumbers(const RimEclipseCase* caseToApply, std::vector<WellSegmentLocation>* locations);
|
||||
|
||||
// Calculate direction
|
||||
static void calculateCellMainAxisDirections(const RigMainGrid* grid, size_t cellIndex, cvf::Vec3d* iAxisDirection, cvf::Vec3d* jAxisDirection, cvf::Vec3d* kAxisDirection);
|
||||
static cvf::Vec3d calculateCellMainAxisDirection(const cvf::Vec3d* hexCorners, cvf::StructGridInterface::FaceType startFace, cvf::StructGridInterface::FaceType endFace);
|
||||
static std::pair<WellSegmentCellDirection, double> calculateDirectionAndDistanceInCell(const RigMainGrid* grid, size_t cellIndex, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint);
|
||||
static void appendCompletionData(std::map<IJKCellIndex, RigCompletionData>* completionData, const std::vector<RigCompletionData>& data);
|
||||
|
||||
static CellDirection wellPathCellDirectionToCellDirection(WellPathCellDirection direction);
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicWellPathImportPerforationIntervalsFeature.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCollection.h"
|
||||
#include "RimPerforationInterval.h"
|
||||
#include "RimPerforationCollection.h"
|
||||
|
||||
#include "RifPerforationIntervalReader.h"
|
||||
|
||||
#include "RiuMainWindow.h"
|
||||
|
||||
#include "cafSelectionManager.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
|
||||
CAF_CMD_SOURCE_INIT(RicWellPathImportPerforationIntervalsFeature, "RicWellPathImportPerforationIntervalsFeature");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathImportPerforationIntervalsFeature::isCommandEnabled()
|
||||
{
|
||||
if (RicWellPathImportPerforationIntervalsFeature::selectedWellPath() != nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathImportPerforationIntervalsFeature::onActionTriggered(bool isChecked)
|
||||
{
|
||||
RimWellPathCollection* wellPathCollection = RicWellPathImportPerforationIntervalsFeature::selectedWellPath();
|
||||
CVF_ASSERT(wellPathCollection);
|
||||
|
||||
// Open dialog box to select well path files
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
QString defaultDir = app->lastUsedDialogDirectory("WELLPATH_DIR");
|
||||
QStringList wellPathFilePaths = QFileDialog::getOpenFileNames(RiuMainWindow::instance(), "Import Well Path Perforation Intervals", defaultDir, "Well Path Perforation Intervals (*.ev);;All Files (*.*)");
|
||||
|
||||
if (wellPathFilePaths.size() < 1) return;
|
||||
|
||||
// Remember the path to next time
|
||||
app->setLastUsedDialogDirectory("WELLPATH_DIR", QFileInfo(wellPathFilePaths.last()).absolutePath());
|
||||
|
||||
std::map<QString, std::vector<RifPerforationInterval> > perforationIntervals = RifPerforationIntervalReader::readPerforationIntervals(wellPathFilePaths);
|
||||
|
||||
for (auto& entry : perforationIntervals)
|
||||
{
|
||||
RimWellPath* wellPath = wellPathCollection->wellPathByName(entry.first);
|
||||
if (wellPath == nullptr)
|
||||
{
|
||||
RiaLogging::warning(QString("Import Well Path Perforation Intervals : Imported file contains unknown well path '%1'.").arg(entry.first));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& interval : entry.second)
|
||||
{
|
||||
RimPerforationInterval* perforationInterval = new RimPerforationInterval;
|
||||
perforationInterval->setStartAndEndMD(interval.startMD, interval.endMD);
|
||||
perforationInterval->setDiameter(interval.diameter);
|
||||
perforationInterval->setSkinFactor(interval.skinFactor);
|
||||
if (interval.startOfHistory)
|
||||
{
|
||||
perforationInterval->setStartOfHistory();
|
||||
}
|
||||
else
|
||||
{
|
||||
perforationInterval->setDate(interval.date);
|
||||
}
|
||||
wellPath->perforationIntervalCollection()->appendPerforation(perforationInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (app->project())
|
||||
{
|
||||
app->project()->createDisplayModelAndRedrawAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathImportPerforationIntervalsFeature::setupActionLook(QAction* actionToSetup)
|
||||
{
|
||||
actionToSetup->setText("Import Perforation Intervals");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellPathCollection* RicWellPathImportPerforationIntervalsFeature::selectedWellPath()
|
||||
{
|
||||
std::vector<RimWellPathCollection*> objects;
|
||||
caf::SelectionManager::instance()->objectsByType(&objects);
|
||||
|
||||
if (objects.size() == 1)
|
||||
{
|
||||
return objects[0];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafCmdFeature.h"
|
||||
|
||||
class RimWellPathCollection;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RicWellPathImportPerforationIntervalsFeature : public caf::CmdFeature
|
||||
{
|
||||
CAF_CMD_HEADER_INIT;
|
||||
protected:
|
||||
|
||||
// Overrides
|
||||
virtual bool isCommandEnabled() override;
|
||||
virtual void onActionTriggered( bool isChecked ) override;
|
||||
virtual void setupActionLook( QAction* actionToSetup ) override;
|
||||
|
||||
private:
|
||||
static RimWellPathCollection* selectedWellPath();
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
# Use this workaround until we're on 2.8.3 on all platforms and can use CMAKE_CURRENT_LIST_DIR directly
|
||||
if (${CMAKE_VERSION} VERSION_GREATER "2.8.2")
|
||||
set(CEE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_DIR}/)
|
||||
endif()
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesLateralsFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsAtMeasuredDepthFeature.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicExportFishbonesLateralsFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicNewFishbonesSubsAtMeasuredDepthFeature.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
${SOURCE_GROUP_HEADER_FILES}
|
||||
)
|
||||
|
||||
list(APPEND CODE_SOURCE_FILES
|
||||
${SOURCE_GROUP_SOURCE_FILES}
|
||||
)
|
||||
|
||||
source_group( "CommandFeature\\Fishbones" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CEE_CURRENT_LIST_DIR}CMakeLists_files.cmake )
|
||||
@@ -22,8 +22,10 @@
|
||||
#include "RiaPreferences.h"
|
||||
|
||||
#include "RifEclipseSummaryAddress.h"
|
||||
#include "RifReaderEclipseSummary.h"
|
||||
|
||||
#include "RigSingleWellResultsData.h"
|
||||
#include "RigSummaryCaseData.h"
|
||||
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimEclipseWell.h"
|
||||
@@ -33,7 +35,7 @@
|
||||
#include "RimProject.h"
|
||||
#include "RimSummaryCaseCollection.h"
|
||||
#include "RimSummaryCurve.h"
|
||||
#include "RimSummaryCurveFilter.h"
|
||||
#include "RimSummaryCurveAppearanceCalculator.h"
|
||||
#include "RimSummaryPlot.h"
|
||||
#include "RimSummaryPlotCollection.h"
|
||||
#include "RimView.h"
|
||||
@@ -94,67 +96,92 @@ void RicPlotProductionRateFeature::onActionTriggered(bool isChecked)
|
||||
RimGridSummaryCase* gridSummaryCase = RicPlotProductionRateFeature::gridSummaryCaseForWell(well);
|
||||
if (!gridSummaryCase) continue;
|
||||
|
||||
QString curveFilterText = "W*PR:";
|
||||
QString description = "Well Production Rates : ";
|
||||
|
||||
RigSingleWellResultsData* wRes = well->wellResults();
|
||||
if (wRes)
|
||||
if (isInjector(well))
|
||||
{
|
||||
RimView* rimView = nullptr;
|
||||
well->firstAncestorOrThisOfTypeAsserted(rimView);
|
||||
|
||||
int currentTimeStep = rimView->currentTimeStep();
|
||||
|
||||
if (wRes->hasWellResult(currentTimeStep))
|
||||
{
|
||||
const RigWellResultFrame& wrf = wRes->wellResultFrame(currentTimeStep);
|
||||
|
||||
if ( wrf.m_productionType == RigWellResultFrame::OIL_INJECTOR
|
||||
|| wrf.m_productionType == RigWellResultFrame::GAS_INJECTOR
|
||||
|| wrf.m_productionType == RigWellResultFrame::WATER_INJECTOR)
|
||||
{
|
||||
curveFilterText = "W*IR:";
|
||||
description = "Well Injection Rates : ";
|
||||
}
|
||||
}
|
||||
description = "Well Injection Rates : ";
|
||||
}
|
||||
|
||||
curveFilterText += well->name();
|
||||
description += well->name();
|
||||
|
||||
RimSummaryPlot* plot = new RimSummaryPlot();
|
||||
summaryPlotColl->summaryPlots().push_back(plot);
|
||||
|
||||
description += well->name();
|
||||
plot->setDescription(description);
|
||||
|
||||
if (isInjector(well))
|
||||
{
|
||||
RimSummaryCurveFilter* newCurveFilter = new RimSummaryCurveFilter();
|
||||
plot->addCurveFilter(newCurveFilter);
|
||||
// Left Axis
|
||||
|
||||
newCurveFilter->createCurves(gridSummaryCase, curveFilterText);
|
||||
RimDefines::PlotAxis plotAxis = RimDefines::PLOT_AXIS_LEFT;
|
||||
|
||||
{
|
||||
// Note : The parameter "WOIR" is probably never-existing, but we check for existence before creating curve
|
||||
// Oil
|
||||
QString parameterName = "WOIR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledGreenColor(0));
|
||||
}
|
||||
|
||||
{
|
||||
// Water
|
||||
QString parameterName = "WWIR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledBlueColor(0));
|
||||
}
|
||||
|
||||
{
|
||||
// Gas
|
||||
QString parameterName = "WGIR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledRedColor(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Left Axis
|
||||
|
||||
RimDefines::PlotAxis plotAxis = RimDefines::PLOT_AXIS_LEFT;
|
||||
|
||||
{
|
||||
// Oil
|
||||
QString parameterName = "WOPR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledGreenColor(0));
|
||||
}
|
||||
|
||||
{
|
||||
// Water
|
||||
QString parameterName = "WWPR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledBlueColor(0));
|
||||
}
|
||||
|
||||
{
|
||||
// Gas
|
||||
QString parameterName = "WGPR";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledRedColor(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Right Axis
|
||||
|
||||
{
|
||||
RimSummaryCurve* newCurve = new RimSummaryCurve();
|
||||
plot->addCurve(newCurve);
|
||||
RimDefines::PlotAxis plotAxis = RimDefines::PLOT_AXIS_RIGHT;
|
||||
|
||||
newCurve->setSummaryCase(gridSummaryCase);
|
||||
{
|
||||
QString parameterName = "WTHP";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledNoneRGBBrColor(0));
|
||||
}
|
||||
|
||||
RifEclipseSummaryAddress addr( RifEclipseSummaryAddress::SUMMARY_WELL,
|
||||
"WBHP",
|
||||
-1,
|
||||
-1,
|
||||
"",
|
||||
well->name().toStdString(),
|
||||
-1,
|
||||
"",
|
||||
-1,
|
||||
-1,
|
||||
-1);
|
||||
|
||||
newCurve->setSummaryAddress(addr);
|
||||
newCurve->setYAxis(RimDefines::PlotAxis::PLOT_AXIS_RIGHT);
|
||||
{
|
||||
QString parameterName = "WBHP";
|
||||
RicPlotProductionRateFeature::addSummaryCurve(plot, well, gridSummaryCase, parameterName,
|
||||
plotAxis, RimSummaryCurveAppearanceCalculator::cycledNoneRGBBrColor(1));
|
||||
}
|
||||
}
|
||||
|
||||
summaryPlotColl->updateConnectedEditors();
|
||||
@@ -210,3 +237,71 @@ RimGridSummaryCase* RicPlotProductionRateFeature::gridSummaryCaseForWell(RimEcli
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicPlotProductionRateFeature::isInjector(RimEclipseWell* well)
|
||||
{
|
||||
RigSingleWellResultsData* wRes = well->wellResults();
|
||||
if (wRes)
|
||||
{
|
||||
RimView* rimView = nullptr;
|
||||
well->firstAncestorOrThisOfTypeAsserted(rimView);
|
||||
|
||||
int currentTimeStep = rimView->currentTimeStep();
|
||||
|
||||
if (wRes->hasWellResult(currentTimeStep))
|
||||
{
|
||||
const RigWellResultFrame& wrf = wRes->wellResultFrame(currentTimeStep);
|
||||
|
||||
if ( wrf.m_productionType == RigWellResultFrame::OIL_INJECTOR
|
||||
|| wrf.m_productionType == RigWellResultFrame::GAS_INJECTOR
|
||||
|| wrf.m_productionType == RigWellResultFrame::WATER_INJECTOR)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSummaryCurve* RicPlotProductionRateFeature::addSummaryCurve( RimSummaryPlot* plot, const RimEclipseWell* well,
|
||||
RimGridSummaryCase* gridSummaryCase, const QString& vectorName,
|
||||
RimDefines::PlotAxis plotAxis, const cvf::Color3f& color)
|
||||
{
|
||||
CVF_ASSERT(plot);
|
||||
CVF_ASSERT(gridSummaryCase);
|
||||
CVF_ASSERT(well);
|
||||
|
||||
RifEclipseSummaryAddress addr(RifEclipseSummaryAddress::SUMMARY_WELL,
|
||||
vectorName.toStdString(),
|
||||
-1,
|
||||
-1,
|
||||
"",
|
||||
well->name().toStdString(),
|
||||
-1,
|
||||
"",
|
||||
-1,
|
||||
-1,
|
||||
-1);
|
||||
|
||||
if (!gridSummaryCase->caseData()->summaryReader()->hasAddress(addr))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RimSummaryCurve* newCurve = new RimSummaryCurve();
|
||||
plot->addCurve(newCurve);
|
||||
|
||||
newCurve->setSummaryCase(gridSummaryCase);
|
||||
newCurve->setSummaryAddress(addr);
|
||||
newCurve->setColor(color);
|
||||
newCurve->setYAxis(plotAxis);
|
||||
|
||||
return newCurve;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,12 @@
|
||||
#include "cafCmdFeature.h"
|
||||
|
||||
#include "RimFlowDiagSolution.h"
|
||||
#include "RimDefines.h"
|
||||
|
||||
class RimGridSummaryCase;
|
||||
class RimEclipseWell;
|
||||
class RimSummaryPlot;
|
||||
class RimSummaryCurve;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
@@ -39,7 +42,11 @@ protected:
|
||||
virtual void setupActionLook( QAction* actionToSetup ) override;
|
||||
|
||||
private:
|
||||
static RimGridSummaryCase* gridSummaryCaseForWell(RimEclipseWell* well);
|
||||
static RimGridSummaryCase* gridSummaryCaseForWell(RimEclipseWell* well);
|
||||
static bool isInjector(RimEclipseWell* well);
|
||||
static RimSummaryCurve* addSummaryCurve(RimSummaryPlot* plot, const RimEclipseWell* well,
|
||||
RimGridSummaryCase* gridSummaryCase, const QString& vectorName,
|
||||
RimDefines::PlotAxis plotAxis, const cvf::Color3f& color);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "RimEclipseResultCase.h"
|
||||
#include "RimEclipseView.h"
|
||||
#include "RimFlowCharacteristicsPlot.h"
|
||||
#include "RigFlowDiagResults.h"
|
||||
#include "RimFlowDiagSolution.h"
|
||||
#include "RimFlowPlotCollection.h"
|
||||
#include "RimMainPlotCollection.h"
|
||||
#include "RimProject.h"
|
||||
@@ -71,6 +73,16 @@ void RicShowFlowCharacteristicsPlotFeature::onActionTriggered(bool isChecked)
|
||||
|
||||
if (eclCase && eclCase->defaultFlowDiagSolution())
|
||||
{
|
||||
// Make sure flow results for the the active timestep is calculated, to avoid an empty plot
|
||||
{
|
||||
RimView * activeView = RiaApplication::instance()->activeReservoirView();
|
||||
if (activeView && eclCase->defaultFlowDiagSolution()->flowDiagResults())
|
||||
{
|
||||
// Trigger calculation
|
||||
eclCase->defaultFlowDiagSolution()->flowDiagResults()->maxAbsPairFlux(activeView->currentTimeStep());
|
||||
}
|
||||
}
|
||||
|
||||
if (RiaApplication::instance()->project())
|
||||
{
|
||||
RimFlowPlotCollection* flowPlotColl = RiaApplication::instance()->project()->mainPlotCollection->flowPlotCollection();
|
||||
|
||||
@@ -6,8 +6,6 @@ endif()
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathDeleteFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathExportCompletionDataFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportCompletionsFileFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathsImportFileFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathsImportSsihubFeature.h
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathViewerEventHandler.h
|
||||
@@ -15,8 +13,6 @@ ${CEE_CURRENT_LIST_DIR}RicWellPathViewerEventHandler.h
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathDeleteFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathExportCompletionDataFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathImportCompletionsFileFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathsImportFileFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathsImportSsihubFeature.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RicWellPathViewerEventHandler.cpp
|
||||
|
||||
@@ -1,898 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RicWellPathExportCompletionDataFeature.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimFishbonesMultipleSubs.h"
|
||||
#include "RimFishbonesCollection.h"
|
||||
#include "RimExportCompletionDataSettings.h"
|
||||
|
||||
#include "RiuMainWindow.h"
|
||||
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigWellPath.h"
|
||||
|
||||
#include "cafSelectionManager.h"
|
||||
#include "cafPdmUiPropertyViewDialog.h"
|
||||
|
||||
#include "cvfPlane.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
CAF_CMD_SOURCE_INIT(RicWellPathExportCompletionDataFeature, "RicWellPathExportCompletionDataFeature");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::isCommandEnabled()
|
||||
{
|
||||
std::vector<RimWellPath*> objects;
|
||||
caf::SelectionManager::instance()->objectsByType(&objects);
|
||||
|
||||
if (objects.size() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::onActionTriggered(bool isChecked)
|
||||
{
|
||||
std::vector<RimWellPath*> objects;
|
||||
caf::SelectionManager::instance()->objectsByType(&objects);
|
||||
|
||||
CVF_ASSERT(objects.size() == 1);
|
||||
|
||||
RiaApplication* app = RiaApplication::instance();
|
||||
|
||||
QString projectFolder = app->currentProjectPath();
|
||||
QString defaultDir = RiaApplication::instance()->lastUsedDialogDirectoryWithFallback("COMPLETIONS", projectFolder);
|
||||
|
||||
RimExportCompletionDataSettings exportSettings;
|
||||
std::vector<RimCase*> cases;
|
||||
app->project()->allCases(cases);
|
||||
for (auto c : cases)
|
||||
{
|
||||
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>(c);
|
||||
if (eclipseCase != nullptr)
|
||||
{
|
||||
exportSettings.caseToApply = eclipseCase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exportSettings.fileName = QDir(defaultDir).filePath("Completions");
|
||||
|
||||
caf::PdmUiPropertyViewDialog propertyDialog(RiuMainWindow::instance(), &exportSettings, "Export Completion Data", "");
|
||||
if (propertyDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
RiaApplication::instance()->setLastUsedDialogDirectory("COMPLETIONS", QFileInfo(exportSettings.fileName).absolutePath());
|
||||
|
||||
exportToFolder(objects[0], exportSettings);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::setupActionLook(QAction* actionToSetup)
|
||||
{
|
||||
actionToSetup->setText("Export Completion Data");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::exportToFolder(RimWellPath* wellPath, const RimExportCompletionDataSettings& exportSettings)
|
||||
{
|
||||
QFile exportFile(exportSettings.fileName());
|
||||
|
||||
if (exportSettings.caseToApply() == nullptr)
|
||||
{
|
||||
RiaLogging::error("Export Completions Data: Cannot export completions data without specified eclipse case");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!exportFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
RiaLogging::error(QString("Export Completions Data: Could not open the file: %1").arg(exportSettings.fileName()));
|
||||
return;
|
||||
}
|
||||
|
||||
RiaLogging::debug(QString("Exporting completion data for well path %1 to %2 [WPIMULT: %3, REM BORE CELLS: %4]").arg(wellPath->name).arg(exportSettings.fileName()).arg(exportSettings.includeWpimult()).arg(exportSettings.removeLateralsInMainBoreCells()));
|
||||
|
||||
|
||||
// Generate data
|
||||
const RigEclipseCaseData* caseData = exportSettings.caseToApply()->eclipseCaseData();
|
||||
std::vector<WellSegmentLocation> wellSegmentLocations = findWellSegmentLocations(exportSettings.caseToApply, wellPath);
|
||||
|
||||
// Filter out cells where main bore is present
|
||||
if (exportSettings.removeLateralsInMainBoreCells())
|
||||
{
|
||||
std::vector<size_t> wellPathCells = findIntersectingCells(caseData, wellPath->wellPathGeometry()->m_wellPathPoints);
|
||||
markWellPathCells(wellPathCells, &wellSegmentLocations);
|
||||
}
|
||||
|
||||
// Print data
|
||||
QTextStream stream(&exportFile);
|
||||
RifEclipseOutputTableFormatter formatter(stream);
|
||||
|
||||
generateCompdatTable(formatter, wellPath, exportSettings, wellSegmentLocations);
|
||||
|
||||
if (exportSettings.includeWpimult())
|
||||
{
|
||||
std::map<size_t, double> lateralsPerCell = computeLateralsPerCell(wellSegmentLocations, exportSettings.removeLateralsInMainBoreCells());
|
||||
generateWpimultTable(formatter, wellPath, exportSettings, lateralsPerCell);
|
||||
}
|
||||
|
||||
generateWelsegsTable(formatter, wellPath, exportSettings, wellSegmentLocations);
|
||||
generateCompsegsTable(formatter, wellPath, exportSettings, wellSegmentLocations);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateCompdatTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations)
|
||||
{
|
||||
RigMainGrid* grid = settings.caseToApply->eclipseCaseData()->mainGrid();
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Well"),
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K1"),
|
||||
RifEclipseOutputTableColumn("K2"),
|
||||
RifEclipseOutputTableColumn("Status"),
|
||||
RifEclipseOutputTableColumn("SAT"),
|
||||
RifEclipseOutputTableColumn("TR"),
|
||||
RifEclipseOutputTableColumn("DIAM"),
|
||||
RifEclipseOutputTableColumn("KH"),
|
||||
RifEclipseOutputTableColumn("S"),
|
||||
RifEclipseOutputTableColumn("Df"),
|
||||
RifEclipseOutputTableColumn("DIR"),
|
||||
RifEclipseOutputTableColumn("r0")
|
||||
};
|
||||
|
||||
formatter.keyword("COMPDAT");
|
||||
formatter.header(header);
|
||||
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
formatter.comment(QString("Fishbone %1 - Sub: %2 - Lateral: %3").arg(location.fishbonesSubs->name()).arg(location.subIndex).arg(lateral.lateralIndex));
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (settings.removeLateralsInMainBoreCells && intersection.mainBoreCell) continue;
|
||||
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex(intersection.cellIndex, &i, &j, &k);
|
||||
formatter.add(wellPath->name());
|
||||
formatter.addZeroBasedCellIndex(i).addZeroBasedCellIndex(j).addZeroBasedCellIndex(k).addZeroBasedCellIndex(k);
|
||||
formatter.add("'OPEN'").add("1*").add("1*");
|
||||
formatter.add(location.fishbonesSubs->holeRadius() / 1000);
|
||||
formatter.add("1*").add("1*").add("1*");
|
||||
switch (intersection.direction)
|
||||
{
|
||||
case POS_I:
|
||||
case NEG_I:
|
||||
formatter.add("'X'");
|
||||
break;
|
||||
case POS_J:
|
||||
case NEG_J:
|
||||
formatter.add("'Y'");
|
||||
break;
|
||||
case POS_K:
|
||||
case NEG_K:
|
||||
formatter.add("'Z'");
|
||||
break;
|
||||
}
|
||||
formatter.add("1*");
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateWpimultTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::map<size_t, double>& lateralsPerCell)
|
||||
{
|
||||
RigMainGrid* grid = settings.caseToApply->eclipseCaseData()->mainGrid();
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Well"),
|
||||
RifEclipseOutputTableColumn("Mult"),
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K"),
|
||||
};
|
||||
formatter.keyword("WPIMULT");
|
||||
formatter.header(header);
|
||||
|
||||
for (auto lateralsInCell : lateralsPerCell)
|
||||
{
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex(lateralsInCell.first, &i, &j, &k);
|
||||
formatter.add(wellPath->name());
|
||||
formatter.add(lateralsInCell.second);
|
||||
formatter.addZeroBasedCellIndex(i).addZeroBasedCellIndex(j).addZeroBasedCellIndex(k);
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateWelsegsTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations)
|
||||
{
|
||||
formatter.keyword("WELSEGS");
|
||||
|
||||
const WellSegmentLocation& firstLocation = locations[0];
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Name"),
|
||||
RifEclipseOutputTableColumn("Dep 1"),
|
||||
RifEclipseOutputTableColumn("Tlen 1"),
|
||||
RifEclipseOutputTableColumn("Vol 1"),
|
||||
RifEclipseOutputTableColumn("Len&Dep"),
|
||||
RifEclipseOutputTableColumn("PresDrop"),
|
||||
};
|
||||
formatter.header(header);
|
||||
|
||||
formatter.add(wellPath->name());
|
||||
formatter.add(firstLocation.trueVerticalDepth);
|
||||
formatter.add(firstLocation.measuredDepth);
|
||||
formatter.add("1*");
|
||||
formatter.add("INC");
|
||||
formatter.add("H--");
|
||||
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("First Seg"),
|
||||
RifEclipseOutputTableColumn("Last Seg"),
|
||||
RifEclipseOutputTableColumn("Branch Num"),
|
||||
RifEclipseOutputTableColumn("Outlet Seg"),
|
||||
RifEclipseOutputTableColumn("Length"),
|
||||
RifEclipseOutputTableColumn("Depth Change"),
|
||||
RifEclipseOutputTableColumn("Diam"),
|
||||
RifEclipseOutputTableColumn("Rough"),
|
||||
};
|
||||
formatter.header(header);
|
||||
}
|
||||
|
||||
{
|
||||
WellSegmentLocation previousLocation = firstLocation;
|
||||
formatter.comment("Main stem");
|
||||
for (size_t i = 0; i < locations.size(); ++i)
|
||||
{
|
||||
const WellSegmentLocation& location = locations[i];
|
||||
|
||||
formatter.comment(QString("Segment for sub %1").arg(location.subIndex));
|
||||
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(location.fishbonesSubs->locationOfSubs()[location.subIndex] - previousLocation.fishbonesSubs->locationOfSubs()[previousLocation.subIndex]);
|
||||
formatter.add(location.trueVerticalDepth - previousLocation.trueVerticalDepth);
|
||||
formatter.add(-1.0); // FIXME : Diam of main stem?
|
||||
formatter.add(-1.0); // FIXME : Rough of main stem?
|
||||
formatter.rowCompleted();
|
||||
|
||||
previousLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
formatter.comment("Laterals");
|
||||
formatter.comment("Diam: MSW - Tubing Radius");
|
||||
formatter.comment("Rough: MSW - Open Hole Roughness Factor");
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
formatter.comment(QString("%1 : Sub index %2 - Lateral %3").arg(location.fishbonesSubs->name()).arg(location.subIndex).arg(lateral.lateralIndex));
|
||||
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
formatter.add(intersection.segmentNumber);
|
||||
formatter.add(intersection.segmentNumber);
|
||||
formatter.add(lateral.branchNumber);
|
||||
formatter.add(intersection.attachedSegmentNumber);
|
||||
formatter.add(intersection.length);
|
||||
formatter.add(intersection.depth);
|
||||
formatter.add(location.fishbonesSubs->tubingRadius());
|
||||
formatter.add(location.fishbonesSubs->openHoleRoughnessFactor());
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::generateCompsegsTable(RifEclipseOutputTableFormatter& formatter, const RimWellPath* wellPath, const RimExportCompletionDataSettings& settings, const std::vector<WellSegmentLocation>& locations)
|
||||
{
|
||||
RigMainGrid* grid = settings.caseToApply->eclipseCaseData()->mainGrid();
|
||||
formatter.keyword("COMPSEGS");
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("Name")
|
||||
};
|
||||
formatter.header(header);
|
||||
formatter.add(wellPath->name());
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<RifEclipseOutputTableColumn> header = {
|
||||
RifEclipseOutputTableColumn("I"),
|
||||
RifEclipseOutputTableColumn("J"),
|
||||
RifEclipseOutputTableColumn("K"),
|
||||
RifEclipseOutputTableColumn("Branch no"),
|
||||
RifEclipseOutputTableColumn("Start Length"),
|
||||
RifEclipseOutputTableColumn("End Length"),
|
||||
RifEclipseOutputTableColumn("Dir Pen"),
|
||||
RifEclipseOutputTableColumn("End Range"),
|
||||
RifEclipseOutputTableColumn("Connection Depth")
|
||||
};
|
||||
formatter.header(header);
|
||||
}
|
||||
|
||||
for (const WellSegmentLocation& location : locations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
double length = 0;
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
length += intersection.length;
|
||||
|
||||
if (settings.removeLateralsInMainBoreCells && intersection.mainBoreCell) continue;
|
||||
|
||||
size_t i, j, k;
|
||||
grid->ijkFromCellIndex(intersection.cellIndex, &i, &j, &k);
|
||||
|
||||
formatter.addZeroBasedCellIndex(i).addZeroBasedCellIndex(j).addZeroBasedCellIndex(k);
|
||||
formatter.add(lateral.branchNumber);
|
||||
formatter.add(length);
|
||||
formatter.add("1*");
|
||||
switch (intersection.direction)
|
||||
{
|
||||
case POS_I:
|
||||
case NEG_I:
|
||||
formatter.add("I");
|
||||
break;
|
||||
case POS_J:
|
||||
case NEG_J:
|
||||
formatter.add("J");
|
||||
break;
|
||||
case POS_K:
|
||||
case NEG_K:
|
||||
formatter.add("K");
|
||||
break;
|
||||
}
|
||||
formatter.add(-1);
|
||||
formatter.rowCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.tableCompleted();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RicWellPathExportCompletionDataFeature::findCloseCells(const RigEclipseCaseData* caseData, const cvf::BoundingBox& bb)
|
||||
{
|
||||
std::vector<size_t> closeCells;
|
||||
caseData->mainGrid()->findIntersectingCells(bb, &closeCells);
|
||||
return closeCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<EclipseCellIndexRange> RicWellPathExportCompletionDataFeature::getCellIndexRange(const RigMainGrid* grid, const std::vector<size_t>& cellIndices)
|
||||
{
|
||||
// Retrieve I, J, K indices
|
||||
std::vector<EclipseCellIndex> eclipseCellIndices;
|
||||
for (auto cellIndex : cellIndices)
|
||||
{
|
||||
size_t i, j, k;
|
||||
if (!grid->ijkFromCellIndex(cellIndex, &i, &j, &k)) continue;
|
||||
eclipseCellIndices.push_back(std::make_tuple(i, j, k));
|
||||
}
|
||||
|
||||
// Group cell indices in K-ranges
|
||||
std::sort(eclipseCellIndices.begin(), eclipseCellIndices.end(), RicWellPathExportCompletionDataFeature::cellOrdering);
|
||||
std::vector<EclipseCellIndexRange> eclipseCellRanges;
|
||||
size_t lastI = std::numeric_limits<size_t>::max();
|
||||
size_t lastJ = std::numeric_limits<size_t>::max();
|
||||
size_t lastK = std::numeric_limits<size_t>::max();
|
||||
size_t startK = std::numeric_limits<size_t>::max();
|
||||
for (EclipseCellIndex cell : eclipseCellIndices)
|
||||
{
|
||||
size_t i, j, k;
|
||||
std::tie(i, j, k) = cell;
|
||||
if (i != lastI || j != lastJ || k != lastK + 1)
|
||||
{
|
||||
if (startK != std::numeric_limits<size_t>::max())
|
||||
{
|
||||
EclipseCellIndexRange cellRange = {lastI, lastJ, startK, lastK};
|
||||
eclipseCellRanges.push_back(cellRange);
|
||||
}
|
||||
lastI = i;
|
||||
lastJ = j;
|
||||
lastK = k;
|
||||
startK = k;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastK = k;
|
||||
}
|
||||
}
|
||||
// Append last cell range
|
||||
if (startK != std::numeric_limits<size_t>::max())
|
||||
{
|
||||
EclipseCellIndexRange cellRange = {lastI, lastJ, startK, lastK};
|
||||
eclipseCellRanges.push_back(cellRange);
|
||||
}
|
||||
return eclipseCellRanges;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::cellOrdering(const EclipseCellIndex& cell1, const EclipseCellIndex& cell2)
|
||||
{
|
||||
size_t i1, i2, j1, j2, k1, k2;
|
||||
std::tie(i1, j1, k1) = cell1;
|
||||
std::tie(i2, j2, k2) = cell2;
|
||||
if (i1 == i2)
|
||||
{
|
||||
if (j1 == j2)
|
||||
{
|
||||
return k1 < k2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return j1 < j2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return i1 < i2;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RicWellPathExportCompletionDataFeature::findCellFromCoords(const RigEclipseCaseData* caseData, const cvf::Vec3d& coords)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = caseData->mainGrid()->nodes();
|
||||
|
||||
cvf::BoundingBox bb;
|
||||
bb.add(coords);
|
||||
std::vector<size_t> closeCells = findCloseCells(caseData, bb);
|
||||
cvf::Vec3d hexCorners[8];
|
||||
|
||||
for (size_t closeCell : closeCells)
|
||||
{
|
||||
const RigCell& cell = caseData->mainGrid()->globalCellArray()[closeCell];
|
||||
if (cell.isInvalid()) continue;
|
||||
|
||||
setHexCorners(cell, nodeCoords, hexCorners);
|
||||
|
||||
if (RigHexIntersector::isPointInCell(coords, hexCorners, closeCell))
|
||||
{
|
||||
return closeCell;
|
||||
}
|
||||
}
|
||||
|
||||
// Coordinate is outside any cells?
|
||||
CVF_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RicWellPathExportCompletionDataFeature::findIntersectingCells(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = caseData->mainGrid()->nodes();
|
||||
std::set<size_t> cells;
|
||||
|
||||
// Find starting cell
|
||||
if (coords.size() > 0)
|
||||
{
|
||||
size_t startCell = findCellFromCoords(caseData, coords[0]);
|
||||
if (startCell > 0)
|
||||
{
|
||||
cells.insert(startCell);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<HexIntersectionInfo> intersections = findIntersections(caseData, coords);
|
||||
for (auto intersection : intersections)
|
||||
{
|
||||
cells.insert(intersection.m_hexIndex);
|
||||
}
|
||||
|
||||
// Ensure only unique cells are included
|
||||
std::vector<size_t> cellsVector;
|
||||
cellsVector.assign(cells.begin(), cells.end());
|
||||
// Sort cells
|
||||
std::sort(cellsVector.begin(), cellsVector.end());
|
||||
return cellsVector;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<HexIntersectionInfo> RicWellPathExportCompletionDataFeature::findIntersections(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = caseData->mainGrid()->nodes();
|
||||
std::vector<HexIntersectionInfo> intersections;
|
||||
for (size_t i = 0; i < coords.size() - 1; ++i)
|
||||
{
|
||||
cvf::BoundingBox bb;
|
||||
bb.add(coords[i]);
|
||||
bb.add(coords[i + 1]);
|
||||
|
||||
std::vector<size_t> closeCells = findCloseCells(caseData, bb);
|
||||
|
||||
cvf::Vec3d hexCorners[8];
|
||||
|
||||
for (size_t closeCell : closeCells)
|
||||
{
|
||||
const RigCell& cell = caseData->mainGrid()->globalCellArray()[closeCell];
|
||||
if (cell.isInvalid()) continue;
|
||||
|
||||
setHexCorners(cell, nodeCoords, hexCorners);
|
||||
|
||||
RigHexIntersector::lineHexCellIntersection(coords[i], coords[i + 1], hexCorners, closeCell, &intersections);
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::setHexCorners(const RigCell& cell, const std::vector<cvf::Vec3d>& nodeCoords, cvf::Vec3d* hexCorners)
|
||||
{
|
||||
const caf::SizeTArray8& cornerIndices = cell.cornerIndices();
|
||||
|
||||
hexCorners[0] = nodeCoords[cornerIndices[0]];
|
||||
hexCorners[1] = nodeCoords[cornerIndices[1]];
|
||||
hexCorners[2] = nodeCoords[cornerIndices[2]];
|
||||
hexCorners[3] = nodeCoords[cornerIndices[3]];
|
||||
hexCorners[4] = nodeCoords[cornerIndices[4]];
|
||||
hexCorners[5] = nodeCoords[cornerIndices[5]];
|
||||
hexCorners[6] = nodeCoords[cornerIndices[6]];
|
||||
hexCorners[7] = nodeCoords[cornerIndices[7]];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::markWellPathCells(const std::vector<size_t>& wellPathCells, std::vector<WellSegmentLocation>* locations)
|
||||
{
|
||||
std::set<size_t> wellPathCellSet(wellPathCells.begin(), wellPathCells.end());
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
for (WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
for (WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (wellPathCellSet.find(intersection.cellIndex) != wellPathCellSet.end())
|
||||
{
|
||||
intersection.mainBoreCell = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<size_t, double> RicWellPathExportCompletionDataFeature::computeLateralsPerCell(const std::vector<WellSegmentLocation>& segmentLocations, bool removeMainBoreCells)
|
||||
{
|
||||
std::map<size_t, double> lateralsPerCell;
|
||||
for (const WellSegmentLocation& location : segmentLocations)
|
||||
{
|
||||
for (const WellSegmentLateral& lateral : location.laterals)
|
||||
{
|
||||
for (const WellSegmentLateralIntersection& intersection : lateral.intersections)
|
||||
{
|
||||
if (removeMainBoreCells && intersection.mainBoreCell) continue;
|
||||
|
||||
auto match = lateralsPerCell.find(intersection.cellIndex);
|
||||
if (match == lateralsPerCell.end())
|
||||
{
|
||||
lateralsPerCell[intersection.cellIndex] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lateralsPerCell[intersection.cellIndex] = match->second + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lateralsPerCell;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::wellSegmentLocationOrdering(const WellSegmentLocation& first, const WellSegmentLocation& second)
|
||||
{
|
||||
return first.measuredDepth < second.measuredDepth;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RicWellPathExportCompletionDataFeature::isPointBetween(const cvf::Vec3d& pointA, const cvf::Vec3d& pointB, const cvf::Vec3d& needle)
|
||||
{
|
||||
cvf::Plane plane;
|
||||
plane.setFromPointAndNormal(needle, pointB - pointA);
|
||||
return plane.side(pointA) != plane.side(pointB);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::filterIntersections(std::vector<HexIntersectionInfo>* intersections)
|
||||
{
|
||||
// Erase intersections that are marked as entering
|
||||
for (auto it = intersections->begin(); it != intersections->end();)
|
||||
{
|
||||
if (it->m_isIntersectionEntering)
|
||||
{
|
||||
it = intersections->erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<WellSegmentLocation> RicWellPathExportCompletionDataFeature::findWellSegmentLocations(const RimEclipseCase* caseToApply, RimWellPath* wellPath)
|
||||
{
|
||||
std::vector<WellSegmentLocation> wellSegmentLocations;
|
||||
for (RimFishbonesMultipleSubs* subs : wellPath->fishbonesCollection()->fishbonesSubs())
|
||||
{
|
||||
for (size_t subIndex = 0; subIndex < subs->locationOfSubs().size(); ++subIndex)
|
||||
{
|
||||
double measuredDepth = subs->locationOfSubs()[subIndex];
|
||||
cvf::Vec3d position = wellPath->wellPathGeometry()->interpolatedPointAlongWellPath(measuredDepth);
|
||||
WellSegmentLocation location = WellSegmentLocation(subs, measuredDepth, -position.z(), subIndex);
|
||||
for (size_t lateralIndex = 0; lateralIndex < subs->lateralLengths().size(); ++lateralIndex)
|
||||
{
|
||||
location.laterals.push_back(WellSegmentLateral(lateralIndex));
|
||||
}
|
||||
wellSegmentLocations.push_back(location);
|
||||
}
|
||||
}
|
||||
std::sort(wellSegmentLocations.begin(), wellSegmentLocations.end(), wellSegmentLocationOrdering);
|
||||
|
||||
assignBranchAndSegmentNumbers(caseToApply, &wellSegmentLocations);
|
||||
|
||||
return wellSegmentLocations;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::calculateLateralIntersections(const RimEclipseCase* caseToApply, WellSegmentLocation* location, int* branchNum, int* segmentNum)
|
||||
{
|
||||
for (WellSegmentLateral& lateral : location->laterals)
|
||||
{
|
||||
lateral.branchNumber = ++(*branchNum);
|
||||
std::vector<cvf::Vec3d> coords = location->fishbonesSubs->coordsForLateral(location->subIndex, lateral.lateralIndex);
|
||||
std::vector<HexIntersectionInfo> intersections = findIntersections(caseToApply->eclipseCaseData(), coords);
|
||||
filterIntersections(&intersections);
|
||||
|
||||
const HexIntersectionInfo* prevIntersection = nullptr;
|
||||
|
||||
{
|
||||
double length = 0;
|
||||
double depth = 0;
|
||||
cvf::Vec3d startPoint = coords[0];
|
||||
auto intersection = intersections.cbegin();
|
||||
int attachedSegmentNumber = location->segmentNumber;
|
||||
|
||||
for (size_t i = 1; i < coords.size() && intersection != intersections.cend(); i++)
|
||||
{
|
||||
if (isPointBetween(startPoint, coords[i], intersection->m_intersectionPoint))
|
||||
{
|
||||
cvf::Vec3d between = intersection->m_intersectionPoint - startPoint;
|
||||
length += between.length();
|
||||
depth += intersection->m_intersectionPoint.z() - startPoint.z();
|
||||
|
||||
// Find the direction of the previous cell
|
||||
if (prevIntersection != nullptr)
|
||||
{
|
||||
std::pair<WellSegmentCellDirection, double> direction = calculateDirectionAndDistanceInCell(caseToApply->eclipseCaseData()->mainGrid(), prevIntersection->m_hexIndex, prevIntersection->m_intersectionPoint, intersection->m_intersectionPoint);
|
||||
WellSegmentLateralIntersection& lateralIntersection = lateral.intersections[lateral.intersections.size() - 1];
|
||||
lateralIntersection.direction = direction.first;
|
||||
lateralIntersection.directionLength = direction.second;
|
||||
}
|
||||
|
||||
lateral.intersections.push_back(WellSegmentLateralIntersection(++(*segmentNum), attachedSegmentNumber, intersection->m_hexIndex, length, depth));
|
||||
|
||||
length = 0;
|
||||
depth = 0;
|
||||
startPoint = intersection->m_intersectionPoint;
|
||||
attachedSegmentNumber = *segmentNum;
|
||||
++intersection;
|
||||
prevIntersection = &*intersection;
|
||||
}
|
||||
else
|
||||
{
|
||||
const cvf::Vec3d between = coords[i] - startPoint;
|
||||
length += between.length();
|
||||
depth += coords[i].z() - startPoint.z();
|
||||
startPoint = coords[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the direction of the last cell
|
||||
if (prevIntersection != nullptr && !coords.empty())
|
||||
{
|
||||
std::pair<WellSegmentCellDirection, double> direction = calculateDirectionAndDistanceInCell(caseToApply->eclipseCaseData()->mainGrid(), prevIntersection->m_hexIndex, prevIntersection->m_intersectionPoint, coords[coords.size()-1]);
|
||||
WellSegmentLateralIntersection& lateralIntersection = lateral.intersections[lateral.intersections.size() - 1];
|
||||
lateralIntersection.direction = direction.first;
|
||||
lateralIntersection.directionLength = direction.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::assignBranchAndSegmentNumbers(const RimEclipseCase* caseToApply, std::vector<WellSegmentLocation>* locations)
|
||||
{
|
||||
int segmentNumber = 1;
|
||||
int branchNumber = 1;
|
||||
// First loop over the locations so that each segment on the main stem is an incremental number
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
location.segmentNumber = ++segmentNumber;
|
||||
}
|
||||
// Then assign branch and segment numbers to each lateral parts
|
||||
for (WellSegmentLocation& location : *locations)
|
||||
{
|
||||
calculateLateralIntersections(caseToApply, &location, &branchNumber, &segmentNumber);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicWellPathExportCompletionDataFeature::calculateCellMainAxisDirections(const RigMainGrid* grid, size_t cellIndex, cvf::Vec3d* iAxisDirection, cvf::Vec3d* jAxisDirection, cvf::Vec3d* kAxisDirection)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = grid->nodes();
|
||||
cvf::Vec3d hexCorners[8];
|
||||
const RigCell& cell = grid->globalCellArray()[cellIndex];
|
||||
setHexCorners(cell, nodeCoords, hexCorners);
|
||||
|
||||
*iAxisDirection = calculateCellMainAxisDirection(hexCorners, cvf::StructGridInterface::FaceType::NEG_I, cvf::StructGridInterface::POS_I);
|
||||
*jAxisDirection = calculateCellMainAxisDirection(hexCorners, cvf::StructGridInterface::FaceType::NEG_J, cvf::StructGridInterface::POS_J);
|
||||
*kAxisDirection = calculateCellMainAxisDirection(hexCorners, cvf::StructGridInterface::FaceType::NEG_K, cvf::StructGridInterface::POS_K);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RicWellPathExportCompletionDataFeature::calculateCellMainAxisDirection(const cvf::Vec3d* hexCorners, cvf::StructGridInterface::FaceType startFace, cvf::StructGridInterface::FaceType endFace)
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
|
||||
cvf::StructGridInterface::cellFaceVertexIndices(startFace, faceVertexIndices);
|
||||
|
||||
cvf::Vec3d startFaceCenter = cvf::GeometryTools::computeFaceCenter(hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]]);
|
||||
|
||||
cvf::StructGridInterface::cellFaceVertexIndices(endFace, faceVertexIndices);
|
||||
|
||||
cvf::Vec3d endFaceCenter = cvf::GeometryTools::computeFaceCenter(hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]]);
|
||||
|
||||
return endFaceCenter - startFaceCenter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<WellSegmentCellDirection, double> RicWellPathExportCompletionDataFeature::calculateDirectionAndDistanceInCell(const RigMainGrid* grid, size_t cellIndex, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint)
|
||||
{
|
||||
cvf::Vec3d vec = endPoint - startPoint;
|
||||
|
||||
cvf::Vec3d iAxisDirection;
|
||||
cvf::Vec3d jAxisDirection;
|
||||
cvf::Vec3d kAxisDirection;
|
||||
calculateCellMainAxisDirections(grid, cellIndex, &iAxisDirection, &jAxisDirection, &kAxisDirection);
|
||||
|
||||
double iLength = iAxisDirection.dot(vec);
|
||||
double jLength = jAxisDirection.dot(vec);
|
||||
double kLength = kAxisDirection.dot(vec);
|
||||
|
||||
double iNormalizedLength = abs(iLength / iAxisDirection.length());
|
||||
double jNormalizedLength = abs(jLength / jAxisDirection.length());
|
||||
double kNormalizedLength = abs(kLength / kAxisDirection.length());
|
||||
|
||||
if (iNormalizedLength > jNormalizedLength && iNormalizedLength > kNormalizedLength)
|
||||
{
|
||||
WellSegmentCellDirection direction = POS_I;
|
||||
if (iLength < 0)
|
||||
{
|
||||
direction = NEG_I;
|
||||
}
|
||||
return std::make_pair(direction, iLength);
|
||||
}
|
||||
else if (jNormalizedLength > iNormalizedLength && jNormalizedLength > kNormalizedLength)
|
||||
{
|
||||
WellSegmentCellDirection direction = POS_J;
|
||||
if (jLength < 0)
|
||||
{
|
||||
direction = NEG_J;
|
||||
}
|
||||
return std::make_pair(direction, jLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
WellSegmentCellDirection direction = POS_K;
|
||||
if (kLength < 0)
|
||||
{
|
||||
direction = NEG_K;
|
||||
}
|
||||
return std::make_pair(direction, kLength);
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,14 @@ if (${CMAKE_VERSION} VERSION_GREATER "2.8.2")
|
||||
endif()
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseDataTableFormatter.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseInputFileTools.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseOutputFileTools.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseOutputTableFormatter.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseRestartDataAccess.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseRestartFilesetAccess.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseSummaryTools.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseUnifiedRestartFileAccess.h
|
||||
${CEE_CURRENT_LIST_DIR}RifPerforationIntervalReader.h
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseInput.h
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseOutput.h
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseSummary.h
|
||||
@@ -26,13 +27,14 @@ ${CEE_CURRENT_LIST_DIR}RifFractureExportTools.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseDataTableFormatter.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseInputFileTools.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseOutputFileTools.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseOutputTableFormatter.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseRestartDataAccess.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseRestartFilesetAccess.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseUnifiedRestartFileAccess.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseSummaryTools.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifPerforationIntervalReader.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseInput.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseOutput.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifReaderEclipseSummary.cpp
|
||||
|
||||
@@ -16,21 +16,21 @@
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RifEclipseOutputTableFormatter.h"
|
||||
#include "RifEclipseDataTableFormatter.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter::RifEclipseOutputTableFormatter(QTextStream& out) : m_out(out)
|
||||
RifEclipseDataTableFormatter::RifEclipseDataTableFormatter(QTextStream& out) : m_out(out)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter::~RifEclipseOutputTableFormatter()
|
||||
RifEclipseDataTableFormatter::~RifEclipseDataTableFormatter()
|
||||
{
|
||||
CVF_ASSERT(m_buffer.empty());
|
||||
CVF_ASSERT(m_columns.empty());
|
||||
@@ -39,7 +39,7 @@ RifEclipseOutputTableFormatter::~RifEclipseOutputTableFormatter()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifEclipseOutputTableFormatter::outputBuffer()
|
||||
void RifEclipseDataTableFormatter::outputBuffer()
|
||||
{
|
||||
if (m_columns.size() > 0)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ void RifEclipseOutputTableFormatter::outputBuffer()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifEclipseOutputTableFormatter::outputComment(RifEclipseOutputTableLine& comment)
|
||||
void RifEclipseDataTableFormatter::outputComment(RifEclipseOutputTableLine& comment)
|
||||
{
|
||||
m_out << "-- " << comment.data[0] << "\n";
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void RifEclipseOutputTableFormatter::outputComment(RifEclipseOutputTableLine& co
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifEclipseOutputTableFormatter::tableCompleted()
|
||||
void RifEclipseDataTableFormatter::tableCompleted()
|
||||
{
|
||||
outputBuffer();
|
||||
// Output an "empty" line after a finished table
|
||||
@@ -92,7 +92,7 @@ void RifEclipseOutputTableFormatter::tableCompleted()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::keyword(const QString keyword)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::keyword(const QString keyword)
|
||||
{
|
||||
CVF_ASSERT(m_buffer.empty());
|
||||
CVF_ASSERT(m_columns.empty());
|
||||
@@ -103,7 +103,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::keyword(const QS
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::header(const std::vector<RifEclipseOutputTableColumn> header)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::header(const std::vector<RifEclipseOutputTableColumn> header)
|
||||
{
|
||||
outputBuffer();
|
||||
m_columns = header;
|
||||
@@ -117,7 +117,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::header(const std
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::comment(const QString comment)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::comment(const QString comment)
|
||||
{
|
||||
RifEclipseOutputTableLine line;
|
||||
line.data.push_back(comment);
|
||||
@@ -136,7 +136,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::comment(const QS
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(const QString str)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::add(const QString str)
|
||||
{
|
||||
size_t column = m_lineBuffer.size();
|
||||
CVF_ASSERT(column < m_columns.size());
|
||||
@@ -148,7 +148,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(const QStrin
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(double num)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::add(double num)
|
||||
{
|
||||
size_t column = m_lineBuffer.size();
|
||||
CVF_ASSERT(column < m_columns.size());
|
||||
@@ -160,7 +160,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(double num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(int num)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::add(int num)
|
||||
{
|
||||
size_t column = m_lineBuffer.size();
|
||||
CVF_ASSERT(column < m_columns.size());
|
||||
@@ -172,7 +172,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(int num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(size_t num)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::add(size_t num)
|
||||
{
|
||||
size_t column = m_lineBuffer.size();
|
||||
CVF_ASSERT(column < m_columns.size());
|
||||
@@ -184,7 +184,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::add(size_t num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::addZeroBasedCellIndex(size_t index)
|
||||
RifEclipseDataTableFormatter& RifEclipseDataTableFormatter::addZeroBasedCellIndex(size_t index)
|
||||
{
|
||||
size_t column = m_lineBuffer.size();
|
||||
CVF_ASSERT(column < m_columns.size());
|
||||
@@ -200,7 +200,7 @@ RifEclipseOutputTableFormatter& RifEclipseOutputTableFormatter::addZeroBasedCell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifEclipseOutputTableFormatter::rowCompleted()
|
||||
void RifEclipseDataTableFormatter::rowCompleted()
|
||||
{
|
||||
RifEclipseOutputTableLine line;
|
||||
line.data = m_lineBuffer;
|
||||
@@ -212,7 +212,7 @@ void RifEclipseOutputTableFormatter::rowCompleted()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifEclipseOutputTableFormatter::measure(const QString str)
|
||||
int RifEclipseDataTableFormatter::measure(const QString str)
|
||||
{
|
||||
return str.length();
|
||||
}
|
||||
@@ -220,7 +220,7 @@ int RifEclipseOutputTableFormatter::measure(const QString str)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifEclipseOutputTableFormatter::measure(double num)
|
||||
int RifEclipseDataTableFormatter::measure(double num)
|
||||
{
|
||||
return format(num).length();
|
||||
}
|
||||
@@ -228,7 +228,7 @@ int RifEclipseOutputTableFormatter::measure(double num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifEclipseOutputTableFormatter::measure(int num)
|
||||
int RifEclipseDataTableFormatter::measure(int num)
|
||||
{
|
||||
return format(num).length();
|
||||
}
|
||||
@@ -236,7 +236,7 @@ int RifEclipseOutputTableFormatter::measure(int num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int RifEclipseOutputTableFormatter::measure(size_t num)
|
||||
int RifEclipseDataTableFormatter::measure(size_t num)
|
||||
{
|
||||
return format(num).length();
|
||||
}
|
||||
@@ -244,7 +244,7 @@ int RifEclipseOutputTableFormatter::measure(size_t num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RifEclipseOutputTableFormatter::format(double num)
|
||||
QString RifEclipseDataTableFormatter::format(double num)
|
||||
{
|
||||
return QString("%1").arg(num, 0, 'f', m_doubleDecimals);
|
||||
}
|
||||
@@ -252,7 +252,7 @@ QString RifEclipseOutputTableFormatter::format(double num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RifEclipseOutputTableFormatter::format(int num)
|
||||
QString RifEclipseDataTableFormatter::format(int num)
|
||||
{
|
||||
return QString("%1").arg(num);
|
||||
}
|
||||
@@ -260,7 +260,7 @@ QString RifEclipseOutputTableFormatter::format(int num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RifEclipseOutputTableFormatter::format(size_t num)
|
||||
QString RifEclipseDataTableFormatter::format(size_t num)
|
||||
{
|
||||
return QString("%1").arg(num);
|
||||
}
|
||||
@@ -268,7 +268,7 @@ QString RifEclipseOutputTableFormatter::format(size_t num)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString RifEclipseOutputTableFormatter::formatColumn(const QString str, RifEclipseOutputTableColumn column)
|
||||
QString RifEclipseDataTableFormatter::formatColumn(const QString str, RifEclipseOutputTableColumn column)
|
||||
{
|
||||
if (column.alignment == LEFT)
|
||||
{
|
||||
@@ -71,20 +71,20 @@ struct RifEclipseOutputTableColumn
|
||||
//==================================================================================================
|
||||
//
|
||||
//==================================================================================================
|
||||
class RifEclipseOutputTableFormatter
|
||||
class RifEclipseDataTableFormatter
|
||||
{
|
||||
public:
|
||||
RifEclipseOutputTableFormatter(QTextStream& out);
|
||||
virtual ~RifEclipseOutputTableFormatter();
|
||||
RifEclipseDataTableFormatter(QTextStream& out);
|
||||
virtual ~RifEclipseDataTableFormatter();
|
||||
|
||||
RifEclipseOutputTableFormatter& keyword(const QString keyword);
|
||||
RifEclipseOutputTableFormatter& header(std::vector<RifEclipseOutputTableColumn> tableHeader);
|
||||
RifEclipseOutputTableFormatter& add(const QString str);
|
||||
RifEclipseOutputTableFormatter& add(double num);
|
||||
RifEclipseOutputTableFormatter& add(int num);
|
||||
RifEclipseOutputTableFormatter& add(size_t num);
|
||||
RifEclipseOutputTableFormatter& addZeroBasedCellIndex(size_t index);
|
||||
RifEclipseOutputTableFormatter& comment(const QString str);
|
||||
RifEclipseDataTableFormatter& keyword(const QString keyword);
|
||||
RifEclipseDataTableFormatter& header(std::vector<RifEclipseOutputTableColumn> tableHeader);
|
||||
RifEclipseDataTableFormatter& add(const QString str);
|
||||
RifEclipseDataTableFormatter& add(double num);
|
||||
RifEclipseDataTableFormatter& add(int num);
|
||||
RifEclipseDataTableFormatter& add(size_t num);
|
||||
RifEclipseDataTableFormatter& addZeroBasedCellIndex(size_t index);
|
||||
RifEclipseDataTableFormatter& comment(const QString str);
|
||||
void rowCompleted();
|
||||
void tableCompleted();
|
||||
|
||||
125
ApplicationCode/FileInterface/RifPerforationIntervalReader.cpp
Normal file
125
ApplicationCode/FileInterface/RifPerforationIntervalReader.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RifPerforationIntervalReader.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDate>
|
||||
|
||||
const QString PERFORATION_KEY("perforation");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<QString, std::vector<RifPerforationInterval> > RifPerforationIntervalReader::readPerforationIntervals(const QStringList& filePaths)
|
||||
{
|
||||
std::map<QString, std::vector<RifPerforationInterval>> perforationIntervals;
|
||||
|
||||
foreach (QString filePath, filePaths)
|
||||
{
|
||||
readFileIntoMap(filePath, &perforationIntervals);
|
||||
}
|
||||
|
||||
return perforationIntervals;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::map<QString, std::vector<RifPerforationInterval> > RifPerforationIntervalReader::readPerforationIntervals(const QString& filePath)
|
||||
{
|
||||
std::map<QString, std::vector<RifPerforationInterval> > perforationIntervals;
|
||||
|
||||
readFileIntoMap(filePath, &perforationIntervals);
|
||||
|
||||
return perforationIntervals;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifPerforationIntervalReader::readFileIntoMap(const QString& filePath, std::map<QString, std::vector<RifPerforationInterval>>* perforations)
|
||||
{
|
||||
QFile data(filePath);
|
||||
|
||||
if (!data.open(QFile::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QString wellName;
|
||||
|
||||
do {
|
||||
QString line = data.readLine();
|
||||
|
||||
if (line.startsWith("--"))
|
||||
{
|
||||
// Skip comment
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace any tabs with spaces to enable splitting on spaces
|
||||
line.replace("\t", " ");
|
||||
QStringList parts = line.split(" ", QString::SkipEmptyParts);
|
||||
|
||||
if (line.startsWith("WELLNAME"))
|
||||
{
|
||||
// Save current well name
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
wellName = parts[1].trimmed();
|
||||
}
|
||||
}
|
||||
else if (parts.size() >= 6)
|
||||
{
|
||||
RifPerforationInterval interval;
|
||||
|
||||
int mdStartIndex;
|
||||
|
||||
if (parts[3] == PERFORATION_KEY)
|
||||
{
|
||||
interval.date = QDate::fromString(QString("%1 %2 %3").arg(parts[0]).arg(parts[1]).arg(parts[2]), "dd MMM yyyy");
|
||||
interval.startOfHistory = false;
|
||||
|
||||
mdStartIndex = 4;
|
||||
}
|
||||
else if (parts[1] == PERFORATION_KEY)
|
||||
{
|
||||
interval.startOfHistory = true;
|
||||
|
||||
mdStartIndex = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
interval.startMD = parts[mdStartIndex].toDouble();
|
||||
interval.endMD = parts[mdStartIndex + 1].toDouble();
|
||||
interval.diameter = parts[mdStartIndex + 2].toDouble();
|
||||
interval.skinFactor = parts[mdStartIndex + 3].toDouble();
|
||||
|
||||
auto match = perforations->find(wellName);
|
||||
if (match == perforations->end())
|
||||
{
|
||||
(*perforations)[wellName] = std::vector<RifPerforationInterval>();
|
||||
}
|
||||
(*perforations)[wellName].push_back(interval);
|
||||
}
|
||||
} while (!data.atEnd());
|
||||
}
|
||||
48
ApplicationCode/FileInterface/RifPerforationIntervalReader.h
Normal file
48
ApplicationCode/FileInterface/RifPerforationIntervalReader.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RimPerforationInterval.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct RifPerforationInterval
|
||||
{
|
||||
double startMD;
|
||||
double endMD;
|
||||
double diameter;
|
||||
double skinFactor;
|
||||
bool startOfHistory;
|
||||
QDate date;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RifPerforationIntervalReader
|
||||
{
|
||||
public:
|
||||
static std::map<QString, std::vector<RifPerforationInterval> > readPerforationIntervals(const QStringList& filePaths);
|
||||
static std::map<QString, std::vector<RifPerforationInterval> > readPerforationIntervals(const QString& filePath);
|
||||
|
||||
private:
|
||||
static void readFileIntoMap(const QString& filePath, std::map<QString, std::vector<RifPerforationInterval> >* perforations);
|
||||
};
|
||||
@@ -971,13 +971,22 @@ RigWellResultPoint RifReaderEclipseOutput::createWellResultPoint(const RigGridBa
|
||||
resultPoint.m_oilRate = oilRate;
|
||||
resultPoint.m_waterRate = waterRate;
|
||||
|
||||
/// Unit conversion for use with Well Allocation plots
|
||||
// Convert Gas to oil equivalents
|
||||
// If field unit, the Gas is in Mega ft^3 while the others are in [stb] (barrel)
|
||||
|
||||
// Unused Gas to Barrel conversion
|
||||
// we convert gas to stb as well. Based on
|
||||
// 1 [stb] = 0.15898729492800007 [m^3]
|
||||
// 1 [ft] = 0.3048 [m]
|
||||
// megaFt3ToStbFactor = 1.0 / (1.0e-6 * 0.15898729492800007 * ( 1.0 / 0.3048 )^3 )
|
||||
double megaFt3ToStbFactor = 178107.60668;
|
||||
if (m_eclipseCase->unitsType() == RigEclipseCaseData::UNITS_FIELD) gasRate = megaFt3ToStbFactor * gasRate;
|
||||
// double megaFt3ToStbFactor = 178107.60668;
|
||||
|
||||
double fieldGasToOilEquivalent = 1.0e6/5800; // Mega ft^3 to BOE
|
||||
double metricGasToOilEquivalent = 1.0/1.0e3; // Sm^3 Gas to Sm^3 oe
|
||||
|
||||
if (m_eclipseCase->unitsType() == RigEclipseCaseData::UNITS_FIELD) gasRate = fieldGasToOilEquivalent * gasRate;
|
||||
if (m_eclipseCase->unitsType() == RigEclipseCaseData::UNITS_METRIC) gasRate = metricGasToOilEquivalent * gasRate;
|
||||
|
||||
resultPoint.m_gasRate = gasRate;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ void RivFishbonesSubsPartMgr::buildParts(caf::DisplayCoordTransform* displayCoor
|
||||
displayCoords.push_back(displayCoordTransform->transformToDisplayCoord(domainCoord));
|
||||
}
|
||||
|
||||
geoGenerator.cylinderWithCenterLineParts(&m_parts, displayCoords, wellPath->wellPathColor(), wellPath->combinedScaleFactor() * characteristicCellSize * 0.5);
|
||||
geoGenerator.cylinderWithCenterLineParts(&m_parts, displayCoords, m_rimFishbonesSubs->fishbonesColor(), wellPath->combinedScaleFactor() * characteristicCellSize * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ endif()
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseCaseCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimCaseCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimCaseAndFileExportSettings.h
|
||||
${CEE_CURRENT_LIST_DIR}RimExportCompletionDataSettings.h
|
||||
${CEE_CURRENT_LIST_DIR}RimCellFilter.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipsePropertyFilter.h
|
||||
${CEE_CURRENT_LIST_DIR}RimPropertyFilterCollection.h
|
||||
@@ -18,8 +16,6 @@ ${CEE_CURRENT_LIST_DIR}RimCellRangeFilterCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimDefines.h
|
||||
${CEE_CURRENT_LIST_DIR}RimLegendConfig.h
|
||||
${CEE_CURRENT_LIST_DIR}RimOilField.h
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationInterval.h
|
||||
${CEE_CURRENT_LIST_DIR}RimProject.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseCase.h
|
||||
${CEE_CURRENT_LIST_DIR}RimIdenticalGridCaseGroup.h
|
||||
@@ -35,8 +31,6 @@ ${CEE_CURRENT_LIST_DIR}RimEclipseWell.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseWellCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPath.h
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPath.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPathCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimScriptCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseStatisticsCase.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseStatisticsCaseCollection.h
|
||||
@@ -96,7 +90,6 @@ ${CEE_CURRENT_LIST_DIR}RimCheckableNamedObject.h
|
||||
${CEE_CURRENT_LIST_DIR}RimGridTimeHistoryCurve.h
|
||||
${CEE_CURRENT_LIST_DIR}RimGeometrySelectionItem.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseGeometrySelectionItem.h
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCompletions.h
|
||||
${CEE_CURRENT_LIST_DIR}RimEllipseFractureTemplate.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFractureTemplateCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFracture.h
|
||||
@@ -114,8 +107,6 @@ ${CEE_CURRENT_LIST_DIR}RimStimPlanColors.h
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseCaseCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimCaseCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimCaseAndFileExportSettings.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimExportCompletionDataSettings.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimCellFilter.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipsePropertyFilter.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimPropertyFilterCollection.cpp
|
||||
@@ -125,8 +116,6 @@ ${CEE_CURRENT_LIST_DIR}RimCellRangeFilterCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimDefines.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimLegendConfig.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimOilField.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationInterval.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimProject.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseCase.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimIdenticalGridCaseGroup.cpp
|
||||
@@ -142,8 +131,6 @@ ${CEE_CURRENT_LIST_DIR}RimEclipseWell.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseWellCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPath.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPath.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPathCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimScriptCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseStatisticsCase.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseStatisticsCaseCollection.cpp
|
||||
@@ -203,7 +190,6 @@ ${CEE_CURRENT_LIST_DIR}RimCheckableNamedObject.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimGridTimeHistoryCurve.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimGeometrySelectionItem.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEclipseGeometrySelectionItem.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCompletions.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimEllipseFractureTemplate.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFractureTemplateCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFracture.cpp
|
||||
@@ -216,8 +202,6 @@ ${CEE_CURRENT_LIST_DIR}RimFractureTemplate.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimStimPlanFractureTemplate.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimStimPlanLegendConfig.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimStimPlanColors.cpp
|
||||
|
||||
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
|
||||
@@ -5,13 +5,25 @@ if (${CMAKE_VERSION} VERSION_GREATER "2.8.2")
|
||||
endif()
|
||||
|
||||
set (SOURCE_GROUP_HEADER_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesMultipleSubs.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesMultipleSubs.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesPipeProperties.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPath.h
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPathCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationCollection.h
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationInterval.h
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCompletions.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesMultipleSubs.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesMultipleSubs.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishbonesPipeProperties.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPath.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimFishboneWellPathCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationCollection.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimPerforationInterval.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimWellPathCompletions.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
@@ -55,8 +55,8 @@ public:
|
||||
void setCoordinates(std::vector< cvf::Vec3d > coordinates);
|
||||
void setMeasuredDepths(std::vector< double > measuredDepths);
|
||||
|
||||
std::vector< cvf::Vec3d > coordinates() { return m_coordinates(); }
|
||||
std::vector< double > measuredDepths() { return m_measuredDepths(); }
|
||||
std::vector< cvf::Vec3d > coordinates() const { return m_coordinates(); }
|
||||
std::vector< double > measuredDepths() const { return m_measuredDepths(); }
|
||||
private:
|
||||
QString displayCoordinates() const;
|
||||
|
||||
@@ -45,6 +45,12 @@ RimFishboneWellPathCollection::RimFishboneWellPathCollection()
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&m_wellPaths, "WellPaths", "Well Paths", "", "", "");
|
||||
m_wellPaths.uiCapability()->setUiHidden(true);
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&m_pipeProperties, "PipeProperties", "Pipe Properties", "", "", "");
|
||||
m_pipeProperties.uiCapability()->setUiHidden(true);
|
||||
m_pipeProperties.uiCapability()->setUiTreeHidden(true);
|
||||
m_pipeProperties.uiCapability()->setUiTreeChildrenHidden(true);
|
||||
m_pipeProperties = new RimFishbonesPipeProperties;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -57,6 +63,30 @@ void RimFishboneWellPathCollection::fieldChangedByUi(const caf::PdmFieldHandle*
|
||||
proj->createDisplayModelAndRedrawAllViews();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<const RimFishboneWellPath*> RimFishboneWellPathCollection::wellPaths() const
|
||||
{
|
||||
std::vector<const RimFishboneWellPath*> paths;
|
||||
|
||||
for (const RimFishboneWellPath* path : m_wellPaths)
|
||||
{
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimFishboneWellPathCollection::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering)
|
||||
{
|
||||
caf::PdmUiGroup* wellPropertiesGroup = uiOrdering.addNewGroup("Well Properties");
|
||||
m_pipeProperties->uiOrdering(uiConfigName, *wellPropertiesGroup);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -21,9 +21,11 @@
|
||||
|
||||
#include "RimCheckableNamedObject.h"
|
||||
#include "RimFishboneWellPath.h"
|
||||
#include "RimFishbonesPipeProperties.h"
|
||||
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmChildField.h"
|
||||
#include "cafPdmField.h"
|
||||
|
||||
//==================================================================================================
|
||||
@@ -42,9 +44,16 @@ public:
|
||||
|
||||
void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue);
|
||||
|
||||
std::vector<const RimFishboneWellPath*> wellPaths() const;
|
||||
double holeDiameter() const { return m_pipeProperties->holeDiameter(); }
|
||||
|
||||
protected:
|
||||
virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override;
|
||||
|
||||
private:
|
||||
void appendCompletion(RimFishboneWellPath* completion);
|
||||
|
||||
private:
|
||||
caf::PdmChildArrayField<RimFishboneWellPath*> m_wellPaths;
|
||||
caf::PdmChildField<RimFishbonesPipeProperties*> m_pipeProperties;
|
||||
};
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "RimFishboneWellPathCollection.h"
|
||||
#include "RimFishbonesMultipleSubs.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RimFishbonesCollection, "FishbonesCollection");
|
||||
@@ -68,3 +71,45 @@ void RimFishbonesCollection::fieldChangedByUi(const caf::PdmFieldHandle* changed
|
||||
proj->createDisplayModelAndRedrawAllViews();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimFishbonesCollection::appendFishbonesSubs(RimFishbonesMultipleSubs* subs)
|
||||
{
|
||||
subs->fishbonesColor = nextFishbonesColor();
|
||||
fishbonesSubs.push_back(subs);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Color3f RimFishbonesCollection::nextFishbonesColor() const
|
||||
{
|
||||
RimWellPath* wellPath;
|
||||
firstAncestorOrThisOfType(wellPath);
|
||||
cvf::Color3ub wellPathColor(wellPath->wellPathColor());
|
||||
QColor qWellPathColor = QColor(wellPathColor.r(), wellPathColor.g(), wellPathColor.b());
|
||||
|
||||
if (qWellPathColor.value() == 0)
|
||||
{
|
||||
// If the color is black, using `lighter` or `darker` will not have any effect, since they multiply `value` by a percentage.
|
||||
// In this case, `value` is set specifically to make `lighter`/`darker` possible.
|
||||
qWellPathColor.setHsl(qWellPathColor.hue(), qWellPathColor.saturation(), 25);
|
||||
}
|
||||
|
||||
QColor qFishbonesColor;
|
||||
|
||||
int newIndex = static_cast<int>(fishbonesSubs.size());
|
||||
|
||||
if (qWellPathColor.lightnessF() < 0.5)
|
||||
{
|
||||
qFishbonesColor = qWellPathColor.lighter(150 + 50 * newIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
qFishbonesColor = qWellPathColor.darker(150 + 50 * newIndex);
|
||||
}
|
||||
|
||||
return cvf::Color3f::fromByteColor(qFishbonesColor.red(), qFishbonesColor.green(), qFishbonesColor.blue());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
#include "cafPdmChildArrayField.h"
|
||||
#include "cafPdmChildField.h"
|
||||
#include "cafPdmFieldCvfColor.h"
|
||||
|
||||
#include "cvfColor3.h"
|
||||
|
||||
class RimFishbonesMultipleSubs;
|
||||
class RimFishboneWellPathCollection;
|
||||
@@ -39,6 +42,7 @@ public:
|
||||
RimFishbonesCollection();
|
||||
|
||||
RimFishboneWellPathCollection* wellPathCollection() const;
|
||||
void appendFishbonesSubs(RimFishbonesMultipleSubs* subs);
|
||||
|
||||
caf::PdmChildArrayField<RimFishbonesMultipleSubs*> fishbonesSubs;
|
||||
|
||||
@@ -46,5 +50,7 @@ protected:
|
||||
virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override;
|
||||
|
||||
private:
|
||||
cvf::Color3f nextFishbonesColor() const;
|
||||
|
||||
caf::PdmChildField<RimFishboneWellPathCollection*> m_wellPathCollection;
|
||||
};
|
||||
@@ -59,24 +59,24 @@ RimFishbonesMultipleSubs::RimFishbonesMultipleSubs()
|
||||
{
|
||||
CAF_PDM_InitObject("FishbonesMultipleSubs", ":/Default.png", "", "");
|
||||
|
||||
CAF_PDM_InitField(&fishbonesColor, "FishbonesColor", cvf::Color3f(0.999f, 0.333f, 0.999f), "Fishbones Color", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralCountPerSub, "LateralCountPerSub", size_t(3), "Laterals Per Sub", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralLength, "LateralLength", QString("12.0"), "Length(s) [m]", "", "Specify multiple length values if the sub lengths differ", "");
|
||||
CAF_PDM_InitField(&m_lateralLength, "LateralLength", QString("11.0"), "Length(s) [m]", "", "Specify multiple length values if the sub lengths differ", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralExitAngle, "LateralExitAngle", 35.0, "Exit Angle [deg]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralBuildAngle, "LateralBuildAngle", 5.0, "Build Angle [deg/m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralBuildAngle, "LateralBuildAngle", 6.0, "Build Angle [deg/m]", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralHoleRadius, "LateralHoleRadius", 12.0, "Hole Radius [mm]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralTubingRadius, "LateralTubingRadius", 8.0, "Tubing Radius [mm]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralTubingDiameter, "LateralTubingDiameter", 8.0, "Tubing Diameter [mm]", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralOpenHoleRoghnessFactor, "LateralOpenHoleRoghnessFactor", 0.001, "Open Hole Roghness Factor [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralTubingRoghnessFactor, "LateralTubingRoghnessFactor", 1e-5, "Tubing Roghness Factor [m]", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralLengthFraction, "LateralLengthFraction", 0.8, "Length Fraction [0..1]", "", "", "");
|
||||
CAF_PDM_InitField(&m_lateralInstallFraction, "LateralInstallFraction", 0.7, "Install Fraction [0..1]", "", "", "");
|
||||
CAF_PDM_InitField(&m_skinFactor, "SkinFactor", 1.0, "Skin Factor [0..1]", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_icdCount, "IcdCount", size_t(2), "ICD Count", "", "", "");
|
||||
CAF_PDM_InitField(&m_icdOrificeRadius, "IcdOrificeRadius", 8.0, "ICD Orifice Radius [mm]", "", "", "");
|
||||
CAF_PDM_InitField(&m_icdCount, "IcdCount", size_t(7), "ICD Count", "", "", "");
|
||||
CAF_PDM_InitField(&m_icdOrificeDiameter, "IcdOrificeDiameter", 7.0, "ICD Orifice Diameter [mm]", "", "", "");
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&m_locationOfSubs, "LocationOfSubs", "Measured Depths [m]", "", "", "");
|
||||
m_locationOfSubs.uiCapability()->setUiEditorTypeName(caf::PdmUiListEditor::uiEditorTypeName());
|
||||
@@ -84,7 +84,7 @@ RimFishbonesMultipleSubs::RimFishbonesMultipleSubs()
|
||||
CAF_PDM_InitField(&m_subsLocationMode, "SubsLocationMode", caf::AppEnum<LocationType>(FB_SUB_COUNT_END), "Location Defined By", "", "", "");
|
||||
CAF_PDM_InitField(&m_rangeStart, "RangeStart", 100.0, "Start MD [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_rangeEnd, "RangeEnd", 250.0, "End MD [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_rangeSubSpacing, "RangeSubSpacing", 40.0, "Spacing [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_rangeSubSpacing, "RangeSubSpacing", 13.0, "Spacing [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_rangeSubCount, "RangeSubCount", size_t(25), "Number of Subs", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_subsOrientationMode, "SubsOrientationMode", caf::AppEnum<LateralsOrientationType>(FB_LATERAL_ORIENTATION_RANDOM), "Orientation", "", "", "");
|
||||
@@ -93,6 +93,12 @@ RimFishbonesMultipleSubs::RimFishbonesMultipleSubs()
|
||||
m_installationRotationAngles.uiCapability()->setUiHidden(true);
|
||||
CAF_PDM_InitField(&m_fixedInstallationRotationAngle, "FixedInstallationRotationAngle", 0.0, " Fixed Angle [deg]", "", "", "");
|
||||
|
||||
CAF_PDM_InitFieldNoDefault(&m_pipeProperties, "PipeProperties", "Pipe Properties", "", "", "");
|
||||
m_pipeProperties.uiCapability()->setUiHidden(true);
|
||||
m_pipeProperties.uiCapability()->setUiTreeChildrenHidden(true);
|
||||
|
||||
m_pipeProperties = new RimFishbonesPipeProperties;
|
||||
|
||||
m_name.uiCapability()->setUiReadOnly(true);
|
||||
|
||||
m_rigFishbonesGeometry = std::unique_ptr<RigFisbonesGeometry>(new RigFisbonesGeometry(this));
|
||||
@@ -165,9 +171,9 @@ double RimFishbonesMultipleSubs::buildAngle() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimFishbonesMultipleSubs::tubingRadius() const
|
||||
double RimFishbonesMultipleSubs::tubingDiameter() const
|
||||
{
|
||||
return m_lateralTubingRadius;
|
||||
return m_lateralTubingDiameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -252,6 +258,13 @@ void RimFishbonesMultipleSubs::fieldChangedByUi(const caf::PdmFieldHandle* chang
|
||||
recomputeLocations = true;
|
||||
}
|
||||
|
||||
if (changedField == &m_rangeSubSpacing &&
|
||||
m_rangeSubSpacing() < 13.0)
|
||||
{
|
||||
// Minimum distance between fishbones is 13m
|
||||
m_rangeSubSpacing = 13.0;
|
||||
}
|
||||
|
||||
if (recomputeLocations)
|
||||
{
|
||||
computeRangesAndLocations();
|
||||
@@ -323,6 +336,12 @@ void RimFishbonesMultipleSubs::computeRangesAndLocations()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimFishbonesMultipleSubs::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering)
|
||||
{
|
||||
{
|
||||
caf::PdmUiGroup* group = uiOrdering.addNewGroup("Appearance");
|
||||
|
||||
group->add(&fishbonesColor);
|
||||
}
|
||||
|
||||
{
|
||||
caf::PdmUiGroup* group = uiOrdering.addNewGroup("Location");
|
||||
|
||||
@@ -362,12 +381,6 @@ void RimFishbonesMultipleSubs::defineUiOrdering(QString uiConfigName, caf::PdmUi
|
||||
lateralConfigGroup->add(&m_fixedInstallationRotationAngle);
|
||||
}
|
||||
|
||||
{
|
||||
caf::PdmUiGroup* wellGroup = lateralConfigGroup->addNewGroup("Well Properties");
|
||||
|
||||
wellGroup->add(&m_lateralHoleRadius);
|
||||
wellGroup->add(&m_skinFactor);
|
||||
}
|
||||
|
||||
{
|
||||
caf::PdmUiGroup* successGroup = lateralConfigGroup->addNewGroup("Installation Success Fractions");
|
||||
@@ -378,14 +391,20 @@ void RimFishbonesMultipleSubs::defineUiOrdering(QString uiConfigName, caf::PdmUi
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
caf::PdmUiGroup* wellGroup = uiOrdering.addNewGroup("Well Properties");
|
||||
|
||||
m_pipeProperties->uiOrdering(uiConfigName, *wellGroup);
|
||||
}
|
||||
|
||||
{
|
||||
caf::PdmUiGroup* mswGroup = uiOrdering.addNewGroup("Multi Segment Wells");
|
||||
mswGroup->setCollapsedByDefault(true);
|
||||
mswGroup->add(&m_lateralTubingRadius);
|
||||
mswGroup->add(&m_lateralTubingDiameter);
|
||||
mswGroup->add(&m_lateralOpenHoleRoghnessFactor);
|
||||
mswGroup->add(&m_lateralTubingRoghnessFactor);
|
||||
mswGroup->add(&m_icdCount);
|
||||
mswGroup->add(&m_icdOrificeRadius);
|
||||
mswGroup->add(&m_icdOrificeDiameter);
|
||||
}
|
||||
|
||||
// Visibility
|
||||
@@ -20,9 +20,15 @@
|
||||
|
||||
#include "RimCheckableNamedObject.h"
|
||||
#include "Rim3dPropertiesInterface.h"
|
||||
#include "RimFishbonesPipeProperties.h"
|
||||
|
||||
#include "cvfBase.h"
|
||||
#include "cvfVector3.h"
|
||||
#include "cvfColor3.h"
|
||||
|
||||
// Include to make Pdm work for cvf::Color
|
||||
#include "cafPdmFieldCvfColor.h"
|
||||
#include "cafPdmChildField.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -64,8 +70,8 @@ public:
|
||||
double exitAngle() const;
|
||||
double buildAngle() const;
|
||||
|
||||
double tubingRadius() const;
|
||||
double holeRadius() const { return m_lateralHoleRadius(); }
|
||||
double tubingDiameter() const;
|
||||
double holeDiameter() const { return m_pipeProperties()->holeDiameter(); }
|
||||
double openHoleRoughnessFactor() const { return m_lateralOpenHoleRoghnessFactor(); }
|
||||
double lateralCountPerSub() const;
|
||||
std::vector<double> lateralLengths() const;
|
||||
@@ -76,6 +82,9 @@ public:
|
||||
// Override from Rim3dPropertiesInterface
|
||||
virtual cvf::BoundingBox boundingBoxInDomainCoords() override;
|
||||
|
||||
public:
|
||||
caf::PdmField<cvf::Color3f> fishbonesColor;
|
||||
|
||||
protected:
|
||||
virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override;
|
||||
|
||||
@@ -97,10 +106,7 @@ private:
|
||||
caf::PdmField<double> m_lateralExitAngle;
|
||||
caf::PdmField<double> m_lateralBuildAngle;
|
||||
|
||||
caf::PdmField<double> m_skinFactor;
|
||||
|
||||
caf::PdmField<double> m_lateralHoleRadius;
|
||||
caf::PdmField<double> m_lateralTubingRadius;
|
||||
caf::PdmField<double> m_lateralTubingDiameter;
|
||||
|
||||
caf::PdmField<double> m_lateralOpenHoleRoghnessFactor;
|
||||
caf::PdmField<double> m_lateralTubingRoghnessFactor;
|
||||
@@ -109,7 +115,7 @@ private:
|
||||
caf::PdmField<double> m_lateralInstallFraction;
|
||||
|
||||
caf::PdmField<size_t> m_icdCount;
|
||||
caf::PdmField<double> m_icdOrificeRadius;
|
||||
caf::PdmField<double> m_icdOrificeDiameter;
|
||||
|
||||
caf::PdmField<caf::AppEnum<LocationType> > m_subsLocationMode;
|
||||
caf::PdmField<double> m_rangeStart;
|
||||
@@ -124,5 +130,7 @@ private:
|
||||
caf::PdmField<std::vector<double>> m_installationRotationAngles;
|
||||
caf::PdmField<double> m_fixedInstallationRotationAngle;
|
||||
|
||||
caf::PdmChildField<RimFishbonesPipeProperties*> m_pipeProperties;
|
||||
|
||||
std::unique_ptr<RigFisbonesGeometry> m_rigFishbonesGeometry;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimFishbonesPipeProperties.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RimFishbonesPipeProperties, "FishbonesPipeProperties");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimFishbonesPipeProperties::RimFishbonesPipeProperties()
|
||||
{
|
||||
CAF_PDM_InitObject("FishbonesPipeProperties", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&m_lateralHoleDiameter, "LateralHoleDiameter", 12.5, "Hole Diameter [mm]", "", "", "");
|
||||
CAF_PDM_InitField(&m_skinFactor, "SkinFactor", 1.0, "Skin Factor [0..1]", "", "", "");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimFishbonesPipeProperties::~RimFishbonesPipeProperties()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimFishbonesPipeProperties::defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering & uiOrdering)
|
||||
{
|
||||
uiOrdering.add(&m_lateralHoleDiameter);
|
||||
uiOrdering.add(&m_skinFactor);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfBase.h"
|
||||
#include "cvfVector3.h"
|
||||
#include "cvfColor3.h"
|
||||
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafPdmField.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimFishbonesPipeProperties : public caf::PdmObject
|
||||
{
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
RimFishbonesPipeProperties();
|
||||
virtual ~RimFishbonesPipeProperties();
|
||||
|
||||
double skinFactor() const { return m_skinFactor(); }
|
||||
double holeDiameter() const { return m_lateralHoleDiameter(); }
|
||||
|
||||
protected:
|
||||
virtual void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override;
|
||||
|
||||
private:
|
||||
caf::PdmField<double> m_skinFactor;
|
||||
caf::PdmField<double> m_lateralHoleDiameter;
|
||||
};
|
||||
@@ -83,3 +83,18 @@ void RimPerforationCollection::appendPerforation(RimPerforationInterval* perfora
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<const RimPerforationInterval*> RimPerforationCollection::perforations() const
|
||||
{
|
||||
std::vector<const RimPerforationInterval*> myPerforations;
|
||||
|
||||
for (auto perforation : m_perforations)
|
||||
{
|
||||
myPerforations.push_back(perforation);
|
||||
}
|
||||
|
||||
return myPerforations;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,12 @@ public:
|
||||
~RimPerforationCollection();
|
||||
|
||||
void appendPerforation(RimPerforationInterval* perforation);
|
||||
std::vector<const RimPerforationInterval*> perforations() const;
|
||||
|
||||
void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue);
|
||||
|
||||
friend class RiuEditPerforationCollectionWidget;
|
||||
|
||||
private:
|
||||
caf::PdmChildArrayField<RimPerforationInterval*> m_perforations;
|
||||
};
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "cafPdmUiListEditor.h"
|
||||
#include "cafPdmUiTextEditor.h"
|
||||
#include "cafPdmUiLineEditor.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RimPerforationInterval, "Perforation");
|
||||
|
||||
@@ -38,8 +39,11 @@ RimPerforationInterval::RimPerforationInterval()
|
||||
|
||||
CAF_PDM_InitField(&m_startMD, "StartMeasuredDepth", 0.0, "Start MD [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_endMD, "EndMeasuredDepth", 0.0, "End MD [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_radius, "Radius", 0.0, "Radius [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_diameter, "Diameter", 0.0, "Diameter [m]", "", "", "");
|
||||
CAF_PDM_InitField(&m_skinFactor, "SkinFactor", 0.0, "Skin Factor", "", "", "");
|
||||
CAF_PDM_InitField(&m_startOfHistory, "StartOfHistory", true, "Start of History", "", "", "");
|
||||
CAF_PDM_InitFieldNoDefault(&m_date, "StartDate", "Start Date", "", "", "");
|
||||
m_date.uiCapability()->setUiEditorTypeName(caf::PdmUiLineEditor::uiEditorTypeName());
|
||||
|
||||
m_name.uiCapability()->setUiReadOnly(true);
|
||||
}
|
||||
@@ -60,6 +64,53 @@ void RimPerforationInterval::setStartAndEndMD(double startMD, double endMD)
|
||||
m_endMD = endMD;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimPerforationInterval::setStartOfHistory()
|
||||
{
|
||||
m_startOfHistory = true;
|
||||
|
||||
m_date.uiCapability()->setUiReadOnly(m_startOfHistory());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimPerforationInterval::setDate(const QDate& date)
|
||||
{
|
||||
m_startOfHistory = false;
|
||||
m_date = QDateTime(date);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimPerforationInterval::setDiameter(double diameter)
|
||||
{
|
||||
m_diameter = diameter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimPerforationInterval::setSkinFactor(double skinFactor)
|
||||
{
|
||||
m_skinFactor = skinFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimPerforationInterval::isActiveOnDate(const QDateTime& date) const
|
||||
{
|
||||
if (m_startOfHistory())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return m_date() < date;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -88,6 +139,11 @@ void RimPerforationInterval::fieldChangedByUi(const caf::PdmFieldHandle* changed
|
||||
RimProject* proj;
|
||||
this->firstAncestorOrThisOfTypeAsserted(proj);
|
||||
proj->createDisplayModelAndRedrawAllViews();
|
||||
|
||||
if (changedField == &m_startOfHistory)
|
||||
{
|
||||
m_date.uiCapability()->setUiReadOnly(m_startOfHistory());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -105,8 +161,10 @@ void RimPerforationInterval::defineUiOrdering(QString uiConfigName, caf::PdmUiOr
|
||||
{
|
||||
uiOrdering.add(&m_startMD);
|
||||
uiOrdering.add(&m_endMD);
|
||||
uiOrdering.add(&m_radius);
|
||||
uiOrdering.add(&m_diameter);
|
||||
uiOrdering.add(&m_skinFactor);
|
||||
uiOrdering.add(&m_startOfHistory);
|
||||
uiOrdering.add(&m_date);
|
||||
|
||||
uiOrdering.skipRemainingFields();
|
||||
}
|
||||
@@ -25,9 +25,10 @@
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
#include <QDate>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
///
|
||||
//==================================================================================================
|
||||
class RimPerforationInterval : public RimCheckableNamedObject, public Rim3dPropertiesInterface
|
||||
{
|
||||
@@ -38,8 +39,16 @@ public:
|
||||
virtual ~RimPerforationInterval();
|
||||
|
||||
void setStartAndEndMD(double startMD, double endMD);
|
||||
double startMD() { return m_startMD(); }
|
||||
double endMD() { return m_endMD(); }
|
||||
void setStartOfHistory();
|
||||
void setDate(const QDate& date);
|
||||
void setDiameter(double diameter);
|
||||
void setSkinFactor(double skinFactor);
|
||||
double startMD() const { return m_startMD(); }
|
||||
double endMD() const { return m_endMD(); }
|
||||
double diameter() const { return m_diameter(); }
|
||||
double skinFactor() const { return m_skinFactor(); }
|
||||
|
||||
bool isActiveOnDate(const QDateTime& date) const;
|
||||
|
||||
virtual cvf::BoundingBox boundingBoxInDomainCoords() override;
|
||||
|
||||
@@ -51,6 +60,8 @@ protected:
|
||||
private:
|
||||
caf::PdmField< double > m_startMD;
|
||||
caf::PdmField< double > m_endMD;
|
||||
caf::PdmField< double > m_radius;
|
||||
caf::PdmField< double > m_diameter;
|
||||
caf::PdmField< double > m_skinFactor;
|
||||
caf::PdmField< bool > m_startOfHistory;
|
||||
caf::PdmField< QDateTime > m_date;
|
||||
};
|
||||
@@ -141,14 +141,16 @@ QList<caf::PdmOptionItemInfo> RimFlowCharacteristicsPlot::calculateValueOptions(
|
||||
{
|
||||
std::vector<RimEclipseResultCase*> cases;
|
||||
proj->descendantsIncludingThisOfType(cases);
|
||||
|
||||
RimEclipseResultCase* defaultCase = nullptr;
|
||||
for ( RimEclipseResultCase* c : cases )
|
||||
{
|
||||
if ( c->defaultFlowDiagSolution() )
|
||||
{
|
||||
options.push_back(caf::PdmOptionItemInfo(c->caseUserDescription(), c, false, c->uiIcon()));
|
||||
if (!defaultCase) defaultCase = c; // Select first
|
||||
}
|
||||
}
|
||||
if (!m_case() && defaultCase) m_case = defaultCase;
|
||||
}
|
||||
}
|
||||
else if ( fieldNeedingOptions == &m_flowDiagSolution )
|
||||
|
||||
@@ -357,29 +357,46 @@ std::map<QString, const std::vector<double> *> RimWellAllocationPlot::findReleva
|
||||
void RimWellAllocationPlot::updateWellFlowPlotXAxisTitle(RimWellLogTrack* plotTrack)
|
||||
{
|
||||
RigEclipseCaseData::UnitsType unitSet = m_case->eclipseCaseData()->unitsType();
|
||||
QString unitText;
|
||||
switch ( unitSet )
|
||||
{
|
||||
case RigEclipseCaseData::UNITS_METRIC:
|
||||
unitText = "[m^3/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_FIELD:
|
||||
unitText = "[Brl/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_LAB:
|
||||
unitText = "[cm^3/hr]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (m_flowDiagSolution)
|
||||
{
|
||||
QString unitText;
|
||||
switch ( unitSet )
|
||||
{
|
||||
case RigEclipseCaseData::UNITS_METRIC:
|
||||
unitText = "[m<sup>3</sup>/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_FIELD:
|
||||
unitText = "[Brl/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>/hr]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
plotTrack->setXAxisTitle("Reservoir Flow Rate " + unitText);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString unitText;
|
||||
switch ( unitSet )
|
||||
{
|
||||
case RigEclipseCaseData::UNITS_METRIC:
|
||||
unitText = "[Liquid Sm<sup>3</sup>/day], [Gas kSm<sup>3</sup>/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_FIELD:
|
||||
unitText = "[Liquid BBL/day], [Gas BOE/day]";
|
||||
break;
|
||||
case RigEclipseCaseData::UNITS_LAB:
|
||||
unitText = "[cm<sup>3</sup>/hr]";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
plotTrack->setXAxisTitle("Surface Flow Rate " + unitText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +437,8 @@ QStringList RimContextCommandBuilder::commandsFromSelection()
|
||||
commandIds << "RicNewPerforationIntervalFeature";
|
||||
commandIds << "RicEditPerforationCollectionFeature";
|
||||
commandIds << "RicExportFishbonesLateralsFeature";
|
||||
commandIds << "RicExportFishbonesWellSegmentsFeature";
|
||||
commandIds << "RicWellPathImportPerforationIntervalsFeature";
|
||||
commandIds << "RicWellPathExportCompletionDataFeature";
|
||||
commandIds << "RicWellPathImportCompletionsFileFeature";
|
||||
commandIds << "RicFlyToObjectFeature";
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RimExportCompletionDataSettings.h"
|
||||
|
||||
CAF_PDM_SOURCE_INIT(RimExportCompletionDataSettings, "RimExportCompletionDataSettings");
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimExportCompletionDataSettings::RimExportCompletionDataSettings()
|
||||
{
|
||||
CAF_PDM_InitObject("RimExportCompletionDataSettings", "", "", "");
|
||||
|
||||
CAF_PDM_InitField(&includeWpimult, "IncludeWPIMULT", true, "Include WPIMLUT", "", "", "");
|
||||
CAF_PDM_InitField(&removeLateralsInMainBoreCells, "RemoveLateralsInMainBoreCells", false, "Remove Laterals in Main Bore Cells", "", "", "");
|
||||
}
|
||||
@@ -172,6 +172,16 @@ RimFishbonesCollection* RimWellPath::fishbonesCollection()
|
||||
return m_completions->fishbonesCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RimFishbonesCollection * RimWellPath::fishbonesCollection() const
|
||||
{
|
||||
CVF_ASSERT(m_completions);
|
||||
|
||||
return m_completions->fishbonesCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -182,6 +192,16 @@ RimPerforationCollection* RimWellPath::perforationIntervalCollection()
|
||||
return m_completions->perforationCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const RimPerforationCollection* RimWellPath::perforationIntervalCollection() const
|
||||
{
|
||||
CVF_ASSERT(m_completions);
|
||||
|
||||
return m_completions->perforationCollection();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -78,7 +78,9 @@ public:
|
||||
caf::PdmChildField<RimWellLogFile*> m_wellLogFile;
|
||||
|
||||
RimFishbonesCollection* fishbonesCollection();
|
||||
const RimFishbonesCollection* fishbonesCollection() const;
|
||||
RimPerforationCollection* perforationIntervalCollection();
|
||||
const RimPerforationCollection* perforationIntervalCollection() const;
|
||||
RimWellPathFractureCollection* fractureCollection();
|
||||
|
||||
RigWellPath* wellPathGeometry();
|
||||
|
||||
@@ -55,19 +55,18 @@ public:
|
||||
|
||||
void setupCurveLook(RimSummaryCurve* curve);
|
||||
|
||||
private:
|
||||
|
||||
static cvf::Color3f cycledPaletteColor(int colorIndex);
|
||||
static cvf::Color3f cycledNoneRGBBrColor(int colorIndex);
|
||||
static cvf::Color3f cycledGreenColor(int colorIndex);
|
||||
static cvf::Color3f cycledBlueColor(int colorIndex);
|
||||
static cvf::Color3f cycledRedColor(int colorIndex);
|
||||
static cvf::Color3f cycledBrownColor(int colorIndex);
|
||||
|
||||
private:
|
||||
void setOneCurveAppearance(CurveAppearanceType appeaType, size_t totalCount, int appeaIdx, RimSummaryCurve* curve);
|
||||
void updateApperanceIndices();
|
||||
std::map<std::string, size_t> mapNameToAppearanceIndex(CurveAppearanceType & appearance, const std::set<std::string>& names);
|
||||
|
||||
cvf::Color3f cycledPaletteColor(int colorIndex);
|
||||
cvf::Color3f cycledNoneRGBBrColor(int colorIndex);
|
||||
cvf::Color3f cycledGreenColor(int colorIndex);
|
||||
cvf::Color3f cycledBlueColor(int colorIndex);
|
||||
cvf::Color3f cycledRedColor(int colorIndex);
|
||||
cvf::Color3f cycledBrownColor(int colorIndex);
|
||||
|
||||
RimPlotCurve::LineStyleEnum cycledLineStyle(int index);
|
||||
RimPlotCurve::PointSymbolEnum cycledSymbol(int index);
|
||||
|
||||
@@ -17,6 +17,7 @@ ${CEE_CURRENT_LIST_DIR}RigActiveCellsResultAccessor.h
|
||||
${CEE_CURRENT_LIST_DIR}RigCellEdgeResultAccessor.h
|
||||
${CEE_CURRENT_LIST_DIR}RigCombTransResultAccessor.h
|
||||
${CEE_CURRENT_LIST_DIR}RigCombMultResultAccessor.h
|
||||
${CEE_CURRENT_LIST_DIR}RigCompletionData.h
|
||||
${CEE_CURRENT_LIST_DIR}RigResultModifier.h
|
||||
${CEE_CURRENT_LIST_DIR}RigResultModifierFactory.h
|
||||
${CEE_CURRENT_LIST_DIR}RigFormationNames.h
|
||||
@@ -48,6 +49,7 @@ ${CEE_CURRENT_LIST_DIR}RigEclipseNativeVisibleCellsStatCalc.h
|
||||
${CEE_CURRENT_LIST_DIR}RigEclipseMultiPropertyStatCalc.h
|
||||
${CEE_CURRENT_LIST_DIR}RigEclipseToStimPlanCellTransmissibilityCalculator.h
|
||||
${CEE_CURRENT_LIST_DIR}RigWellLogCurveData.h
|
||||
${CEE_CURRENT_LIST_DIR}RigWellLogExtractionTools.h
|
||||
${CEE_CURRENT_LIST_DIR}RigTimeHistoryResultAccessor.h
|
||||
${CEE_CURRENT_LIST_DIR}RigCurveDataTools.h
|
||||
${CEE_CURRENT_LIST_DIR}RigSummaryCaseData.h
|
||||
@@ -64,10 +66,7 @@ ${CEE_CURRENT_LIST_DIR}RigStimPlanFractureDefinition.h
|
||||
${CEE_CURRENT_LIST_DIR}RigStimPlanUpscalingCalc.h
|
||||
${CEE_CURRENT_LIST_DIR}RigFractureGrid.h
|
||||
${CEE_CURRENT_LIST_DIR}RigFractureCell.h
|
||||
|
||||
|
||||
|
||||
|
||||
${CEE_CURRENT_LIST_DIR}RigWellPathIntersectionTools.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
@@ -83,6 +82,7 @@ ${CEE_CURRENT_LIST_DIR}RigActiveCellsResultAccessor.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigCellEdgeResultAccessor.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigCombTransResultAccessor.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigCombMultResultAccessor.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigCompletionData.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigResultModifierFactory.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigFormationNames.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigFlowDiagResultAddress.cpp
|
||||
@@ -126,7 +126,7 @@ ${CEE_CURRENT_LIST_DIR}RigStimPlanFractureDefinition.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigStimPlanUpscalingCalc.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigFractureGrid.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigFractureCell.cpp
|
||||
|
||||
${CEE_CURRENT_LIST_DIR}RigWellPathIntersectionTools.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
|
||||
@@ -74,7 +74,6 @@ public:
|
||||
const std::vector<double>& pseudoLengthFromTop(size_t branchIdx) const;
|
||||
const std::vector<double>& trueVerticalDepth(size_t branchIdx) const;
|
||||
const std::vector<double>& accumulatedTracerFlowPrPseudoLength(const QString& tracerName, size_t branchIdx) const;
|
||||
const std::vector<double>& flowPrPseudoLength( size_t branchIdx) const;
|
||||
const std::vector<double>& tracerFlowPrPseudoLength(const QString& tracerName, size_t branchIdx) const;
|
||||
|
||||
|
||||
|
||||
197
ApplicationCode/ReservoirDataModel/RigCompletionData.cpp
Normal file
197
ApplicationCode/ReservoirDataModel/RigCompletionData.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigCompletionData.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include <QString>
|
||||
#include <cmath> // Needed for HUGE_VAL on linux
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::RigCompletionData(const QString wellName, const IJKCellIndex cellIndex)
|
||||
: m_wellName(wellName),
|
||||
m_cellIndex(cellIndex),
|
||||
m_saturation(HUGE_VAL),
|
||||
m_transmissibility(HUGE_VAL),
|
||||
m_diameter(HUGE_VAL),
|
||||
m_kh(HUGE_VAL),
|
||||
m_skinFactor(HUGE_VAL),
|
||||
m_dFactor(HUGE_VAL),
|
||||
m_direction(DIR_UNDEF),
|
||||
m_connectionState(OPEN),
|
||||
m_count(1)
|
||||
{
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::~RigCompletionData()
|
||||
{
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData::RigCompletionData(const RigCompletionData& other)
|
||||
{
|
||||
copy(*this, other);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData RigCompletionData::combine(const RigCompletionData& first, const RigCompletionData& second)
|
||||
{
|
||||
RigCompletionData result(first);
|
||||
CVF_ASSERT(result.m_wellName == second.m_wellName);
|
||||
CVF_ASSERT(result.m_cellIndex == second.m_cellIndex);
|
||||
|
||||
if (onlyOneIsDefaulted(result.m_transmissibility, second.m_transmissibility))
|
||||
{
|
||||
RiaLogging::error("Transmissibility defaulted in one but not both, will produce erroneous result");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.m_transmissibility += second.m_transmissibility;
|
||||
}
|
||||
|
||||
result.m_metadata.reserve(result.m_metadata.size() + second.m_metadata.size());
|
||||
result.m_metadata.insert(result.m_metadata.end(), second.m_metadata.begin(), second.m_metadata.end());
|
||||
|
||||
result.m_count += second.m_count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
bool RigCompletionData::operator<(const RigCompletionData& other) const
|
||||
{
|
||||
if (m_wellName < other.m_wellName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return m_cellIndex < other.m_cellIndex;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
RigCompletionData& RigCompletionData::operator=(const RigCompletionData& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
copy(*this, other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setFromFracture(double transmissibility, double skinFactor)
|
||||
{
|
||||
m_transmissibility = transmissibility;
|
||||
m_skinFactor = skinFactor;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setFromFishbone(double diameter, CellDirection direction)
|
||||
{
|
||||
m_diameter = diameter;
|
||||
m_direction = direction;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::setFromPerforation(double diameter, CellDirection direction)
|
||||
{
|
||||
m_diameter = diameter;
|
||||
m_direction = direction;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::addMetadata(const QString& name, const QString& comment)
|
||||
{
|
||||
m_metadata.push_back(RigCompletionMetaData(name, comment));
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
bool RigCompletionData::isDefaultValue(double val)
|
||||
{
|
||||
return val == HUGE_VAL;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
bool RigCompletionData::onlyOneIsDefaulted(double first, double second)
|
||||
{
|
||||
if (first == HUGE_VAL)
|
||||
{
|
||||
if (second == HUGE_VAL)
|
||||
{
|
||||
// Both have default values
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First has default value, second does not
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (second == HUGE_VAL)
|
||||
{
|
||||
// Second has default value, first does not
|
||||
return true;
|
||||
}
|
||||
|
||||
// Neither has default values
|
||||
return false;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
void RigCompletionData::copy(RigCompletionData& target, const RigCompletionData& from)
|
||||
{
|
||||
target.m_metadata = from.m_metadata;
|
||||
target.m_wellName = from.m_wellName;
|
||||
target.m_cellIndex = from.m_cellIndex;
|
||||
target.m_connectionState = from.m_connectionState;
|
||||
target.m_saturation = from.m_saturation;
|
||||
target.m_transmissibility = from.m_transmissibility;
|
||||
target.m_diameter = from.m_diameter;
|
||||
target.m_kh = from.m_kh;
|
||||
target.m_skinFactor = from.m_skinFactor;
|
||||
target.m_dFactor = from.m_dFactor;
|
||||
target.m_direction = from.m_direction;
|
||||
target.m_count = from.m_count;
|
||||
}
|
||||
141
ApplicationCode/ReservoirDataModel/RigCompletionData.h
Normal file
141
ApplicationCode/ReservoirDataModel/RigCompletionData.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfBase.h"
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <QString>
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum WellConnectionState {
|
||||
OPEN,
|
||||
SHUT,
|
||||
AUTO,
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum CellDirection {
|
||||
DIR_I,
|
||||
DIR_J,
|
||||
DIR_K,
|
||||
DIR_UNDEF,
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class IJKCellIndex {
|
||||
public:
|
||||
IJKCellIndex() {};
|
||||
IJKCellIndex(size_t i, size_t j, size_t k) : i(i), j(j), k(k) {};
|
||||
IJKCellIndex(const IJKCellIndex& other)
|
||||
{
|
||||
i = other.i;
|
||||
j = other.j;
|
||||
k = other.k;
|
||||
}
|
||||
|
||||
bool operator==(const IJKCellIndex& other) const
|
||||
{
|
||||
return i == other.i && j == other.j && k == other.k;
|
||||
}
|
||||
|
||||
bool operator<(const IJKCellIndex& other) const
|
||||
{
|
||||
if (i != other.i) return i < other.i;
|
||||
if (j != other.j) return j < other.j;
|
||||
if (k != other.k) return k < other.k;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
size_t j;
|
||||
size_t k;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
struct RigCompletionMetaData {
|
||||
RigCompletionMetaData(const QString& name, const QString& comment) : name(name), comment(comment) {}
|
||||
|
||||
QString name;
|
||||
QString comment;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigCompletionData : public cvf::Object
|
||||
{
|
||||
public:
|
||||
RigCompletionData(const QString wellName, const IJKCellIndex cellIndex);
|
||||
~RigCompletionData();
|
||||
RigCompletionData(const RigCompletionData& other);
|
||||
|
||||
static RigCompletionData combine(const RigCompletionData& first, const RigCompletionData& second);
|
||||
|
||||
bool operator<(const RigCompletionData& other) const;
|
||||
RigCompletionData& operator=(const RigCompletionData& other);
|
||||
|
||||
void setFromFracture(double transmissibility, double skinFactor);
|
||||
void setFromFishbone(double diameter, CellDirection direction);
|
||||
void setFromPerforation(double diameter, CellDirection direction);
|
||||
void addMetadata(const QString& name, const QString& comment);
|
||||
static bool isDefaultValue(double val);
|
||||
|
||||
const std::vector<RigCompletionMetaData>& metadata() const { return m_metadata; }
|
||||
const QString& wellName() const { return m_wellName; }
|
||||
const IJKCellIndex& cellIndex() const { return m_cellIndex; }
|
||||
WellConnectionState connectionState() const { return m_connectionState; }
|
||||
double saturation() const { return m_saturation; }
|
||||
double transmissibility() const { return m_transmissibility; }
|
||||
double diameter() const { return m_diameter; }
|
||||
double kh() const { return m_kh; }
|
||||
double skinFactor() const { return m_skinFactor; }
|
||||
double dFactor() const { return m_dFactor; }
|
||||
CellDirection direction() const { return m_direction; }
|
||||
size_t count() const { return m_count; }
|
||||
|
||||
private:
|
||||
std::vector<RigCompletionMetaData> m_metadata;
|
||||
QString m_wellName;
|
||||
IJKCellIndex m_cellIndex;
|
||||
WellConnectionState m_connectionState;
|
||||
double m_saturation;
|
||||
double m_transmissibility;
|
||||
double m_diameter;
|
||||
double m_kh;
|
||||
double m_skinFactor;
|
||||
double m_dFactor;
|
||||
CellDirection m_direction;
|
||||
|
||||
// Number of parts that have contributed to this completion
|
||||
size_t m_count;
|
||||
|
||||
private:
|
||||
static bool onlyOneIsDefaulted(double first, double second);
|
||||
static void copy(RigCompletionData& target, const RigCompletionData& from);
|
||||
};
|
||||
@@ -21,7 +21,7 @@ namespace RigFlowDiagInterfaceTools {
|
||||
template <class FluxCalc>
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField(const Opm::ECLGraph& G,
|
||||
FluxCalc&& getFlux)
|
||||
FluxCalc&& getFlux)
|
||||
{
|
||||
using ConnVals = Opm::FlowDiagnostics::ConnectionValues;
|
||||
|
||||
@@ -52,24 +52,11 @@ namespace RigFlowDiagInterfaceTools {
|
||||
}
|
||||
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField(const Opm::ECLGraph& G,
|
||||
const Opm::ECLRestartData& rstrt,
|
||||
const bool compute_fluxes)
|
||||
extractFluxFieldFromRestartFile(const Opm::ECLGraph& G,
|
||||
const Opm::ECLRestartData& rstrt)
|
||||
{
|
||||
if (compute_fluxes) {
|
||||
Opm::ECLFluxCalc calc(G);
|
||||
|
||||
auto getFlux = [&calc, &rstrt]
|
||||
(const Opm::ECLGraph::PhaseIndex p)
|
||||
{
|
||||
return calc.flux(rstrt, p);
|
||||
};
|
||||
|
||||
return extractFluxField(G, getFlux);
|
||||
}
|
||||
|
||||
auto getFlux = [&G, &rstrt]
|
||||
(const Opm::ECLGraph::PhaseIndex p)
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
return G.flux(rstrt, p);
|
||||
};
|
||||
|
||||
@@ -251,9 +251,8 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
Opm::FlowDiagnostics::CellSetValues sumWellFluxPrCell;
|
||||
|
||||
{
|
||||
Opm::FlowDiagnostics::ConnectionValues connectionsVals = RigFlowDiagInterfaceTools::extractFluxField(*(m_opmFlowDiagStaticData->m_eclGraph),
|
||||
*currentRestartData,
|
||||
false);
|
||||
Opm::FlowDiagnostics::ConnectionValues connectionsVals = RigFlowDiagInterfaceTools::extractFluxFieldFromRestartFile(*(m_opmFlowDiagStaticData->m_eclGraph),
|
||||
*currentRestartData);
|
||||
|
||||
m_opmFlowDiagStaticData->m_fldToolbox->assignConnectionFlux(connectionsVals);
|
||||
|
||||
@@ -400,7 +399,8 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
{
|
||||
Graph flowCapStorCapCurve = flowCapacityStorageCapacityCurve(*(injectorSolution.get()),
|
||||
*(producerSolution.get()),
|
||||
m_opmFlowDiagStaticData->m_poreVolume);
|
||||
m_opmFlowDiagStaticData->m_poreVolume,
|
||||
0.1);
|
||||
|
||||
result.setFlowCapStorageCapCurve(flowCapStorCapCurve);
|
||||
result.setSweepEfficiencyCurve(sweepEfficiency(flowCapStorCapCurve));
|
||||
|
||||
@@ -91,10 +91,12 @@ struct RigHexIntersector
|
||||
return intersectionCount;
|
||||
}
|
||||
|
||||
static bool isPointInCell(const cvf::Vec3d point, const cvf::Vec3d hexCorners[8], const size_t hexIndex)
|
||||
static bool isPointInCell(const cvf::Vec3d point, const cvf::Vec3d hexCorners[8])
|
||||
{
|
||||
cvf::Ray ray;
|
||||
ray.setOrigin(point);
|
||||
size_t intersections = 0;
|
||||
|
||||
for (int face = 0; face < 6; ++face)
|
||||
{
|
||||
cvf::ubyte faceVertexIndices[4];
|
||||
@@ -106,11 +108,11 @@ struct RigHexIntersector
|
||||
int next = i < 3 ? i + 1 : 0;
|
||||
if (ray.triangleIntersect(hexCorners[faceVertexIndices[i]], hexCorners[faceVertexIndices[next]], faceCenter))
|
||||
{
|
||||
return true;
|
||||
++intersections;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return intersections % 2 == 1;
|
||||
}
|
||||
|
||||
static bool isEqualDepth(double d1, double d2)
|
||||
|
||||
@@ -58,7 +58,7 @@ double RigWellPath::datumElevation() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigWellPath::interpolatedPointAlongWellPath(double measuredDepth)
|
||||
cvf::Vec3d RigWellPath::interpolatedPointAlongWellPath(double measuredDepth) const
|
||||
{
|
||||
cvf::Vec3d wellPathPoint = cvf::Vec3d::ZERO;
|
||||
|
||||
@@ -190,3 +190,42 @@ void RigWellPath::twoClosestPoints(const cvf::Vec3d& position, cvf::Vec3d* p1, c
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<cvf::Vec3d> RigWellPath::clippedPointSubset(double startMD, double endMD) const
|
||||
{
|
||||
std::vector<cvf::Vec3d> points;
|
||||
if (m_measuredDepths.empty()) return points;
|
||||
|
||||
size_t i = 0;
|
||||
// Skip points below startMD
|
||||
while (i < m_measuredDepths.size() && m_measuredDepths[i] < startMD) ++i;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// If startMD is at or below the starting MD, use that point
|
||||
points.push_back(m_wellPathPoints[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
double stepsize = (startMD - m_measuredDepths[i - 1]) / (m_measuredDepths[i] - m_measuredDepths[i - 1]);
|
||||
points.push_back(m_wellPathPoints[i - 1] + stepsize * (m_wellPathPoints[i] - m_wellPathPoints[i - 1]));
|
||||
}
|
||||
|
||||
while (i < m_measuredDepths.size() && m_measuredDepths[i] < endMD)
|
||||
{
|
||||
// Add all points between startMD and endMD
|
||||
points.push_back(m_wellPathPoints[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i < m_measuredDepths.size() && m_measuredDepths[i] > endMD)
|
||||
{
|
||||
double stepsize = (endMD - m_measuredDepths[i - 1]) / (m_measuredDepths[i] - m_measuredDepths[i - 1]);
|
||||
points.push_back(m_wellPathPoints[i - 1] + stepsize * (m_wellPathPoints[i] - m_wellPathPoints[i - 1]));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,10 @@ public:
|
||||
void setDatumElevation(double value);
|
||||
bool hasDatumElevation() const;
|
||||
double datumElevation() const;
|
||||
cvf::Vec3d interpolatedPointAlongWellPath(double measuredDepth);
|
||||
cvf::Vec3d interpolatedPointAlongWellPath(double measuredDepth) const;
|
||||
double wellPathAzimuthAngle(const cvf::Vec3d& position) const;
|
||||
void twoClosestPoints(const cvf::Vec3d& position, cvf::Vec3d* p1, cvf::Vec3d* p2) const;
|
||||
std::vector<cvf::Vec3d> clippedPointSubset(double startMD, double endMD) const;
|
||||
|
||||
private:
|
||||
bool m_hasDatumElevation;
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RigWellPathIntersectionTools.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RigWellPath.h"
|
||||
#include "RigMainGrid.h"
|
||||
#include "RigEclipseCaseData.h"
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
|
||||
#include "cvfGeometryTools.h"
|
||||
#include "cvfMatrix3.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<WellPathCellIntersectionInfo> RigWellPathIntersectionTools::findCellsIntersectedByPath(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords, bool includeStartCell, bool includeEndCell)
|
||||
{
|
||||
std::vector<WellPathCellIntersectionInfo> intersectionInfo;
|
||||
const RigMainGrid* grid = caseData->mainGrid();
|
||||
|
||||
if (coords.size() < 2) return intersectionInfo;
|
||||
|
||||
std::vector<HexIntersectionInfo> intersections = getIntersectedCells(grid, coords);
|
||||
removeEnteringIntersections(&intersections);
|
||||
|
||||
if (intersections.empty()) return intersectionInfo;
|
||||
|
||||
cvf::Vec3d startPoint;
|
||||
cvf::Vec3d endPoint;
|
||||
size_t cellIndex;
|
||||
WellPathCellDirection direction;
|
||||
|
||||
auto intersection = intersections.cbegin();
|
||||
|
||||
if (includeStartCell)
|
||||
{
|
||||
bool foundCell;
|
||||
startPoint = coords[0];
|
||||
cellIndex = findCellFromCoords(grid, startPoint, &foundCell);
|
||||
if (foundCell)
|
||||
{
|
||||
endPoint = intersection->m_intersectionPoint;
|
||||
direction = calculateDirectionInCell(grid, cellIndex, startPoint, endPoint);
|
||||
intersectionInfo.push_back(WellPathCellIntersectionInfo(cellIndex, direction, startPoint, endPoint));
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::debug("Path starts outside valid cell");
|
||||
}
|
||||
}
|
||||
|
||||
startPoint = intersection->m_intersectionPoint;
|
||||
cellIndex = intersection->m_hexIndex;
|
||||
|
||||
++intersection;
|
||||
|
||||
while (intersection != intersections.cend())
|
||||
{
|
||||
endPoint = intersection->m_intersectionPoint;
|
||||
direction = calculateDirectionInCell(grid, cellIndex, startPoint, endPoint);
|
||||
intersectionInfo.push_back(WellPathCellIntersectionInfo(cellIndex, direction, startPoint, endPoint));
|
||||
|
||||
startPoint = endPoint;
|
||||
cellIndex = intersection->m_hexIndex;
|
||||
++intersection;
|
||||
}
|
||||
|
||||
if (includeEndCell)
|
||||
{
|
||||
endPoint = coords[coords.size() - 1];
|
||||
direction = calculateDirectionInCell(grid, cellIndex, startPoint, endPoint);
|
||||
intersectionInfo.push_back(WellPathCellIntersectionInfo(cellIndex, direction, startPoint, endPoint));
|
||||
}
|
||||
|
||||
return intersectionInfo;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<HexIntersectionInfo> RigWellPathIntersectionTools::getIntersectedCells(const RigMainGrid* grid, const std::vector<cvf::Vec3d>& coords)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = grid->nodes();
|
||||
std::vector<HexIntersectionInfo> intersections;
|
||||
for (size_t i = 0; i < coords.size() - 1; ++i)
|
||||
{
|
||||
cvf::BoundingBox bb;
|
||||
bb.add(coords[i]);
|
||||
bb.add(coords[i + 1]);
|
||||
|
||||
std::vector<size_t> closeCells = findCloseCells(grid, bb);
|
||||
|
||||
cvf::Vec3d hexCorners[8];
|
||||
|
||||
for (size_t closeCell : closeCells)
|
||||
{
|
||||
const RigCell& cell = grid->globalCellArray()[closeCell];
|
||||
if (cell.isInvalid()) continue;
|
||||
|
||||
setHexCorners(cell, nodeCoords, hexCorners);
|
||||
|
||||
RigHexIntersector::lineHexCellIntersection(coords[i], coords[i + 1], hexCorners, closeCell, &intersections);
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Calculates main axis vectors for each axis in the cell.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigWellPathIntersectionTools::calculateCellMainAxisVectors(const std::array<cvf::Vec3d, 8>& hexCorners, cvf::Vec3d* iAxisDirection, cvf::Vec3d* jAxisDirection, cvf::Vec3d* kAxisDirection)
|
||||
{
|
||||
*iAxisDirection = calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_I, cvf::StructGridInterface::POS_I);
|
||||
*jAxisDirection = calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_J, cvf::StructGridInterface::POS_J);
|
||||
*kAxisDirection = calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_K, cvf::StructGridInterface::POS_K);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Calculate the main axis vector for one axis in the cell, fron `negativeFace` to `positiveFace`
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RigWellPathIntersectionTools::calculateCellMainAxisVector(const std::array<cvf::Vec3d, 8>& hexCorners,
|
||||
cvf::StructGridInterface::FaceType negativeFace,
|
||||
cvf::StructGridInterface::FaceType positiveFace)
|
||||
{
|
||||
cvf::ubyte negativeFaceVertexIndices[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices(negativeFace, negativeFaceVertexIndices);
|
||||
cvf::Vec3d negativeFaceCenter = cvf::GeometryTools::computeFaceCenter(hexCorners[negativeFaceVertexIndices[0]],
|
||||
hexCorners[negativeFaceVertexIndices[1]],
|
||||
hexCorners[negativeFaceVertexIndices[2]],
|
||||
hexCorners[negativeFaceVertexIndices[3]]);
|
||||
|
||||
cvf::ubyte positiveFaceVertexIndices[4];
|
||||
cvf::StructGridInterface::cellFaceVertexIndices(positiveFace, positiveFaceVertexIndices);
|
||||
cvf::Vec3d positiveFaceCenter = cvf::GeometryTools::computeFaceCenter(hexCorners[positiveFaceVertexIndices[0]],
|
||||
hexCorners[positiveFaceVertexIndices[1]],
|
||||
hexCorners[positiveFaceVertexIndices[2]],
|
||||
hexCorners[positiveFaceVertexIndices[3]]);
|
||||
|
||||
return positiveFaceCenter - negativeFaceCenter;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
WellPathCellDirection RigWellPathIntersectionTools::calculateDirectionInCell(const std::array<cvf::Vec3d, 8>& hexCorners, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint)
|
||||
{
|
||||
cvf::Vec3d vec = endPoint - startPoint;
|
||||
|
||||
cvf::Vec3d iAxisVector;
|
||||
cvf::Vec3d jAxisVector;
|
||||
cvf::Vec3d kAxisVector;
|
||||
calculateCellMainAxisVectors(hexCorners, &iAxisVector, &jAxisVector, &kAxisVector);
|
||||
|
||||
cvf::Vec3d iAxisDirection = iAxisVector.getNormalized();
|
||||
cvf::Vec3d jAxisDirection = jAxisVector.getNormalized();
|
||||
cvf::Vec3d kAxisDirection = kAxisVector.getNormalized();
|
||||
|
||||
cvf::Mat3d localCellCoordinateSystem(iAxisDirection.x(), jAxisDirection.x(), kAxisDirection.x(),
|
||||
iAxisDirection.y(), jAxisDirection.y(), kAxisDirection.y(),
|
||||
iAxisDirection.z(), jAxisDirection.z(), kAxisDirection.z());
|
||||
|
||||
cvf::Vec3d localCellCoordinateVec = vec.getTransformedVector(localCellCoordinateSystem.getInverted());
|
||||
|
||||
double iLengthFraction = abs(localCellCoordinateVec.x() / iAxisVector.length());
|
||||
double jLengthFraction = abs(localCellCoordinateVec.y() / jAxisVector.length());
|
||||
double kLengthFraction = abs(localCellCoordinateVec.z() / kAxisVector.length());
|
||||
|
||||
if (iLengthFraction > jLengthFraction && iLengthFraction > kLengthFraction)
|
||||
{
|
||||
WellPathCellDirection direction = POS_I;
|
||||
if (localCellCoordinateVec.x() < 0)
|
||||
{
|
||||
direction = NEG_I;
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
else if (jLengthFraction > iLengthFraction && jLengthFraction > kLengthFraction)
|
||||
{
|
||||
WellPathCellDirection direction = POS_J;
|
||||
if (localCellCoordinateVec.y() < 0)
|
||||
{
|
||||
direction = NEG_J;
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
else
|
||||
{
|
||||
WellPathCellDirection direction = POS_K;
|
||||
if (localCellCoordinateVec.z() < 0)
|
||||
{
|
||||
direction = NEG_K;
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
WellPathCellDirection RigWellPathIntersectionTools::calculateDirectionInCell(const RigMainGrid* grid, size_t cellIndex, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint)
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners = getCellHexCorners(grid, cellIndex);
|
||||
|
||||
return calculateDirectionInCell(hexCorners, startPoint, endPoint);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RigWellPathIntersectionTools::findCloseCells(const RigMainGrid* grid, const cvf::BoundingBox& bb)
|
||||
{
|
||||
std::vector<size_t> closeCells;
|
||||
grid->findIntersectingCells(bb, &closeCells);
|
||||
return closeCells;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RigWellPathIntersectionTools::findCellFromCoords(const RigMainGrid* grid, const cvf::Vec3d& coords, bool* foundCell)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = grid->nodes();
|
||||
|
||||
cvf::BoundingBox bb;
|
||||
bb.add(coords);
|
||||
std::vector<size_t> closeCells = findCloseCells(grid, bb);
|
||||
cvf::Vec3d hexCorners[8];
|
||||
|
||||
for (size_t closeCell : closeCells)
|
||||
{
|
||||
const RigCell& cell = grid->globalCellArray()[closeCell];
|
||||
if (cell.isInvalid()) continue;
|
||||
|
||||
setHexCorners(cell, nodeCoords, hexCorners);
|
||||
|
||||
if (RigHexIntersector::isPointInCell(coords, hexCorners))
|
||||
{
|
||||
*foundCell = true;
|
||||
return closeCell;
|
||||
}
|
||||
}
|
||||
|
||||
*foundCell = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<cvf::Vec3d, 8> RigWellPathIntersectionTools::getCellHexCorners(const RigMainGrid* grid, size_t cellIndex)
|
||||
{
|
||||
const std::vector<cvf::Vec3d>& nodeCoords = grid->nodes();
|
||||
std::array<cvf::Vec3d, 8> corners;
|
||||
const RigCell& cell = grid->globalCellArray()[cellIndex];
|
||||
setHexCorners(cell, nodeCoords, corners.data());
|
||||
|
||||
return corners;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigWellPathIntersectionTools::setHexCorners(const RigCell& cell, const std::vector<cvf::Vec3d>& nodeCoords, cvf::Vec3d* hexCorners)
|
||||
{
|
||||
const caf::SizeTArray8& cornerIndices = cell.cornerIndices();
|
||||
|
||||
hexCorners[0] = nodeCoords[cornerIndices[0]];
|
||||
hexCorners[1] = nodeCoords[cornerIndices[1]];
|
||||
hexCorners[2] = nodeCoords[cornerIndices[2]];
|
||||
hexCorners[3] = nodeCoords[cornerIndices[3]];
|
||||
hexCorners[4] = nodeCoords[cornerIndices[4]];
|
||||
hexCorners[5] = nodeCoords[cornerIndices[5]];
|
||||
hexCorners[6] = nodeCoords[cornerIndices[6]];
|
||||
hexCorners[7] = nodeCoords[cornerIndices[7]];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigWellPathIntersectionTools::removeEnteringIntersections(std::vector<HexIntersectionInfo>* intersections)
|
||||
{
|
||||
for (auto it = intersections->begin(); it != intersections->end();)
|
||||
{
|
||||
if (it->m_isIntersectionEntering)
|
||||
{
|
||||
it = intersections->erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigCell.h"
|
||||
|
||||
#include "RigWellLogExtractionTools.h"
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
class RigWellPath;
|
||||
class RigMainGrid;
|
||||
class RigEclipseCaseData;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
enum WellPathCellDirection {
|
||||
POS_I,
|
||||
NEG_I,
|
||||
POS_J,
|
||||
NEG_J,
|
||||
POS_K,
|
||||
NEG_K
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
struct WellPathCellIntersectionInfo {
|
||||
WellPathCellIntersectionInfo(size_t cellIndex, WellPathCellDirection direction, cvf::Vec3d startPoint, cvf::Vec3d endPoint)
|
||||
: cellIndex(cellIndex),
|
||||
direction(direction),
|
||||
startPoint(startPoint),
|
||||
endPoint(endPoint)
|
||||
{}
|
||||
|
||||
size_t cellIndex;
|
||||
WellPathCellDirection direction;
|
||||
cvf::Vec3d startPoint;
|
||||
cvf::Vec3d endPoint;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RigWellPathIntersectionTools
|
||||
{
|
||||
public:
|
||||
static std::vector<WellPathCellIntersectionInfo> findCellsIntersectedByPath(const RigEclipseCaseData* caseData, const std::vector<cvf::Vec3d>& coords, bool includeStartCell = true, bool includeEndCell = true);
|
||||
|
||||
static std::vector<HexIntersectionInfo> getIntersectedCells(const RigMainGrid* grid, const std::vector<cvf::Vec3d>& coords);
|
||||
|
||||
// Calculate direction
|
||||
static void calculateCellMainAxisVectors(const std::array<cvf::Vec3d, 8>& hexCorners, cvf::Vec3d* iAxisDirection, cvf::Vec3d* jAxisDirection, cvf::Vec3d* kAxisDirection);
|
||||
static cvf::Vec3d calculateCellMainAxisVector(const std::array<cvf::Vec3d, 8>& hexCorners, cvf::StructGridInterface::FaceType startFace, cvf::StructGridInterface::FaceType endFace);
|
||||
static WellPathCellDirection calculateDirectionInCell(const std::array<cvf::Vec3d, 8>& hexCorners, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint);
|
||||
static WellPathCellDirection calculateDirectionInCell(const RigMainGrid* grid, size_t cellIndex, const cvf::Vec3d& startPoint, const cvf::Vec3d& endPoint);
|
||||
|
||||
static std::vector<size_t> findCloseCells(const RigMainGrid* grid, const cvf::BoundingBox& bb);
|
||||
static size_t findCellFromCoords(const RigMainGrid* caseData, const cvf::Vec3d& coords, bool* foundCell);
|
||||
|
||||
static std::array<cvf::Vec3d, 8> getCellHexCorners(const RigMainGrid* grid, size_t cellIndex);
|
||||
static void setHexCorners(const RigCell& cell, const std::vector<cvf::Vec3d>& nodeCoords, cvf::Vec3d* hexCorners);
|
||||
|
||||
private:
|
||||
static void removeEnteringIntersections(std::vector<HexIntersectionInfo>* intersections);
|
||||
};
|
||||
@@ -16,6 +16,7 @@ ${CEE_CURRENT_LIST_DIR}RifReaderEclipseSummary-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigActiveCellInfo-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigReservoir-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigStatisticsMath-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RigWellPathIntersectionTools-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RimWellLogExtractionCurveImpl-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RivPipeGeometryGenerator-Test.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RivTernaryScalarMapper-Test.cpp
|
||||
|
||||
140
ApplicationCode/UnitTests/RigWellPathIntersectionTools-Test.cpp
Normal file
140
ApplicationCode/UnitTests/RigWellPathIntersectionTools-Test.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RigWellPathIntersectionTools.h"
|
||||
|
||||
#include "cvfStructGrid.h"
|
||||
|
||||
|
||||
TEST(RigWellPathIntersectionTools, TestCalculateMainAxisVector)
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners = {
|
||||
cvf::Vec3d(0.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 10.0, 10.0),
|
||||
cvf::Vec3d(0.0, 10.0, 10.0),
|
||||
};
|
||||
|
||||
cvf::Vec3d iMainAxisVector = RigWellPathIntersectionTools::calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_I, cvf::StructGridInterface::POS_I);
|
||||
EXPECT_EQ(10, iMainAxisVector.x());
|
||||
EXPECT_EQ(0, iMainAxisVector.y());
|
||||
EXPECT_EQ(0, iMainAxisVector.z());
|
||||
|
||||
cvf::Vec3d jMainAxisVector = RigWellPathIntersectionTools::calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_J, cvf::StructGridInterface::POS_J);
|
||||
EXPECT_EQ(0 , jMainAxisVector.x());
|
||||
EXPECT_EQ(10, jMainAxisVector.y());
|
||||
EXPECT_EQ(0, jMainAxisVector.z());
|
||||
|
||||
cvf::Vec3d kMainAxisVector = RigWellPathIntersectionTools::calculateCellMainAxisVector(hexCorners, cvf::StructGridInterface::NEG_K, cvf::StructGridInterface::POS_K);
|
||||
EXPECT_EQ(0, kMainAxisVector.x());
|
||||
EXPECT_EQ(0, kMainAxisVector.y());
|
||||
EXPECT_EQ(10, kMainAxisVector.z());
|
||||
}
|
||||
|
||||
TEST(RigWellPathIntersectionTools, TestCalculateMainAxisVectors)
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners = {
|
||||
cvf::Vec3d(0.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 10.0, 10.0),
|
||||
cvf::Vec3d(0.0, 10.0, 10.0),
|
||||
};
|
||||
|
||||
|
||||
cvf::Vec3d iMainAxisVector;
|
||||
cvf::Vec3d jMainAxisVector;
|
||||
cvf::Vec3d kMainAxisVector;
|
||||
RigWellPathIntersectionTools::calculateCellMainAxisVectors(hexCorners, &iMainAxisVector, &jMainAxisVector, &kMainAxisVector);
|
||||
|
||||
EXPECT_EQ(10, iMainAxisVector.x());
|
||||
EXPECT_EQ(0, iMainAxisVector.y());
|
||||
EXPECT_EQ(0, iMainAxisVector.z());
|
||||
|
||||
EXPECT_EQ(0 , jMainAxisVector.x());
|
||||
EXPECT_EQ(10, jMainAxisVector.y());
|
||||
EXPECT_EQ(0, jMainAxisVector.z());
|
||||
|
||||
EXPECT_EQ(0, kMainAxisVector.x());
|
||||
EXPECT_EQ(0, kMainAxisVector.y());
|
||||
EXPECT_EQ(10, kMainAxisVector.z());
|
||||
}
|
||||
|
||||
TEST(RigWellPathIntersectionTools, TestCalculateDirectionInCellThroughCellCenters)
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners = {
|
||||
cvf::Vec3d(0.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 0.0, 0.0),
|
||||
cvf::Vec3d(10.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 10.0, 0.0),
|
||||
cvf::Vec3d(0.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 0.0, 10.0),
|
||||
cvf::Vec3d(10.0, 10.0, 10.0),
|
||||
cvf::Vec3d(0.0, 10.0, 10.0),
|
||||
};
|
||||
|
||||
{
|
||||
cvf::Vec3d startPoint(0, 5, 5);
|
||||
cvf::Vec3d endPoint(10, 5, 5);
|
||||
WellPathCellDirection direction = RigWellPathIntersectionTools::calculateDirectionInCell(hexCorners, startPoint, endPoint);
|
||||
EXPECT_EQ(POS_I, direction);
|
||||
}
|
||||
|
||||
{
|
||||
cvf::Vec3d startPoint(5, 0, 5);
|
||||
cvf::Vec3d endPoint(5, 10, 5);
|
||||
WellPathCellDirection direction = RigWellPathIntersectionTools::calculateDirectionInCell(hexCorners, startPoint, endPoint);
|
||||
EXPECT_EQ(POS_J, direction);
|
||||
}
|
||||
|
||||
{
|
||||
cvf::Vec3d startPoint(5, 5, 0);
|
||||
cvf::Vec3d endPoint(5, 5, 10);
|
||||
WellPathCellDirection direction = RigWellPathIntersectionTools::calculateDirectionInCell(hexCorners, startPoint, endPoint);
|
||||
EXPECT_EQ(POS_K, direction);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RigWellPathIntersectionTools, TestCalculateJDirectionThroughCells)
|
||||
{
|
||||
std::array<cvf::Vec3d, 8> hexCorners = {
|
||||
cvf::Vec3d(402380, 7.21315e+06, -4219.43),
|
||||
cvf::Vec3d(402475, 7.21311e+06, -4226.57),
|
||||
cvf::Vec3d(402510, 7.2132e+06, -4226.75),
|
||||
cvf::Vec3d(402414, 7.21324e+06, -4222.8),
|
||||
cvf::Vec3d(402385, 7.21314e+06, -4232.71),
|
||||
cvf::Vec3d(402480, 7.2131e+06, -4239.88),
|
||||
cvf::Vec3d(402515, 7.2132e+06, -4239.94),
|
||||
cvf::Vec3d(402419, 7.21323e+06, -4235.84),
|
||||
};
|
||||
|
||||
cvf::Vec3d startPoint(402428, 7.21323e+06, -4223.35);
|
||||
cvf::Vec3d endPoint(402399, 7.21314e+06, -4224.48);
|
||||
WellPathCellDirection direction = RigWellPathIntersectionTools::calculateDirectionInCell(hexCorners, startPoint, endPoint);
|
||||
|
||||
EXPECT_EQ(NEG_J, direction);
|
||||
}
|
||||
@@ -54,8 +54,7 @@ public:
|
||||
virtual ~PdmChildArrayField();
|
||||
|
||||
PdmChildArrayField& operator() () { return *this; }
|
||||
|
||||
const PdmChildArrayField& operator() () const { return *this; }
|
||||
const PdmChildArrayField& operator() () const { return *this; }
|
||||
|
||||
// Reimplementation of PdmPointersFieldHandle methods
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
set(RESINSIGHT_MAJOR_VERSION 2016)
|
||||
set(RESINSIGHT_MINOR_VERSION 11)
|
||||
set(RESINSIGHT_MAJOR_VERSION 2017)
|
||||
set(RESINSIGHT_MINOR_VERSION 05)
|
||||
set(RESINSIGHT_INCREMENT_VERSION "pre-proto.12")
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ set(NRLIB_GITHUB_SHA "ba35d4359882f1c6f5e9dc30eb95fe52af50fd6f")
|
||||
set(ERT_GITHUB_SHA "06a39878636af0bc52582430ad0431450e51139c")
|
||||
|
||||
# https://github.com/OPM/opm-flowdiagnostics
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "2c5fb55db4c4ded49c14161dd16463e1207da049")
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "b6e59ddcd2feba450c8612a7402c9239e442c0d4")
|
||||
|
||||
# https://github.com/OPM/opm-flowdiagnostics-applications
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "570601718e7197b751bc3cba60c1e5fb7d842842")
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "ccaaa4dd1b553e131a3051687fd615fe728b76ee")
|
||||
|
||||
# https://github.com/OPM/opm-parser/blob/master/opm/parser/eclipse/Units/Units.hpp
|
||||
# This file was moved from opm-core to opm-parser october 2016
|
||||
|
||||
@@ -21,14 +21,19 @@
|
||||
# the library needs it.
|
||||
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
opm/utility/ECLEndPointScaling.cpp
|
||||
opm/utility/ECLFluxCalc.cpp
|
||||
opm/utility/ECLGraph.cpp
|
||||
opm/utility/ECLPropTable.cpp
|
||||
opm/utility/ECLResultData.cpp
|
||||
opm/utility/ECLSaturationFunc.cpp
|
||||
opm/utility/ECLUnitHandling.cpp
|
||||
opm/utility/ECLWellSolution.cpp
|
||||
)
|
||||
|
||||
list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_eclendpointscaling.cpp
|
||||
tests/test_eclproptable.cpp
|
||||
tests/test_eclunithandling.cpp
|
||||
)
|
||||
|
||||
@@ -44,9 +49,13 @@ list (APPEND EXAMPLE_SOURCE_FILES
|
||||
)
|
||||
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/utility/ECLEndPointScaling.hpp
|
||||
opm/utility/ECLFluxCalc.hpp
|
||||
opm/utility/ECLGraph.hpp
|
||||
opm/utility/ECLPhaseIndex.hpp
|
||||
opm/utility/ECLPropTable.hpp
|
||||
opm/utility/ECLResultData.hpp
|
||||
opm/utility/ECLSaturationFunc.hpp
|
||||
opm/utility/ECLUnitHandling.hpp
|
||||
opm/utility/ECLWellSolution.hpp
|
||||
)
|
||||
|
||||
@@ -38,34 +38,27 @@ try {
|
||||
std::vector<Opm::FlowDiagnostics::CellSet> start;
|
||||
auto fwd = fdTool.computeInjectionDiagnostics(start);
|
||||
auto rev = fdTool.computeProductionDiagnostics(start);
|
||||
|
||||
// Give disconnected cells zero pore volume.
|
||||
std::vector<int> nbcount(setup.graph.numCells(), 0);
|
||||
for (int nb : setup.graph.neighbours()) {
|
||||
if (nb >= 0) {
|
||||
++nbcount[nb];
|
||||
}
|
||||
}
|
||||
auto pv = setup.graph.poreVolume();
|
||||
for (size_t i = 0; i < pv.size(); ++i) {
|
||||
if (nbcount[i] == 0) {
|
||||
pv[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Cells that have more than 100 times the average pore volume are
|
||||
// probably aquifers, we ignore them (again by setting pore volume
|
||||
// to zero). If this is the correct thing to do or not could
|
||||
// depend on what you want to use the diagnostic for.
|
||||
const double average_pv = std::accumulate(pv.begin(), pv.end(), 0.0) / double(pv.size());
|
||||
for (double& pvval : pv) {
|
||||
if (pvval > 100.0 * average_pv) {
|
||||
pvval = 0.0;
|
||||
const bool ignore_disconnected = setup.param.getDefault("ignore_disconnected", true);
|
||||
if (ignore_disconnected) {
|
||||
// Give disconnected cells zero pore volume.
|
||||
std::vector<int> nbcount(setup.graph.numCells(), 0);
|
||||
for (int nb : setup.graph.neighbours()) {
|
||||
if (nb >= 0) {
|
||||
++nbcount[nb];
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < pv.size(); ++i) {
|
||||
if (nbcount[i] == 0) {
|
||||
pv[i] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute graph.
|
||||
auto fphi = Opm::FlowDiagnostics::flowCapacityStorageCapacityCurve(fwd, rev, pv);
|
||||
const double max_pv_fraction = setup.param.getDefault("max_pv_fraction", 0.1);
|
||||
auto fphi = Opm::FlowDiagnostics::flowCapacityStorageCapacityCurve(fwd, rev, pv, max_pv_fraction);
|
||||
|
||||
// Write it to standard out.
|
||||
std::cout.precision(16);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <opm/utility/ECLFluxCalc.hpp>
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLWellSolution.hpp>
|
||||
|
||||
@@ -115,15 +116,19 @@ namespace example {
|
||||
}
|
||||
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField(const Opm::ECLGraph& G,
|
||||
const Opm::ECLRestartData& rstrt,
|
||||
const bool compute_fluxes)
|
||||
extractFluxField(const Opm::ECLGraph& G,
|
||||
const Opm::ECLInitFileData& init,
|
||||
const Opm::ECLRestartData& rstrt,
|
||||
const bool compute_fluxes,
|
||||
const bool useEPS)
|
||||
{
|
||||
if (compute_fluxes) {
|
||||
Opm::ECLFluxCalc calc(G);
|
||||
auto satfunc = ::Opm::ECLSaturationFunc(G, init, useEPS);
|
||||
|
||||
Opm::ECLFluxCalc calc(G, std::move(satfunc));
|
||||
|
||||
auto getFlux = [&calc, &rstrt]
|
||||
(const Opm::ECLGraph::PhaseIndex p)
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
return calc.flux(rstrt, p);
|
||||
};
|
||||
@@ -132,7 +137,7 @@ namespace example {
|
||||
}
|
||||
|
||||
auto getFlux = [&G, &rstrt]
|
||||
(const Opm::ECLGraph::PhaseIndex p)
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
return G.flux(rstrt, p);
|
||||
};
|
||||
@@ -205,17 +210,6 @@ namespace example {
|
||||
|
||||
|
||||
|
||||
inline Opm::ECLGraph
|
||||
initGraph(const FilePaths& file_paths)
|
||||
{
|
||||
const auto I = Opm::ECLInitFileData(file_paths.init);
|
||||
|
||||
return Opm::ECLGraph::load(file_paths.grid, I);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
inline Opm::FlowDiagnostics::Toolbox
|
||||
initToolbox(const Opm::ECLGraph& G)
|
||||
{
|
||||
@@ -238,11 +232,13 @@ namespace example {
|
||||
Setup(int argc, char** argv)
|
||||
: param (initParam(argc, argv))
|
||||
, file_paths (param)
|
||||
, init (file_paths.init)
|
||||
, rstrt (file_paths.restart)
|
||||
, graph (initGraph(file_paths))
|
||||
, graph (::Opm::ECLGraph::load(file_paths.grid, init))
|
||||
, well_fluxes ()
|
||||
, toolbox (initToolbox(graph))
|
||||
, compute_fluxes_(param.getDefault("compute_fluxes", false))
|
||||
, useEPS_ (param.getDefault("use_ep_scaling", false))
|
||||
{
|
||||
const int step = param.getDefault("step", 0);
|
||||
|
||||
@@ -268,7 +264,9 @@ namespace example {
|
||||
well_fluxes = wsol.solution(rstrt, graph.activeGrids());
|
||||
}
|
||||
|
||||
toolbox.assignConnectionFlux(extractFluxField(graph, rstrt, compute_fluxes_));
|
||||
toolbox.assignConnectionFlux(extractFluxField(graph, init, rstrt,
|
||||
compute_fluxes_, useEPS_));
|
||||
|
||||
toolbox.assignInflowFlux(extractWellFlows(graph, well_fluxes));
|
||||
|
||||
return true;
|
||||
@@ -276,11 +274,13 @@ namespace example {
|
||||
|
||||
Opm::ParameterGroup param;
|
||||
FilePaths file_paths;
|
||||
Opm::ECLInitFileData init;
|
||||
Opm::ECLRestartData rstrt;
|
||||
Opm::ECLGraph graph;
|
||||
std::vector<Opm::ECLWellSolution::WellData> well_fluxes;
|
||||
Opm::FlowDiagnostics::Toolbox toolbox;
|
||||
bool compute_fluxes_ = false;
|
||||
bool useEPS_ = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
1163
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLEndPointScaling.cpp
vendored
Normal file
1163
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLEndPointScaling.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLENDPOINTSCALING_HEADER_INCLUDED
|
||||
#define OPM_ECLENDPOINTSCALING_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class ECLGraph;
|
||||
class ECLInitFileData;
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
namespace Opm { namespace SatFunc {
|
||||
|
||||
/// Protocol for computing scaled saturation values.
|
||||
class EPSEvalInterface
|
||||
{
|
||||
public:
|
||||
/// Static (unscaled) end-points of a particular, tabulated
|
||||
/// saturation function.
|
||||
struct TableEndPoints {
|
||||
/// Critical or connate/minimum saturation, depending on
|
||||
/// subsequent function evaluation context.
|
||||
///
|
||||
/// Use critical saturation when computing look-up values for
|
||||
/// relative permeability and connate/minimum saturation when
|
||||
/// computing look-up values for capillary pressure.
|
||||
double low;
|
||||
|
||||
/// Displacing saturation (3-pt option only).
|
||||
double disp;
|
||||
|
||||
/// Maximum (high) saturation point.
|
||||
double high;
|
||||
};
|
||||
|
||||
/// Associate a saturation value to a specific cell.
|
||||
struct SaturationAssoc {
|
||||
/// Cell to which to connect a saturation value.
|
||||
std::vector<int>::size_type cell;
|
||||
|
||||
/// Saturation value.
|
||||
double sat;
|
||||
};
|
||||
|
||||
/// Convenience type alias.
|
||||
using SaturationPoints = std::vector<SaturationAssoc>;
|
||||
|
||||
/// Derive scaled saturations--inputs to subsequent evaluation of
|
||||
/// saturation functions--corresponding to a sequence of unscaled
|
||||
/// saturation values.
|
||||
///
|
||||
/// \param[in] tep Static end points that identify the saturation
|
||||
/// scaling intervals of a particular tabulated saturation
|
||||
/// function.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points.
|
||||
///
|
||||
/// \return Sequence of scaled saturation values in order of the
|
||||
/// input sequence. In particular the \c i-th element of this
|
||||
/// result is the scaled version of \code sp[i].sat \endcode.
|
||||
virtual std::vector<double>
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const = 0;
|
||||
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const = 0;
|
||||
|
||||
/// Destructor. Must be virtual.
|
||||
virtual ~EPSEvalInterface();
|
||||
};
|
||||
|
||||
/// Implementation of ECLIPSE's standard, two-point, saturation scaling
|
||||
/// option.
|
||||
class TwoPointScaling : public EPSEvalInterface
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// Typically set up to define the end-point scaling of all active
|
||||
/// cells in a model, but could alternatively be used as a means to
|
||||
/// computing the effective saturation function of a single cell.
|
||||
///
|
||||
/// \param[in] smin Left end points for a set of cells.
|
||||
///
|
||||
/// \param[in] smax Right end points for a set of cells.
|
||||
TwoPointScaling(std::vector<double> smin,
|
||||
std::vector<double> smax);
|
||||
|
||||
/// Destructor.
|
||||
~TwoPointScaling();
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
TwoPointScaling(const TwoPointScaling& rhs);
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
TwoPointScaling(TwoPointScaling&& rhs);
|
||||
|
||||
/// Assignment operator.
|
||||
///
|
||||
/// Replaces current implementation with a copy of existing object's
|
||||
/// implementation details.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
TwoPointScaling&
|
||||
operator=(const TwoPointScaling& rhs);
|
||||
|
||||
/// Move assingment operator.
|
||||
///
|
||||
/// Subsumes existing object's implementation details and uses those
|
||||
/// to replace the current implementation.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
TwoPointScaling&
|
||||
operator=(TwoPointScaling&& rhs);
|
||||
|
||||
/// Derive scaled saturations using the two-point scaling definition
|
||||
/// from a sequence of unscaled saturation values.
|
||||
///
|
||||
/// \param[in] tep Static end points that identify the saturation
|
||||
/// scaling intervals of a particular tabulated saturation
|
||||
/// function. The evaluation procedure considers only \code
|
||||
/// tep.low \endcode and \code tep.high \endcode. The value of
|
||||
/// \code tep.disp \endcode is never read.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points. The maximum cell
|
||||
/// index (\code sp[i].cell \endcode) must be strictly less than
|
||||
/// the size of the input arrays that define the current
|
||||
/// saturation regions.
|
||||
///
|
||||
/// \return Sequence of scaled saturation values in order of the
|
||||
/// input sequence. In particular the \c i-th element of this
|
||||
/// result is the scaled version of \code sp[i].sat \endcode.
|
||||
virtual std::vector<double>
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const override;
|
||||
|
||||
private:
|
||||
/// Implementation class.
|
||||
class Impl;
|
||||
|
||||
/// Pimpl idiom.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
/// Implementation of ECLIPSE's alternative, three-point, saturation
|
||||
/// scaling option.
|
||||
class ThreePointScaling : public EPSEvalInterface
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// Typically set up to define the end-point scaling of all active
|
||||
/// cells in a model, but could alternatively be used as a means to
|
||||
/// computing the effective saturation function of a single cell.
|
||||
///
|
||||
/// \param[in] smin Left end points for a set of cells.
|
||||
///
|
||||
/// \param[in] sdisp Intermediate--displacing saturation--end points
|
||||
/// for a set of cells.
|
||||
///
|
||||
/// \param[in] smax Right end points for a set of cells.
|
||||
ThreePointScaling(std::vector<double> smin,
|
||||
std::vector<double> sdisp,
|
||||
std::vector<double> smax);
|
||||
|
||||
/// Destructor.
|
||||
~ThreePointScaling();
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
ThreePointScaling(const ThreePointScaling& rhs);
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
ThreePointScaling(ThreePointScaling&& rhs);
|
||||
|
||||
/// Assignment operator.
|
||||
///
|
||||
/// Replaces current implementation with a copy of existing object's
|
||||
/// implementation details.
|
||||
///
|
||||
/// \param[in] rhs Existing object.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
ThreePointScaling&
|
||||
operator=(const ThreePointScaling& rhs);
|
||||
|
||||
/// Move assingment operator.
|
||||
///
|
||||
/// Subsumes existing object's implementation details and uses those
|
||||
/// to replace the current implementation.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
ThreePointScaling&
|
||||
operator=(ThreePointScaling&& rhs);
|
||||
|
||||
/// Derive scaled saturations using the three-point scaling
|
||||
/// definition from a sequence of unscaled saturation values.
|
||||
///
|
||||
/// \param[in] tep Static end points that identify the saturation
|
||||
/// scaling intervals of a particular tabulated saturation
|
||||
/// function. The evaluation procedure considers only \code
|
||||
/// tep.low \endcode and \code tep.high \endcode. The value of
|
||||
/// \code tep.disp \endcode is never read.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points. The maximum cell
|
||||
/// index (\code sp[i].cell \endcode) must be strictly less than
|
||||
/// the size of the input arrays that define the current
|
||||
/// saturation regions.
|
||||
///
|
||||
/// \return Sequence of scaled saturation values in order of the
|
||||
/// input sequence. In particular the \c i-th element of this
|
||||
/// result is the scaled version of \code sp[i].sat \endcode.
|
||||
virtual std::vector<double>
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const override;
|
||||
|
||||
private:
|
||||
/// Implementation class.
|
||||
class Impl;
|
||||
|
||||
/// Pimpl idiom.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
/// Named constructors for enabling saturation end-point scaling from an
|
||||
/// ECL result set (see class \c ECLInitFileData).
|
||||
struct CreateEPS
|
||||
{
|
||||
/// Category of function for which to create an EPS evaluator.
|
||||
enum class FunctionCategory {
|
||||
/// This EPS is for relative permeability. Possibly uses
|
||||
/// three-point (alternative) formulation.
|
||||
Relperm,
|
||||
|
||||
/// This EPS is for capillary pressure. Two-point option only.
|
||||
CapPress,
|
||||
};
|
||||
|
||||
/// Categories of saturation function systems for which to create an
|
||||
/// EPS evaluator.
|
||||
enum class SubSystem {
|
||||
/// Create an EPS for a curve in the Oil-Water (sub-) system.
|
||||
OilWater,
|
||||
|
||||
/// Create an EPS for a curve in the Oil-Gas (sub-) system.
|
||||
OilGas,
|
||||
};
|
||||
|
||||
/// Set of options that uniquely define a single EPS operation.
|
||||
struct EPSOptions {
|
||||
/// Whether or not to employ the alternative (i.e., 3-pt) scaling
|
||||
/// procedure. Only applicable to FunctionCategory::Relperm and
|
||||
/// ignored in the case of FunctionCategory::CapPress.
|
||||
bool use3PtScaling;
|
||||
|
||||
/// Curve-type for which to create an EPS.
|
||||
FunctionCategory curve;
|
||||
|
||||
/// Part of global fluid system for which to create an EPS.
|
||||
SubSystem subSys;
|
||||
|
||||
/// Phase for whose \c curve in which \c subSys to create an
|
||||
/// EPS.
|
||||
///
|
||||
/// Example: Create a standard (two-point) EPS for the relative
|
||||
/// permeability of oil in the oil-gas subsystem of an
|
||||
/// oil-gas-water active phase system.
|
||||
///
|
||||
/// \code
|
||||
/// auto opt = EPSOptions{};
|
||||
///
|
||||
/// opt.use3PtScaling = false;
|
||||
/// opt.curve = FunctionCategory::Relperm;
|
||||
/// opt.subSys = SubSystem::OilGas;
|
||||
/// opt.thisPh = ECLPhaseIndex::Oil;
|
||||
///
|
||||
/// auto eps = CreateEPS::fromECLOutput(G, init, opt);
|
||||
/// \endcode
|
||||
::Opm::ECLPhaseIndex thisPh;
|
||||
};
|
||||
|
||||
/// Collection of raw saturation table end points.
|
||||
struct RawTableEndPoints {
|
||||
/// Collection of connate (minimum) saturation end points.
|
||||
struct Connate {
|
||||
/// Connate oil saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> oil;
|
||||
|
||||
/// Connate gas saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> gas;
|
||||
|
||||
/// Connate water saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> water;
|
||||
};
|
||||
|
||||
/// Collection of critical saturations. Used in deriving scaled
|
||||
/// displacing saturations in the alternative (three-point)
|
||||
/// scaling procedure.
|
||||
struct Critical {
|
||||
/// Critical oil saturation in 2p OG system from total set
|
||||
/// of tabulated saturation functions.
|
||||
std::vector<double> oil_in_gas;
|
||||
|
||||
/// Critical oil saturation in 2p OW system from total set
|
||||
/// of tabulated saturation functions.
|
||||
std::vector<double> oil_in_water;
|
||||
|
||||
/// Critical gas saturation in 2p OG or 3p OGW system from
|
||||
/// total set of tabulated saturation functions.
|
||||
std::vector<double> gas;
|
||||
|
||||
/// Critical water saturation in 2p OW or 3p OGW system from
|
||||
/// total set of tabulated saturation functions.
|
||||
std::vector<double> water;
|
||||
};
|
||||
|
||||
/// Collection of maximum saturation end points.
|
||||
struct Maximum {
|
||||
/// Maximum oil saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> oil;
|
||||
|
||||
/// Maximum gas saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> gas;
|
||||
|
||||
/// Maximum water saturation for each table in total set of
|
||||
/// tabulated saturation functions.
|
||||
std::vector<double> water;
|
||||
};
|
||||
|
||||
/// Minimum saturation end points for all tabulated saturation
|
||||
/// functions.
|
||||
Connate conn;
|
||||
|
||||
/// Critical saturations for all tabulated saturation functions.
|
||||
Critical crit;
|
||||
|
||||
/// Maximum saturation end points for all tabulated saturation
|
||||
/// functions.
|
||||
Maximum smax;
|
||||
};
|
||||
|
||||
/// Construct an EPS evaluator from a particular ECL result set.
|
||||
///
|
||||
/// \param[in] G Connected topology of current model's active cells.
|
||||
/// Needed to linearise table end-points that may be distributed
|
||||
/// on local grids to all of the model's active cells (\code
|
||||
/// member function G.rawLinearisedCellData() \endcode).
|
||||
///
|
||||
/// \param[in] init Container of tabulated saturation functions and
|
||||
/// saturation table end points for all active cells.
|
||||
///
|
||||
/// \param[in] opt Options that identify a particular end-point
|
||||
/// scaling behaviour of a particular saturation function curve.
|
||||
///
|
||||
/// \return EPS evaluator for the particular curve defined by the
|
||||
/// input options.
|
||||
static std::unique_ptr<EPSEvalInterface>
|
||||
fromECLOutput(const ECLGraph& G,
|
||||
const ECLInitFileData& init,
|
||||
const EPSOptions& opt);
|
||||
|
||||
/// Extract table end points relevant to a particular EPS evaluator
|
||||
/// from raw tabulated saturation functions.
|
||||
///
|
||||
/// \param[in] ep Collection of all raw table saturation end points
|
||||
/// for all tabulated saturation functions. Typically computed
|
||||
/// by direct calls to the \code connateSat() \endcode, \code
|
||||
/// criticalSat() \endcode, and \code maximumSat() \endcode of
|
||||
/// the currently active \code Opm::SatFuncInterpolant \code
|
||||
/// objects.
|
||||
///
|
||||
/// \param[in] opt Options that identify a particular end-point
|
||||
/// scaling behaviour of a particular saturation function curve.
|
||||
///
|
||||
/// \return Subset of the input end points in a format intended for
|
||||
/// passing as the first argument of member function \code eval()
|
||||
/// \endcode of the \code EPSEvalInterface \endcode that
|
||||
/// corresponds to the input options.
|
||||
static std::vector<EPSEvalInterface::TableEndPoints>
|
||||
unscaledEndPoints(const RawTableEndPoints& ep,
|
||||
const EPSOptions& opt);
|
||||
};
|
||||
}} // namespace Opm::SatFunc
|
||||
|
||||
#endif // OPM_ECLENDPOINTSCALING_HEADER_INCLUDED
|
||||
@@ -22,11 +22,15 @@
|
||||
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
ECLFluxCalc::ECLFluxCalc(const ECLGraph& graph)
|
||||
ECLFluxCalc::ECLFluxCalc(const ECLGraph& graph,
|
||||
ECLSaturationFunc&& satfunc)
|
||||
: graph_(graph)
|
||||
, satfunc_(std::move(satfunc))
|
||||
, neighbours_(graph.neighbours())
|
||||
, transmissibility_(graph.transmissibility())
|
||||
{
|
||||
@@ -38,7 +42,7 @@ namespace Opm
|
||||
|
||||
std::vector<double>
|
||||
ECLFluxCalc::flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex /* phase */) const
|
||||
const ECLPhaseIndex phase) const
|
||||
{
|
||||
// Obtain dynamic data.
|
||||
DynamicData dyn_data;
|
||||
@@ -46,6 +50,9 @@ namespace Opm
|
||||
.linearisedCellData(rstrt, "PRESSURE",
|
||||
&ECLUnits::UnitSystem::pressure);
|
||||
|
||||
dyn_data.relperm = this->satfunc_
|
||||
.relperm(this->graph_, rstrt, phase);
|
||||
|
||||
// Compute fluxes per connection.
|
||||
const int num_conn = transmissibility_.size();
|
||||
std::vector<double> fluxvec(num_conn);
|
||||
@@ -66,8 +73,12 @@ namespace Opm
|
||||
const int c2 = neighbours_[2*connection + 1];
|
||||
const double transmissibility = transmissibility_[connection];
|
||||
const double viscosity = 1.0 * prefix::centi * unit::Poise;
|
||||
const double mobility = 1.0 / viscosity;
|
||||
const auto& pressure = dyn_data.pressure;
|
||||
|
||||
const int upwind_cell = (pressure[c2] > pressure[c1]) ? c2 : c1;
|
||||
const double kr = dyn_data.relperm[upwind_cell];
|
||||
|
||||
const double mobility = kr / viscosity;
|
||||
return mobility * transmissibility * (pressure[c1] - pressure[c2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#define OPM_ECLFLUXCALC_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLSaturationFunc.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
@@ -34,13 +37,15 @@ namespace Opm
|
||||
/// Construct from ECLGraph.
|
||||
///
|
||||
/// \param[in] graph Connectivity data, as well as providing a means to read data from the restart file.
|
||||
explicit ECLFluxCalc(const ECLGraph& graph);
|
||||
|
||||
using PhaseIndex = ECLGraph::PhaseIndex;
|
||||
explicit ECLFluxCalc(const ECLGraph& graph,
|
||||
ECLSaturationFunc&& satfunc);
|
||||
|
||||
/// Retrive phase flux on all connections defined by \code
|
||||
/// graph.neighbours() \endcode.
|
||||
///
|
||||
/// \param[in] rstrt ECL Restart data set from which to extract
|
||||
/// relevant data per cell.
|
||||
///
|
||||
/// \param[in] phase Canonical phase for which to retrive flux.
|
||||
///
|
||||
/// \return Flux values corresponding to selected phase.
|
||||
@@ -48,18 +53,20 @@ namespace Opm
|
||||
/// Numerical values in SI units (rm^3/s).
|
||||
std::vector<double>
|
||||
flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex phase) const;
|
||||
const ECLPhaseIndex phase) const;
|
||||
|
||||
private:
|
||||
struct DynamicData
|
||||
{
|
||||
std::vector<double> pressure;
|
||||
std::vector<double> relperm;
|
||||
};
|
||||
|
||||
double singleFlux(const int connection,
|
||||
const DynamicData& dyn_data) const;
|
||||
|
||||
const ECLGraph& graph_;
|
||||
ECLSaturationFunc satfunc_;
|
||||
std::vector<int> neighbours_;
|
||||
std::vector<double> transmissibility_;
|
||||
};
|
||||
|
||||
@@ -1278,7 +1278,7 @@ public:
|
||||
///
|
||||
/// Mostly useful to determine the set of \c PhaseIndex values for which
|
||||
/// flux() may return non-zero values.
|
||||
const std::vector<PhaseIndex>& activePhases() const;
|
||||
const std::vector<ECLPhaseIndex>& activePhases() const;
|
||||
|
||||
/// Retrieve the simulation scenario's set of active grids.
|
||||
///
|
||||
@@ -1322,7 +1322,7 @@ public:
|
||||
/// all).
|
||||
std::vector<double>
|
||||
flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex phase) const;
|
||||
const ECLPhaseIndex phase) const;
|
||||
|
||||
/// Retrieve result set vector from current view linearised on active
|
||||
/// cells.
|
||||
@@ -1609,7 +1609,7 @@ private:
|
||||
/// Set of active phases in result set. Derived from .INIT on the
|
||||
/// assumption that the set of active phases does not change throughout
|
||||
/// the simulation run.
|
||||
std::vector<PhaseIndex> activePhases_;
|
||||
std::vector<ECLPhaseIndex> activePhases_;
|
||||
|
||||
/// Set of active grids in result set.
|
||||
std::vector<std::string> activeGrids_;
|
||||
@@ -1639,7 +1639,7 @@ private:
|
||||
/// \return Basename for ECl vector corresponding to particular phase
|
||||
/// flux.
|
||||
std::string
|
||||
flowVector(const PhaseIndex phase) const;
|
||||
flowVector(const ECLPhaseIndex phase) const;
|
||||
|
||||
/// Extract flux values corresponding to particular result set vector
|
||||
/// for all identified non-neighbouring connections.
|
||||
@@ -1940,7 +1940,7 @@ Opm::ECLGraph::Impl::numConnections() const
|
||||
return nconn + this->nnc_.numConnections();
|
||||
}
|
||||
|
||||
const std::vector<Opm::ECLGraph::PhaseIndex>&
|
||||
const std::vector<Opm::ECLPhaseIndex>&
|
||||
Opm::ECLGraph::Impl::activePhases() const
|
||||
{
|
||||
return this->activePhases_;
|
||||
@@ -2028,7 +2028,7 @@ Opm::ECLGraph::Impl::transmissibility() const
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLGraph::Impl::flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex phase) const
|
||||
const ECLPhaseIndex phase) const
|
||||
{
|
||||
auto fluxUnit = [&rstrt](const std::string& gridID)
|
||||
{
|
||||
@@ -2226,12 +2226,12 @@ defineActivePhases(const ::Opm::ECLInitFileData& init)
|
||||
const auto phaseMask =
|
||||
static_cast<unsigned int>(ih[INTEHEAD_PHASE_INDEX]);
|
||||
|
||||
using Check = std::pair<PhaseIndex, unsigned int>;
|
||||
using Check = std::pair<ECLPhaseIndex, unsigned int>;
|
||||
|
||||
const auto allPhases = std::vector<Check> {
|
||||
{ PhaseIndex::Aqua , (1u << 1) },
|
||||
{ PhaseIndex::Liquid, (1u << 0) },
|
||||
{ PhaseIndex::Vapour, (1u << 2) },
|
||||
{ ECLPhaseIndex::Aqua , (1u << 1) },
|
||||
{ ECLPhaseIndex::Liquid, (1u << 0) },
|
||||
{ ECLPhaseIndex::Vapour, (1u << 2) },
|
||||
};
|
||||
|
||||
this->activePhases_.clear();
|
||||
@@ -2243,19 +2243,19 @@ defineActivePhases(const ::Opm::ECLInitFileData& init)
|
||||
}
|
||||
|
||||
std::string
|
||||
Opm::ECLGraph::Impl::flowVector(const PhaseIndex phase) const
|
||||
Opm::ECLGraph::Impl::flowVector(const ECLPhaseIndex phase) const
|
||||
{
|
||||
const auto vector = std::string("FLR"); // Flow-rate, reservoir
|
||||
|
||||
if (phase == PhaseIndex::Aqua) {
|
||||
if (phase == ECLPhaseIndex::Aqua) {
|
||||
return vector + "WAT";
|
||||
}
|
||||
|
||||
if (phase == PhaseIndex::Liquid) {
|
||||
if (phase == ECLPhaseIndex::Liquid) {
|
||||
return vector + "OIL";
|
||||
}
|
||||
|
||||
if (phase == PhaseIndex::Vapour) {
|
||||
if (phase == ECLPhaseIndex::Vapour) {
|
||||
return vector + "GAS";
|
||||
}
|
||||
|
||||
@@ -2322,7 +2322,7 @@ std::size_t Opm::ECLGraph::numConnections() const
|
||||
return this->pImpl_->numConnections();
|
||||
}
|
||||
|
||||
const std::vector<Opm::ECLGraph::PhaseIndex>&
|
||||
const std::vector<Opm::ECLPhaseIndex >&
|
||||
Opm::ECLGraph::activePhases() const
|
||||
{
|
||||
return this->pImpl_->activePhases();
|
||||
@@ -2351,7 +2351,7 @@ std::vector<double> Opm::ECLGraph::transmissibility() const
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLGraph::flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex phase) const
|
||||
const ECLPhaseIndex phase) const
|
||||
{
|
||||
return this->pImpl_->flux(rstrt, phase);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef OPM_ECLGRAPH_HEADER_INCLUDED
|
||||
#define OPM_ECLGRAPH_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
@@ -45,9 +46,6 @@ namespace Opm {
|
||||
class ECLGraph
|
||||
{
|
||||
public:
|
||||
/// Enum for indicating requested phase from the flux() method.
|
||||
enum class PhaseIndex { Aqua = 0, Liquid = 1, Vapour = 2 };
|
||||
|
||||
/// Disabled default constructor.
|
||||
ECLGraph() = delete;
|
||||
|
||||
@@ -125,7 +123,7 @@ namespace Opm {
|
||||
///
|
||||
/// Mostly useful to determine the set of \c PhaseIndex values for
|
||||
/// which flux() will return non-zero values if data available.
|
||||
const std::vector<PhaseIndex>& activePhases() const;
|
||||
const std::vector<ECLPhaseIndex>& activePhases() const;
|
||||
|
||||
/// Retrieve the simulation scenario's set of active grids.
|
||||
///
|
||||
@@ -167,7 +165,7 @@ namespace Opm {
|
||||
/// (rm^3/s).
|
||||
std::vector<double>
|
||||
flux(const ECLRestartData& rstrt,
|
||||
const PhaseIndex phase) const;
|
||||
const ECLPhaseIndex phase) const;
|
||||
|
||||
/// Retrieve result set vector from current view (e.g., particular
|
||||
/// report step) linearised on active cells.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLPHASEINDEX_HEADER_INCLUDED
|
||||
#define OPM_ECLPHASEINDEX_HEADER_INCLUDED
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// Enum for indicating the phase--or set of phases--on which to apply a
|
||||
/// phase-dependent operation (e.g., extracting flux data from a result
|
||||
/// set or computing relative permeabilities from tabulated functions).
|
||||
enum class ECLPhaseIndex { Aqua = 0, Liquid = 1, Vapour = 2 };
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_ECLPHASEINDEX_HEADER_INCLUDED
|
||||
305
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPropTable.cpp
vendored
Normal file
305
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPropTable.cpp
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/utility/ECLPropTable.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
Opm::SatFuncInterpolant::SingleTable::
|
||||
SingleTable(ElmIt xBegin,
|
||||
ElmIt xEnd,
|
||||
std::vector<ElmIt>& colIt)
|
||||
{
|
||||
// There must be at least one dependent variable/result variable.
|
||||
assert (colIt.size() >= 1);
|
||||
|
||||
const auto nRows = std::distance(xBegin, xEnd);
|
||||
|
||||
this->x_.reserve(nRows);
|
||||
this->y_.reserve(nRows * colIt.size());
|
||||
|
||||
auto keyValid = [](const double xi)
|
||||
{
|
||||
// Indep. variable values <= -1.0e20 or >= 1.0e20 signal "unused"
|
||||
// table nodes (rows). These nodes are in the table to fill out the
|
||||
// allocated size if one particular sub-table does not use all
|
||||
// nodes. The magic value 1.0e20 is documented in the Fileformats
|
||||
// Reference Manual.
|
||||
return std::abs(xi) < 1.0e20;
|
||||
};
|
||||
|
||||
while (xBegin != xEnd) {
|
||||
// Extract relevant portion of the table. Preallocated rows that
|
||||
// are not actually part of the result set (i.e., those that are set
|
||||
// to a sentinel value) are discarded.
|
||||
if (keyValid(*xBegin)) {
|
||||
this->x_.push_back(*xBegin);
|
||||
|
||||
for (auto ci : colIt) {
|
||||
// Store 'y_' with column index cycling most rapidly.
|
||||
this->y_.push_back(*ci);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Advance iterators.
|
||||
|
||||
// 1) Independent variable.
|
||||
++xBegin;
|
||||
|
||||
// 2) Dependent/result/columns.
|
||||
for (auto& ci : colIt) {
|
||||
++ci;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose of any excess capacity.
|
||||
if (this->x_.size() < static_cast<decltype(this->x_.size())>(nRows)) {
|
||||
this->x_.shrink_to_fit();
|
||||
this->y_.shrink_to_fit();
|
||||
}
|
||||
|
||||
if (this->x_.size() < 2) {
|
||||
// Table has no interval that supports interpolation. Either just a
|
||||
// single node or no nodes at all. We can't do anything useful
|
||||
// here, so don't pretend that this is okay.
|
||||
|
||||
throw std::invalid_argument {
|
||||
"No Interpolation Intervals of Non-Zero Size"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::
|
||||
y(const ECLPropTableRawData::SizeType nCols,
|
||||
const ECLPropTableRawData::SizeType row,
|
||||
const ResultColumn& c) const
|
||||
{
|
||||
assert (row * nCols < this->y_.size());
|
||||
assert (c.i < nCols);
|
||||
|
||||
// Recall: 'y_' stored with column index cycling the most rapidly (row
|
||||
// major ordering).
|
||||
return this->y_[row*nCols + c.i];
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFuncInterpolant::SingleTable::
|
||||
interpolate(const ECLPropTableRawData::SizeType nCols,
|
||||
const ResultColumn& c,
|
||||
const std::vector<double>& x) const
|
||||
{
|
||||
auto y = std::vector<double>{}; y.reserve(x.size());
|
||||
|
||||
auto yval = [nCols, c, this]
|
||||
(const ECLPropTableRawData::SizeType i)
|
||||
{
|
||||
return this->y(nCols, i, c);
|
||||
};
|
||||
|
||||
const auto yfirst =
|
||||
yval(ECLPropTableRawData::SizeType{ 0 });
|
||||
|
||||
const auto ylast =
|
||||
yval(ECLPropTableRawData::SizeType{ this->x_.size() - 1 });
|
||||
|
||||
for (const auto& xi : x) {
|
||||
y.push_back(0.0);
|
||||
auto& yi = y.back();
|
||||
|
||||
if (! (xi > this->x_.front())) {
|
||||
// Constant extrapolation to the left of range.
|
||||
yi = yfirst;
|
||||
}
|
||||
else if (! (xi < this->x_.back())) {
|
||||
// Constant extrapolation to the right of range.
|
||||
yi = ylast;
|
||||
}
|
||||
else {
|
||||
// Somewhere in [min(x_), max(x_)]. Primary key (indep. var) is
|
||||
// sorted range. Recall: lower_bound() returns insertion point,
|
||||
// which translates to the *upper* (right-hand) end-point of the
|
||||
// interval in this context.
|
||||
auto b = std::begin(this->x_);
|
||||
auto p = std::lower_bound(b, std::end(this->x_), xi);
|
||||
|
||||
assert ((p != b) && "Logic Error Left End-Point");
|
||||
assert ((p != std::end(this->x_)) &&
|
||||
"Logic Error Right End-Point");
|
||||
|
||||
// p = lower_bound() => left == i-1, right == i-0.
|
||||
const auto i = p - b;
|
||||
const auto left = i - 1;
|
||||
const auto right = i - 0;
|
||||
|
||||
const auto xl = this->x_[left];
|
||||
const auto t = (xi - xl) / (this->x_[right] - xl);
|
||||
|
||||
yi = (1.0 - t)*yval(left) + t*yval(right);
|
||||
}
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::connateSat() const
|
||||
{
|
||||
return this->x_.front();
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::
|
||||
criticalSat(const ECLPropTableRawData::SizeType nCols,
|
||||
const ResultColumn& c) const
|
||||
{
|
||||
// Note: Relative permeability functions are presented as non-decreasing
|
||||
// functions of the corresponding phase saturation. The internal table
|
||||
// format essentially mirrors that of input deck keywords SWFN, SGFN,
|
||||
// and SOF* (i.e., saturation function family II). Extracting the
|
||||
// critical saturation--even for oil--therefore amounts to a forward,
|
||||
// linear scan from row=0 to row=n-1 irrespective of the input format of
|
||||
// the current saturation function.
|
||||
|
||||
const auto nRows = this->x_.size();
|
||||
|
||||
auto row = 0 * nRows;
|
||||
for (; row < nRows; ++row) {
|
||||
if (this->y(nCols, row, c) > 0.0) { break; }
|
||||
}
|
||||
|
||||
if (row == 0) {
|
||||
throw std::invalid_argument {
|
||||
"Table Does Not Define Critical Saturation"
|
||||
};
|
||||
}
|
||||
|
||||
return this->x_[row - 1];
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::maximumSat() const
|
||||
{
|
||||
return this->x_.back();
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
Opm::SatFuncInterpolant::SatFuncInterpolant(const ECLPropTableRawData& raw)
|
||||
: nResCols_(raw.numCols - 1)
|
||||
{
|
||||
if (raw.numCols < 2) {
|
||||
throw std::invalid_argument {
|
||||
"Malformed Property Table"
|
||||
};
|
||||
}
|
||||
|
||||
this->table_.reserve(raw.numTables);
|
||||
|
||||
// Table format: numRows*numTables values of first column (indep. var)
|
||||
// followed by numCols-1 dependent variable (function value result)
|
||||
// columns of numRows*numTables values each, one column at a time.
|
||||
const auto colStride = raw.numRows * raw.numTables;
|
||||
|
||||
// Position column iterators (independent variable and results
|
||||
// respectively) at beginning of each pertinent table column.
|
||||
auto xBegin = std::begin(raw.data);
|
||||
auto colIt = std::vector<decltype(xBegin)>{ xBegin + colStride };
|
||||
for (auto col = 0*raw.numCols + 1; col < raw.numCols - 1; ++col) {
|
||||
colIt.push_back(colIt.back() + colStride);
|
||||
}
|
||||
|
||||
for (auto t = 0*raw.numTables;
|
||||
t < raw.numTables;
|
||||
++t, xBegin += raw.numRows)
|
||||
{
|
||||
auto xEnd = xBegin + raw.numRows;
|
||||
|
||||
// Note: The SingleTable ctor advances each 'colIt' across numRows
|
||||
// entries. That is a bit of a layering violation, but helps in the
|
||||
// implementation of this loop.
|
||||
this->table_.push_back(SingleTable(xBegin, xEnd, colIt));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFuncInterpolant::interpolate(const InTable& t,
|
||||
const ResultColumn& c,
|
||||
const std::vector<double>& x) const
|
||||
{
|
||||
if (t.i >= this->table_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Invalid Table ID"
|
||||
};
|
||||
}
|
||||
|
||||
if (c.i >= this->nResCols_) {
|
||||
throw std::invalid_argument {
|
||||
"Invalid Result Column ID"
|
||||
};
|
||||
}
|
||||
|
||||
return this->table_[t.i].interpolate(this->nResCols_, c, x);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFuncInterpolant::connateSat() const
|
||||
{
|
||||
auto sconn = std::vector<double>{};
|
||||
sconn.reserve(this->table_.size());
|
||||
|
||||
for (const auto& t : this->table_) {
|
||||
sconn.push_back(t.connateSat());
|
||||
}
|
||||
|
||||
return sconn;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFuncInterpolant::criticalSat(const ResultColumn& c) const
|
||||
{
|
||||
auto scrit = std::vector<double>{};
|
||||
scrit.reserve(this->table_.size());
|
||||
|
||||
for (const auto& t : this->table_) {
|
||||
scrit.push_back(t.criticalSat(this->nResCols_, c));
|
||||
}
|
||||
|
||||
return scrit;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFuncInterpolant::maximumSat() const
|
||||
{
|
||||
auto smax = std::vector<double>{};
|
||||
smax.reserve(this->table_.size());
|
||||
|
||||
for (const auto& t : this->table_) {
|
||||
smax.push_back(t.maximumSat());
|
||||
}
|
||||
|
||||
return smax;
|
||||
}
|
||||
182
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPropTable.hpp
vendored
Normal file
182
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPropTable.hpp
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLPROPTABLE_HEADER_INCLUDED
|
||||
#define OPM_ECLPROPTABLE_HEADER_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// ECL Tabulated Functions (e.g., saturation functions).
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// Raw table data from which to construct collection of interpolants.
|
||||
struct ECLPropTableRawData
|
||||
{
|
||||
/// Representation of the raw table data. 1D array with implicit
|
||||
/// substructure.
|
||||
using DataVector = std::vector<double>;
|
||||
|
||||
/// Size type for subsets of table data.
|
||||
using SizeType = DataVector::size_type;
|
||||
|
||||
/// Iterator to table elements. Must be random access.
|
||||
using ElementIterator = DataVector::const_iterator;
|
||||
|
||||
/// Raw table data. Column major (Fortran) order. Typically
|
||||
/// copied/extracted directly from TAB vector of INIT result-set.
|
||||
DataVector data;
|
||||
|
||||
/// Number of rows allocated in the result set for each individual
|
||||
/// table. Typically corresponds to setting in one of the *DIMS
|
||||
/// keywords. Should normally be at least two.
|
||||
SizeType numRows;
|
||||
|
||||
/// Number of columns in this table. Varies by keyword/table.
|
||||
SizeType numCols;
|
||||
|
||||
/// Number of tables of this type. Must match the corresponding
|
||||
/// region keyword.
|
||||
SizeType numTables;
|
||||
};
|
||||
|
||||
/// Collection of 1D interpolants from tabulated functions (e.g., the
|
||||
/// saturation functions).
|
||||
class SatFuncInterpolant
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] raw Raw table data for this collection.
|
||||
explicit SatFuncInterpolant(const ECLPropTableRawData& raw);
|
||||
|
||||
/// Wrapper type to disambiguate API usage. Represents a table ID.
|
||||
struct InTable {
|
||||
/// Table ID.
|
||||
ECLPropTableRawData::SizeType i;
|
||||
};
|
||||
|
||||
/// Wrapper type to disambiguate API usage. Represents a column ID.
|
||||
struct ResultColumn {
|
||||
/// Column ID.
|
||||
ECLPropTableRawData::SizeType i;
|
||||
};
|
||||
|
||||
/// Evaluate 1D interpolant in sequence of points.
|
||||
///
|
||||
/// \param[in] t ID of sub-table of interpolant.
|
||||
///
|
||||
/// \param[in] c ID of result column/dependent variable.
|
||||
///
|
||||
/// \param[in] x Points at which to evaluate interpolant.
|
||||
///
|
||||
/// \return Function values of dependent variable \p c evaluated at
|
||||
/// points \p x in table \p t.
|
||||
std::vector<double>
|
||||
interpolate(const InTable& t,
|
||||
const ResultColumn& c,
|
||||
const std::vector<double>& x) const;
|
||||
|
||||
/// Retrieve connate saturation from all tables.
|
||||
std::vector<double> connateSat() const;
|
||||
|
||||
/// Retrieve critical saturation for particular result column in all
|
||||
/// tables.
|
||||
std::vector<double> criticalSat(const ResultColumn& c) const;
|
||||
|
||||
/// Retrieve maximum saturation in all tables.
|
||||
std::vector<double> maximumSat() const;
|
||||
|
||||
private:
|
||||
/// Single tabulated 1D interpolant.
|
||||
class SingleTable
|
||||
{
|
||||
public:
|
||||
using ElmIt = ECLPropTableRawData::ElementIterator;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] xBegin Beginning (initial element) of linar range
|
||||
/// of independent variable values.
|
||||
///
|
||||
/// \param[in] xEnd One past the end of linear range of
|
||||
/// independent variable values.
|
||||
///
|
||||
/// \param[in,out] colIt Dependent/column range iterators. On
|
||||
/// input, point to the beginnings of ranges of results
|
||||
/// pertinent to a single table. On output, each iterator is
|
||||
/// advanced across all rows of the SingleTable (including
|
||||
/// sentinel/invalid nodes) which makes the pointers valid
|
||||
/// for the next table if relevant (and called in a loop).
|
||||
SingleTable(ElmIt xBegin,
|
||||
ElmIt xEnd,
|
||||
std::vector<ElmIt>& colIt);
|
||||
|
||||
/// Evaluate 1D interpolant in sequence of points.
|
||||
///
|
||||
/// \param[in] nCols Number of table columns.
|
||||
///
|
||||
/// \param[in] c ID of result column/dependent variable.
|
||||
///
|
||||
/// \param[in] x Points at which to evaluate interpolant.
|
||||
///
|
||||
/// \return Function values of dependent variable \p c evaluated
|
||||
/// at points \p x.
|
||||
std::vector<double>
|
||||
interpolate(const ECLPropTableRawData::SizeType nCols,
|
||||
const ResultColumn& c,
|
||||
const std::vector<double>& x) const;
|
||||
|
||||
/// Retrieve connate saturation in table.
|
||||
double connateSat() const;
|
||||
|
||||
/// Retrieve critical saturation for particular result column in
|
||||
/// table.
|
||||
double criticalSat(const ECLPropTableRawData::SizeType nCols,
|
||||
const ResultColumn& c) const;
|
||||
|
||||
/// Retrieve maximum saturation in table.
|
||||
double maximumSat() const;
|
||||
|
||||
private:
|
||||
/// Independent variable.
|
||||
std::vector<double> x_;
|
||||
|
||||
/// Dependent variable (or variables). Row major (i.e., C)
|
||||
/// ordering. Number of elements: x_.size() * host.nCols_.
|
||||
std::vector<double> y_;
|
||||
|
||||
/// Value of dependent variable at position (row,c).
|
||||
double y(const ECLPropTableRawData::SizeType nCols,
|
||||
const ECLPropTableRawData::SizeType row,
|
||||
const ResultColumn& c) const;
|
||||
};
|
||||
|
||||
/// Number of result/dependent variables (== #table cols - 1).
|
||||
ECLPropTableRawData::SizeType nResCols_;
|
||||
|
||||
/// Sequence of individual tables, indexed by *NUM-type vectors.
|
||||
std::vector<SingleTable> table_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_ECLPROPTABLE_HEADER_INCLUDED
|
||||
@@ -112,7 +112,9 @@ namespace {
|
||||
result.reserve(x.size());
|
||||
|
||||
for (const auto& xi : x) {
|
||||
result.emplace_back(xi);
|
||||
// push_back(T(xi)) because vector<bool> does not
|
||||
// support emplace_back until C++14.
|
||||
result.push_back(Output(xi));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -188,6 +190,17 @@ namespace {
|
||||
using type = void;
|
||||
};
|
||||
|
||||
/// Translate ERT type class to keyword element type.
|
||||
///
|
||||
/// Actual element type of \code ECL_INT_TYPE \endcode.
|
||||
template <>
|
||||
struct ElementType<ECL_BOOL_TYPE>
|
||||
{
|
||||
/// Element type of ERT Boolean (LOGICAL) data. Stored
|
||||
/// internally as 'int'.
|
||||
using type = int;
|
||||
};
|
||||
|
||||
/// Translate ERT type class to keyword element type.
|
||||
///
|
||||
/// Actual element type of \code ECL_INT_TYPE \endcode.
|
||||
@@ -226,6 +239,37 @@ namespace {
|
||||
template <ecl_type_enum Input>
|
||||
struct ExtractKeywordElements;
|
||||
|
||||
/// Extract ERT keyword Boolean (LOGICAL) data.
|
||||
template <>
|
||||
struct ExtractKeywordElements<ECL_BOOL_TYPE>
|
||||
{
|
||||
using EType = ElementType<ECL_BOOL_TYPE>::type;
|
||||
|
||||
/// Function call operator.
|
||||
///
|
||||
/// Retrieve actual data elements from ERT keyword of integer
|
||||
/// (specifically, \c int) type.
|
||||
///
|
||||
/// \param[in] kw ERT keyword instance.
|
||||
///
|
||||
/// \param[in,out] x Linearised keyword data elements. On
|
||||
/// input points to memory block of size \code
|
||||
/// ecl_kw_get_size(kw) * sizeof *x \endcode bytes. On
|
||||
/// output, those bytes are filled with the actual data
|
||||
/// values of \p kw.
|
||||
void operator()(const ecl_kw_type* kw, EType* x) const
|
||||
{
|
||||
// 1) Extract raw 'int' values.
|
||||
ecl_kw_get_memcpy_int_data(kw, x);
|
||||
|
||||
// 2) Convert to 'bool'-like values by comparing to
|
||||
// magic constant ECL_BOOL_TRUE_INT (ecl_util.h).
|
||||
for (auto n = ecl_kw_get_size(kw), i = 0*n; i < n; ++i) {
|
||||
x[i] = static_cast<EType>(x[i] == ECL_BOOL_TRUE_INT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Extract ERT keyword integer data.
|
||||
template <>
|
||||
struct ExtractKeywordElements<ECL_INT_TYPE>
|
||||
@@ -489,6 +533,10 @@ namespace {
|
||||
return GetKeywordData<ECL_CHAR_TYPE>::
|
||||
as<T>(kw, makeStringVector);
|
||||
|
||||
case ECL_BOOL_TYPE:
|
||||
return GetKeywordData<ECL_BOOL_TYPE>::
|
||||
as<T>(kw, makeStringVector);
|
||||
|
||||
case ECL_INT_TYPE:
|
||||
return GetKeywordData<ECL_INT_TYPE>::
|
||||
as<T>(kw, makeStringVector);
|
||||
@@ -1569,6 +1617,10 @@ namespace Opm {
|
||||
ECLRestartData::keywordData<std::string>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
|
||||
template std::vector<bool>
|
||||
ECLRestartData::keywordData<bool>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
|
||||
template std::vector<int>
|
||||
ECLRestartData::keywordData<int>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
@@ -1647,6 +1699,10 @@ namespace Opm {
|
||||
ECLInitFileData::keywordData<std::string>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
|
||||
template std::vector<bool>
|
||||
ECLInitFileData::keywordData<bool>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
|
||||
template std::vector<int>
|
||||
ECLInitFileData::keywordData<int>(const std::string& vector,
|
||||
const std::string& gridID) const;
|
||||
|
||||
1272
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLSaturationFunc.cpp
vendored
Normal file
1272
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLSaturationFunc.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLSATURATIONFUNC_HEADER_INCLUDED
|
||||
#define OPM_ECLSATURATIONFUNC_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// Public interface to relative permeability evaluation machinery. The
|
||||
/// back-end is aware of ECLIPSE's standard three-phase model for relative
|
||||
/// permeability of oil and the two- and three-point saturation end-point
|
||||
/// scaling options. Vertical scaling of relative permeability is not
|
||||
/// supported at present.
|
||||
|
||||
namespace Opm {
|
||||
class ECLGraph;
|
||||
class ECLRestartData;
|
||||
class ECLInitFileData;
|
||||
|
||||
/// Gateway to engine for computing relative permeability values based
|
||||
/// on tabulated saturation functions in ECL output.
|
||||
class ECLSaturationFunc
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
///
|
||||
/// \param[in] G Connected topology of current model's active cells.
|
||||
/// Needed to linearise region mapping (e.g., SATNUM) that is
|
||||
/// distributed on local grids to all of the model's active cells
|
||||
/// (\code member function G.rawLinearisedCellData() \endcode).
|
||||
///
|
||||
/// \param[in] init Container of tabulated saturation functions and
|
||||
/// saturation table end points, if applicable, for all active
|
||||
/// cells in the model \p G.
|
||||
///
|
||||
/// \param[in] useEPS Whether or not to include effects of
|
||||
/// saturation end-point scaling. No effect if the INIT result
|
||||
/// set does not actually include saturation end-point scaling
|
||||
/// data. Otherwise, enables turning EPS off even if associate
|
||||
/// data is present in the INIT result set.
|
||||
///
|
||||
/// Default value (\c true) means that effects of EPS are
|
||||
/// included if requisite data is present in the INIT result.
|
||||
ECLSaturationFunc(const ECLGraph& G,
|
||||
const ECLInitFileData& init,
|
||||
const bool useEPS = true);
|
||||
|
||||
/// Destructor.
|
||||
~ECLSaturationFunc();
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing engine for saturation function
|
||||
/// evaluation. Does not have a valid implementation when the
|
||||
/// constructor completes.
|
||||
ECLSaturationFunc(ECLSaturationFunc&& rhs);
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing engine for saturation function
|
||||
/// evaluation.
|
||||
ECLSaturationFunc(const ECLSaturationFunc& rhs);
|
||||
|
||||
/// Move assignment operator.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing engine for saturation function
|
||||
/// evaluation. Does not have a valid implementation when the
|
||||
/// constructor completes.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
ECLSaturationFunc& operator=(ECLSaturationFunc&& rhs);
|
||||
|
||||
/// Assignment operator.
|
||||
///
|
||||
/// \param[in] rhs Existing engine for saturation function
|
||||
/// evaluation.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
ECLSaturationFunc& operator=(const ECLSaturationFunc& rhs);
|
||||
|
||||
/// Compute relative permeability values in all active cells for a
|
||||
/// single phase.
|
||||
///
|
||||
/// \param[in] G Connected topology of current model's active cells.
|
||||
/// Needed to linearise phase saturations (e.g., SOIL) that are
|
||||
/// distributed on local grids to all of the model's active cells
|
||||
/// (\code member function G.rawLinearisedCellData() \endcode).
|
||||
///
|
||||
/// \param[in] rstrt ECLIPSE restart vectors. Result set view
|
||||
/// assumed to be positioned at a particular report step of
|
||||
/// interest.
|
||||
///
|
||||
/// \param[in] p Phase for which to compute relative permeability
|
||||
/// values.
|
||||
///
|
||||
/// \return Derived relative permeability values of active phase \p
|
||||
/// p for all active cells in model \p G. Empty if phase \p p is
|
||||
/// not actually active in the current result set.
|
||||
std::vector<double>
|
||||
relperm(const ECLGraph& G,
|
||||
const ECLRestartData& rstrt,
|
||||
const ECLPhaseIndex p) const;
|
||||
|
||||
private:
|
||||
/// Implementation backend.
|
||||
class Impl;
|
||||
|
||||
/// Pointer to actual backend/engine object.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_ECLSATURATIONFUNC_HEADER_INCLUDED
|
||||
@@ -554,6 +554,14 @@ namespace {
|
||||
return errorAcceptable(E.absolute, tol.absolute)
|
||||
&& errorAcceptable(E.relative, tol.relative);
|
||||
}
|
||||
|
||||
::Opm::ECLGraph
|
||||
constructGraph(const example::FilePaths& pth)
|
||||
{
|
||||
const auto I = ::Opm::ECLInitFileData(pth.init);
|
||||
|
||||
return ::Opm::ECLGraph::load(pth.grid, I);
|
||||
}
|
||||
} // namespace Anonymous
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
@@ -564,7 +572,7 @@ try {
|
||||
|
||||
const auto rstrt = ::Opm::ECLRestartData(pth.restart);
|
||||
const auto steps = availableReportSteps(pth);
|
||||
const auto graph = example::initGraph(pth);
|
||||
const auto graph = constructGraph(pth);
|
||||
|
||||
auto all_ok = true;
|
||||
for (const auto& quant : testQuantities(prm)) {
|
||||
|
||||
@@ -206,13 +206,21 @@ namespace {
|
||||
return ! ((pointMetric(diff) > tol.absolute) ||
|
||||
(pointMetric(rat) > tol.relative));
|
||||
}
|
||||
|
||||
::Opm::ECLGraph
|
||||
constructGraph(const example::FilePaths& pth)
|
||||
{
|
||||
const auto I = ::Opm::ECLInitFileData(pth.init);
|
||||
|
||||
return ::Opm::ECLGraph::load(pth.grid, I);
|
||||
}
|
||||
} // namespace Anonymous
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
try {
|
||||
const auto prm = example::initParam(argc, argv);
|
||||
const auto pth = example::FilePaths(prm);
|
||||
const auto G = example::initGraph(pth);
|
||||
const auto G = constructGraph(pth);
|
||||
const auto T = G.transmissibility();
|
||||
const auto ok = transfieldAcceptable(prm, T);
|
||||
|
||||
|
||||
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
Copyright 2017 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media Project (OPM).
|
||||
|
||||
OPM 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.
|
||||
|
||||
OPM 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 for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#if HAVE_DYNAMIC_BOOST_TEST
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#endif
|
||||
|
||||
#define NVERBOSE
|
||||
|
||||
#define BOOST_TEST_MODULE TEST_ECLENDPOINTSCALING
|
||||
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
|
||||
#include <opm/utility/ECLEndPointScaling.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
template <class Collection1, class Collection2>
|
||||
void check_is_close(const Collection1& c1, const Collection2& c2)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(c1.size(), c2.size());
|
||||
|
||||
if (! c1.empty()) {
|
||||
auto i1 = c1.begin(), e1 = c1.end();
|
||||
auto i2 = c2.begin();
|
||||
|
||||
for (; i1 != e1; ++i1, ++i2) {
|
||||
BOOST_CHECK_CLOSE(*i1, *i2, 1.0e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::Opm::SatFunc::EPSEvalInterface::SaturationPoints
|
||||
associate(const std::vector<double>& s)
|
||||
{
|
||||
using SatAssoc = ::Opm::SatFunc::
|
||||
EPSEvalInterface::SaturationAssoc;
|
||||
|
||||
auto sp = ::Opm::SatFunc::
|
||||
EPSEvalInterface::SaturationPoints{};
|
||||
|
||||
sp.reserve(s.size());
|
||||
|
||||
for (const auto& si : s) {
|
||||
sp.push_back(SatAssoc{ 0, si });
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
} // Namespace Anonymous
|
||||
|
||||
// =====================================================================
|
||||
// Two-point scaling
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (TwoPointScaling_FullRange)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.0, 1.0 };
|
||||
|
||||
const auto smin = std::vector<double>{ 0.0 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.2, 1.0] maps to [ 0.0, 1.0 ]
|
||||
const auto smin = std::vector<double>{ 0.2 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.0, 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.0, 0.8] maps to [ 0.0, 1.0 ]
|
||||
const auto smin = std::vector<double>{ 0.0 };
|
||||
const auto smax = std::vector<double>{ 0.8 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.0, 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1.0,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.2, 0.8] maps to [ 0.0, 1.0 ]
|
||||
const auto smin = std::vector<double>{ 0.2 };
|
||||
const auto smax = std::vector<double>{ 0.8 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.0, 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0.0,
|
||||
1.0 / 3.0,
|
||||
2.0 / 3.0,
|
||||
1.0,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
||||
// =====================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (TwoPointScaling_ReducedRange)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
const auto smin = std::vector<double>{ 0.2 };
|
||||
const auto smax = std::vector<double>{ 0.8 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.2, 0.0, 0.8 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.2,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
0.8,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.0, 1.0] maps to [ 0.2, 0.8 ]
|
||||
// s_eff = 0.6*s + 0.2
|
||||
const auto smin = std::vector<double>{ 0.0 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.2, 0.0, 0.8 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.20,
|
||||
0.32,
|
||||
0.44,
|
||||
0.56,
|
||||
0.68,
|
||||
0.80,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.2, 0.8] maps to [ 0.0, 1.0 ]
|
||||
// s_eff = max(0.75*s + 0.05, 0.2)
|
||||
const auto smin = std::vector<double>{ 0.2 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.2, 0.0, 0.8 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.20,
|
||||
0.20,
|
||||
0.35,
|
||||
0.50,
|
||||
0.65,
|
||||
0.80,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.2, 0.8] maps to [ 0.5, 0.7 ]
|
||||
// s_eff = min(max(0.2, 3*s - 13/10), 0.8)
|
||||
const auto smin = std::vector<double>{ 0.5 };
|
||||
const auto smax = std::vector<double>{ 0.7 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.2, 0.0, 0.8 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.5,
|
||||
0.8,
|
||||
0.8,
|
||||
};
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
||||
// =====================================================================
|
||||
// Three-point (alternative) scaling, applicable to relperm only.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (ThreePointScaling_FullRange)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.2, 1.0 };
|
||||
|
||||
const auto smin = std::vector<double>{ 0.0 };
|
||||
const auto sdisp = std::vector<double>{ 0.2 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::ThreePointScaling{ smin, sdisp, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
{
|
||||
namespace SF = ::Opm::SatFunc;
|
||||
|
||||
// Mobile Range: [0.4, 1.0] maps to [ 0.0, 1.0 ]
|
||||
const auto smin = std::vector<double>{ 0.1 };
|
||||
const auto sdisp = std::vector<double>{ 0.4 };
|
||||
const auto smax = std::vector<double>{ 1.0 };
|
||||
|
||||
const auto tep = SF::EPSEvalInterface::
|
||||
TableEndPoints { 0.0, 0.2, 1.0 };
|
||||
|
||||
const auto s = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
1.0 / 15,
|
||||
0.2,
|
||||
7.0 / 15,
|
||||
11.0 / 15,
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto eps = SF::ThreePointScaling{ smin, sdisp, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
2417
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclproptable.cpp
vendored
Normal file
2417
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclproptable.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -69,11 +69,13 @@ namespace FlowDiagnostics
|
||||
/// al. (SPE 146446), Shook and Mitchell (SPE 124625).
|
||||
Graph flowCapacityStorageCapacityCurve(const Toolbox::Forward& injector_solution,
|
||||
const Toolbox::Reverse& producer_solution,
|
||||
const std::vector<double>& pv)
|
||||
const std::vector<double>& pv,
|
||||
const double max_pv_fraction)
|
||||
{
|
||||
return flowCapacityStorageCapacityCurve(injector_solution.fd.timeOfFlight(),
|
||||
producer_solution.fd.timeOfFlight(),
|
||||
pv);
|
||||
pv,
|
||||
max_pv_fraction);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,20 +90,30 @@ namespace FlowDiagnostics
|
||||
/// al. (SPE 146446), Shook and Mitchell (SPE 124625).
|
||||
Graph flowCapacityStorageCapacityCurve(const std::vector<double>& injector_tof,
|
||||
const std::vector<double>& producer_tof,
|
||||
const std::vector<double>& pv)
|
||||
const std::vector<double>& pv,
|
||||
const double max_pv_fraction)
|
||||
{
|
||||
if (pv.size() != injector_tof.size() || pv.size() != producer_tof.size()) {
|
||||
throw std::runtime_error("flowCapacityStorageCapacityCurve(): "
|
||||
"Input solutions must have same size.");
|
||||
}
|
||||
|
||||
// Compute max pv cutoff.
|
||||
const double total_pv = std::accumulate(pv.begin(), pv.end(), 0.0);
|
||||
const double max_pv = max_pv_fraction * total_pv;
|
||||
|
||||
// Sort according to total travel time.
|
||||
const int n = pv.size();
|
||||
typedef std::pair<double, double> D2;
|
||||
std::vector<D2> time_and_pv(n);
|
||||
for (int ii = 0; ii < n; ++ii) {
|
||||
time_and_pv[ii].first = injector_tof[ii] + producer_tof[ii]; // Total travel time.
|
||||
time_and_pv[ii].second = pv[ii];
|
||||
if (pv[ii] > max_pv) {
|
||||
time_and_pv[ii].first = 1e100;
|
||||
time_and_pv[ii].second = 0.0;
|
||||
} else {
|
||||
time_and_pv[ii].first = injector_tof[ii] + producer_tof[ii]; // Total travel time.
|
||||
time_and_pv[ii].second = pv[ii];
|
||||
}
|
||||
}
|
||||
std::sort(time_and_pv.begin(), time_and_pv.end());
|
||||
|
||||
|
||||
@@ -44,11 +44,19 @@ namespace FlowDiagnostics
|
||||
/// coefficient. For a technical description see Shavali et
|
||||
/// al. (SPE 146446), Shook and Mitchell (SPE 124625).
|
||||
///
|
||||
/// Single cells with a very large pore volume can be filtered out
|
||||
/// before creating the curve. The 'max_pv_fraction' parameter
|
||||
/// gives a fraction such that, if a cell's fraction of the total
|
||||
/// pore volume is above this number, that cell will be
|
||||
/// ignored. This can be used to disregard numerical aquifers for
|
||||
/// example.
|
||||
///
|
||||
/// Returns F (flow capacity) as a function of Phi (storage capacity),
|
||||
/// that is for the returned Graph g, g.first is Phi and g.second is F.
|
||||
Graph flowCapacityStorageCapacityCurve(const Toolbox::Forward& injector_solution,
|
||||
const Toolbox::Reverse& producer_solution,
|
||||
const std::vector<double>& pore_volume);
|
||||
const std::vector<double>& pore_volume,
|
||||
const double max_pv_fraction = 1.0);
|
||||
|
||||
/// This overload gets the injector and producer time-of-flight
|
||||
/// directly instead of extracting it from the solution
|
||||
@@ -56,7 +64,8 @@ namespace FlowDiagnostics
|
||||
/// overload.
|
||||
Graph flowCapacityStorageCapacityCurve(const std::vector<double>& injector_tof,
|
||||
const std::vector<double>& producer_tof,
|
||||
const std::vector<double>& pore_volume);
|
||||
const std::vector<double>& pore_volume,
|
||||
const double max_pv_fraction = 1.0);
|
||||
|
||||
/// The Lorenz coefficient from the F-Phi curve.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user