mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
#1988 Update flow diag libraries to handle multiple connections in same well, and have PVT Rel Perm support.
This commit is contained in:
parent
6c51938000
commit
4d097a3149
@ -110,7 +110,7 @@ namespace RigFlowDiagInterfaceTools {
|
||||
{
|
||||
auto satfunc = Opm::ECLSaturationFunc(G, init);
|
||||
|
||||
Opm::ECLFluxCalc calc(G, std::move(satfunc));
|
||||
Opm::ECLFluxCalc calc(G, init, 9.80665, true);
|
||||
|
||||
auto getFlux = [&calc, &rstrt]
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
@ -122,12 +122,13 @@ namespace RigFlowDiagInterfaceTools {
|
||||
}
|
||||
|
||||
template <class WellFluxes>
|
||||
Opm::FlowDiagnostics::CellSetValues
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues>
|
||||
extractWellFlows(const Opm::ECLGraph& G,
|
||||
const WellFluxes& well_fluxes)
|
||||
const WellFluxes& well_fluxes)
|
||||
{
|
||||
Opm::FlowDiagnostics::CellSetValues inflow;
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues> well_flows;
|
||||
for (const auto& well : well_fluxes) {
|
||||
Opm::FlowDiagnostics::CellSetValues& inflow = well_flows[Opm::FlowDiagnostics::CellSetID(well.name)];
|
||||
for (const auto& completion : well.completions) {
|
||||
const auto& gridName = completion.gridName;
|
||||
const auto& ijk = completion.ijk;
|
||||
@ -143,7 +144,7 @@ namespace RigFlowDiagInterfaceTools {
|
||||
}
|
||||
}
|
||||
|
||||
return inflow;
|
||||
return well_flows;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -147,6 +147,63 @@ RigFlowDiagSolverInterface::~RigFlowDiagSolverInterface()
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void removeCrossFlowCells(std::pair<const std::string, std::vector<int>> & tracerCellIdxsPair,
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues> & WellInFluxPrCell,
|
||||
std::function<bool(double)> isFlowOkFunction)
|
||||
{
|
||||
std::string tracerName = tracerCellIdxsPair.first;
|
||||
tracerName = RimFlowDiagSolution::removeCrossFlowEnding(QString::fromStdString(tracerName)).toStdString();
|
||||
auto cellSetIdInFlowsPair = WellInFluxPrCell.find(Opm::FlowDiagnostics::CellSetID(tracerName));
|
||||
|
||||
CVF_TIGHT_ASSERT(cellSetIdInFlowsPair != WellInFluxPrCell.end());
|
||||
|
||||
std::vector<int> filteredCellIndices;
|
||||
|
||||
for ( int activeCellIdx : tracerCellIdxsPair.second )
|
||||
{
|
||||
auto activeCellIdxFluxPair = cellSetIdInFlowsPair->second.find(activeCellIdx);
|
||||
CVF_TIGHT_ASSERT(activeCellIdxFluxPair != cellSetIdInFlowsPair->second.end());
|
||||
|
||||
if ( isFlowOkFunction(activeCellIdxFluxPair->second) )
|
||||
{
|
||||
filteredCellIndices.push_back(activeCellIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if ( tracerCellIdxsPair.second.size() != filteredCellIndices.size() )
|
||||
{
|
||||
tracerCellIdxsPair.second = filteredCellIndices;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string removeCrossFlowEnding(std::string tracerName)
|
||||
{
|
||||
return RimFlowDiagSolution::removeCrossFlowEnding(QString::fromStdString(tracerName)).toStdString();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool hasCrossFlowEnding(std::string tracerName)
|
||||
{
|
||||
return RimFlowDiagSolution::hasCrossFlowEnding(QString::fromStdString(tracerName));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::string addCrossFlowEnding(std::string tracerName)
|
||||
{
|
||||
return RimFlowDiagSolution::addCrossFlowEnding(QString::fromStdString(tracerName)).toStdString();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
@ -253,7 +310,7 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
|
||||
|
||||
// Set up flow Toolbox with timestep data
|
||||
Opm::FlowDiagnostics::CellSetValues sumWellFluxPrCell;
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues> WellInFluxPrCell;
|
||||
|
||||
{
|
||||
if (m_eclipseCase->eclipseCaseData()->results(RiaDefines::MATRIX_MODEL)->hasFlowDiagUsableFluxes())
|
||||
@ -266,7 +323,10 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
else
|
||||
{
|
||||
Opm::ECLInitFileData init(getInitFileName());
|
||||
Opm::FlowDiagnostics::ConnectionValues connectionVals = RigFlowDiagInterfaceTools::calculateFluxField((*m_opmFlowDiagStaticData->m_eclGraph), init, *currentRestartData, phaseSelection);
|
||||
Opm::FlowDiagnostics::ConnectionValues connectionVals = RigFlowDiagInterfaceTools::calculateFluxField((*m_opmFlowDiagStaticData->m_eclGraph),
|
||||
init,
|
||||
*currentRestartData,
|
||||
phaseSelection);
|
||||
m_opmFlowDiagStaticData->m_fldToolbox->assignConnectionFlux(connectionVals);
|
||||
}
|
||||
|
||||
@ -279,20 +339,20 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
|
||||
const std::vector<Opm::ECLWellSolution::WellData> well_fluxes = wsol.solution(*currentRestartData, gridNames);
|
||||
|
||||
sumWellFluxPrCell = RigFlowDiagInterfaceTools::extractWellFlows(*(m_opmFlowDiagStaticData->m_eclGraph), well_fluxes);
|
||||
WellInFluxPrCell = RigFlowDiagInterfaceTools::extractWellFlows(*(m_opmFlowDiagStaticData->m_eclGraph), well_fluxes);
|
||||
|
||||
m_opmFlowDiagStaticData->m_fldToolbox->assignInflowFlux(sumWellFluxPrCell);
|
||||
m_opmFlowDiagStaticData->m_fldToolbox->assignInflowFlux(WellInFluxPrCell);
|
||||
|
||||
#if 0
|
||||
// Start Hack: Filter connection cells with inconsistent well in flow direction (Hack, we should do something better)
|
||||
|
||||
for ( auto& tracerCellIdxsPair: injectorTracers )
|
||||
{
|
||||
std::vector<int> filteredCellIndices;
|
||||
|
||||
for (int activeCellIdx : tracerCellIdxsPair.second)
|
||||
{
|
||||
auto activeCellIdxFluxPair = sumWellFluxPrCell.find(activeCellIdx);
|
||||
CVF_TIGHT_ASSERT(activeCellIdxFluxPair != sumWellFluxPrCell.end());
|
||||
auto activeCellIdxFluxPair = WellInFluxPrCell.find(activeCellIdx);
|
||||
CVF_TIGHT_ASSERT(activeCellIdxFluxPair != WellInFluxPrCell.end());
|
||||
|
||||
if (activeCellIdxFluxPair->second > 0 )
|
||||
{
|
||||
@ -309,8 +369,8 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
|
||||
for (int activeCellIdx : tracerCellIdxsPair.second)
|
||||
{
|
||||
auto activeCellIdxFluxPair = sumWellFluxPrCell.find(activeCellIdx);
|
||||
CVF_TIGHT_ASSERT(activeCellIdxFluxPair != sumWellFluxPrCell.end());
|
||||
auto activeCellIdxFluxPair = WellInFluxPrCell.find(activeCellIdx);
|
||||
CVF_TIGHT_ASSERT(activeCellIdxFluxPair != WellInFluxPrCell.end());
|
||||
|
||||
if (activeCellIdxFluxPair->second < 0 )
|
||||
{
|
||||
@ -319,8 +379,20 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
}
|
||||
if (tracerCellIdxsPair.second.size() != filteredCellIndices.size()) tracerCellIdxsPair.second = filteredCellIndices;
|
||||
}
|
||||
|
||||
|
||||
// End Hack
|
||||
// New Filtering Probably not neccesary
|
||||
for ( auto& tracerCellIdxsPair: injectorTracers )
|
||||
{
|
||||
removeCrossFlowCells(tracerCellIdxsPair, WellInFluxPrCell, [](double inFlow){ return inFlow > 0;});
|
||||
}
|
||||
|
||||
for ( auto& tracerCellIdxsPair: producerTracers )
|
||||
{
|
||||
removeCrossFlowCells(tracerCellIdxsPair, WellInFluxPrCell, [](double inFlow){ return inFlow < 0;});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
progressInfo.incrementProgress();
|
||||
@ -328,60 +400,82 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
|
||||
{
|
||||
// Injection Solution
|
||||
|
||||
std::set<std::string> injectorCrossFlowTracers;
|
||||
std::vector<CellSet> injectorCellSets;
|
||||
for ( const auto& tIt: injectorTracers )
|
||||
{
|
||||
injectorCellSets.push_back(CellSet(CellSetID(tIt.first), tIt.second));
|
||||
}
|
||||
|
||||
std::unique_ptr<Toolbox::Forward> injectorSolution;
|
||||
try
|
||||
{
|
||||
injectorSolution.reset(new Toolbox::Forward( m_opmFlowDiagStaticData->m_fldToolbox->computeInjectionDiagnostics(injectorCellSets)));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
QMessageBox::critical(nullptr, "ResInsight", "Flow Diagnostics: " + QString(e.what()));
|
||||
return result;
|
||||
}
|
||||
for ( const auto& tIt: injectorTracers )
|
||||
{
|
||||
std::string tracerName = tIt.first;
|
||||
if (hasCrossFlowEnding(tracerName))
|
||||
{
|
||||
tracerName = removeCrossFlowEnding(tracerName);
|
||||
injectorCrossFlowTracers.insert(tracerName);
|
||||
}
|
||||
injectorCellSets.push_back(CellSet(CellSetID(tracerName), tIt.second));
|
||||
}
|
||||
|
||||
for ( const CellSetID& tracerId: injectorSolution->fd.startPoints() )
|
||||
{
|
||||
CellSetValues tofVals = injectorSolution->fd.timeOfFlight(tracerId);
|
||||
result.setTracerTOF(tracerId.to_string(), phaseSelection, tofVals);
|
||||
CellSetValues fracVals = injectorSolution->fd.concentration(tracerId);
|
||||
result.setTracerFraction(tracerId.to_string(), phaseSelection, fracVals);
|
||||
try
|
||||
{
|
||||
injectorSolution.reset(new Toolbox::Forward(m_opmFlowDiagStaticData->m_fldToolbox->computeInjectionDiagnostics(injectorCellSets)));
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
QMessageBox::critical(nullptr, "ResInsight", "Flow Diagnostics: " + QString(e.what()));
|
||||
return result;
|
||||
}
|
||||
|
||||
for ( const CellSetID& tracerId: injectorSolution->fd.startPoints() )
|
||||
{
|
||||
std::string tracername = tracerId.to_string();
|
||||
if (injectorCrossFlowTracers.count(tracername)) tracername = addCrossFlowEnding(tracername);
|
||||
|
||||
CellSetValues tofVals = injectorSolution->fd.timeOfFlight(tracerId);
|
||||
result.setTracerTOF(tracername, phaseSelection, tofVals);
|
||||
CellSetValues fracVals = injectorSolution->fd.concentration(tracerId);
|
||||
result.setTracerFraction(tracername, phaseSelection, fracVals);
|
||||
}
|
||||
}
|
||||
|
||||
progressInfo.incrementProgress();
|
||||
progressInfo.setProgressDescription("Producer Solution");
|
||||
|
||||
// Producer Solution
|
||||
|
||||
std::set<std::string> producerCrossFlowTracers;
|
||||
std::vector<CellSet> prodjCellSets;
|
||||
for ( const auto& tIt: producerTracers )
|
||||
{
|
||||
prodjCellSets.push_back(CellSet(CellSetID(tIt.first), tIt.second));
|
||||
}
|
||||
|
||||
std::unique_ptr<Toolbox::Reverse> producerSolution;
|
||||
try
|
||||
{
|
||||
producerSolution.reset(new Toolbox::Reverse(m_opmFlowDiagStaticData->m_fldToolbox->computeProductionDiagnostics(prodjCellSets)));
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
QMessageBox::critical(nullptr, "ResInsight", "Flow Diagnostics: " + QString(e.what()));
|
||||
return result;
|
||||
}
|
||||
for ( const auto& tIt: producerTracers )
|
||||
{
|
||||
std::string tracerName = tIt.first;
|
||||
if (hasCrossFlowEnding(tracerName))
|
||||
{
|
||||
tracerName = removeCrossFlowEnding(tracerName);
|
||||
producerCrossFlowTracers.insert(tracerName);
|
||||
}
|
||||
prodjCellSets.push_back(CellSet(CellSetID(tracerName), tIt.second));
|
||||
}
|
||||
|
||||
for ( const CellSetID& tracerId: producerSolution->fd.startPoints() )
|
||||
{
|
||||
CellSetValues tofVals = producerSolution->fd.timeOfFlight(tracerId);
|
||||
result.setTracerTOF(tracerId.to_string(), phaseSelection, tofVals);
|
||||
CellSetValues fracVals = producerSolution->fd.concentration(tracerId);
|
||||
result.setTracerFraction(tracerId.to_string(), phaseSelection, fracVals);
|
||||
try
|
||||
{
|
||||
producerSolution.reset(new Toolbox::Reverse(m_opmFlowDiagStaticData->m_fldToolbox->computeProductionDiagnostics(prodjCellSets)));
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
QMessageBox::critical(nullptr, "ResInsight", "Flow Diagnostics: " + QString(e.what()));
|
||||
return result;
|
||||
}
|
||||
|
||||
for ( const CellSetID& tracerId: producerSolution->fd.startPoints() )
|
||||
{
|
||||
std::string tracername = tracerId.to_string();
|
||||
if (producerCrossFlowTracers.count(tracername)) tracername = addCrossFlowEnding(tracername);
|
||||
|
||||
CellSetValues tofVals = producerSolution->fd.timeOfFlight(tracerId);
|
||||
result.setTracerTOF(tracername, phaseSelection, tofVals);
|
||||
CellSetValues fracVals = producerSolution->fd.concentration(tracerId);
|
||||
result.setTracerFraction(tracername, phaseSelection, fracVals);
|
||||
}
|
||||
}
|
||||
|
||||
progressInfo.incrementProgress();
|
||||
@ -394,17 +488,37 @@ RigFlowDiagTimeStepResult RigFlowDiagSolverInterface::calculate(size_t timeStepI
|
||||
{
|
||||
const auto& prodCellSet = prodjCellSets[pIdx];
|
||||
|
||||
std::string prodTracerName = prodCellSet.id().to_string();
|
||||
CellSetID prodID(prodTracerName);
|
||||
|
||||
std::string uiProducerTracerName = prodTracerName;
|
||||
if (producerCrossFlowTracers.count(prodTracerName))
|
||||
{
|
||||
uiProducerTracerName = addCrossFlowEnding(prodTracerName);
|
||||
}
|
||||
|
||||
for ( const auto& injCellSet : injectorCellSets )
|
||||
{
|
||||
std::string injTracerName = injCellSet.id().to_string();
|
||||
CellSetID injID(injTracerName);
|
||||
|
||||
std::pair<double, double> fluxPair = injectorProducerPairFlux(*(injectorSolution.get()),
|
||||
*(producerSolution.get()),
|
||||
injCellSet,
|
||||
prodCellSet,
|
||||
sumWellFluxPrCell);
|
||||
injID,
|
||||
prodID,
|
||||
WellInFluxPrCell);
|
||||
std::string uiInjectorTracerName = injTracerName;
|
||||
|
||||
if (injectorCrossFlowTracers.count(injTracerName))
|
||||
{
|
||||
uiInjectorTracerName = addCrossFlowEnding(injTracerName);
|
||||
}
|
||||
|
||||
|
||||
#pragma omp critical
|
||||
{
|
||||
result.setInjProdWellPairFlux(injCellSet.id().to_string(),
|
||||
prodCellSet.id().to_string(),
|
||||
result.setInjProdWellPairFlux(uiInjectorTracerName,
|
||||
uiProducerTracerName,
|
||||
fluxPair);
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ set(NRLIB_GITHUB_SHA "ba35d4359882f1c6f5e9dc30eb95fe52af50fd6f")
|
||||
set(ERT_GITHUB_SHA "2e36798b43daf18c112b91aa3febbf2fccd4a95f")
|
||||
|
||||
# https://github.com/OPM/opm-flowdiagnostics
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "b6e59ddcd2feba450c8612a7402c9239e442c0d4")
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "b0b8bbd32881fa100661cc8da5d4843794a4184a")
|
||||
|
||||
# https://github.com/OPM/opm-flowdiagnostics-applications
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "c78f50897cea10ed56c7eadd1a1b23aa5ffbc56e")
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "e6ae223fbeb0c9bd83c63a5fb6f57e4a3ad2ec18")
|
||||
|
||||
# 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
|
||||
|
@ -9,11 +9,13 @@ Macro (add_acceptance_test casename)
|
||||
|
||||
String (REGEX REPLACE "\\.[^.]*$" "" basename "${casename}")
|
||||
|
||||
# Note: non-default tolerances used for TOF tests (defaults too strict)
|
||||
|
||||
Add_Test (NAME ToF_accept_${casename}_all_steps
|
||||
COMMAND runAcceptanceTest
|
||||
"case=${OPM_DATA_ROOT}/flow_diagnostic_test/eclipse-simulation/${basename}"
|
||||
"ref-dir=${OPM_DATA_ROOT}/flow_diagnostic_test/fd-ref-data/${basename}"
|
||||
"atol=${abs_tol}" "rtol=${rel_tol}")
|
||||
"atol=5e-6" "rtol=1e-13")
|
||||
|
||||
EndMacro (add_acceptance_test)
|
||||
|
||||
|
@ -17,43 +17,40 @@
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
# additional search modules
|
||||
set(OPM_COMMON_ROOT "" CACHE PATH "Root directory containing OPM related cmake modules")
|
||||
option(SIBLING_SEARCH "Search for other modules in sibling directories?" ON)
|
||||
|
||||
if (NOT OPM_COMMON_ROOT)
|
||||
find_package(opm-common QUIET)
|
||||
# Mandatory call to project
|
||||
project(opm-flowdiagnostics-applications CXX)
|
||||
|
||||
if(SIBLING_SEARCH AND NOT opm-common_DIR)
|
||||
# guess the sibling dir
|
||||
get_filename_component(_leaf_dir_name ${PROJECT_BINARY_DIR} NAME)
|
||||
get_filename_component(_parent_full_dir ${PROJECT_BINARY_DIR} DIRECTORY)
|
||||
get_filename_component(_parent_dir_name ${_parent_full_dir} NAME)
|
||||
#Try if <module-name>/<build-dir> is used
|
||||
get_filename_component(_modules_dir ${_parent_full_dir} DIRECTORY)
|
||||
if(IS_DIRECTORY ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
set(opm-common_DIR ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
else()
|
||||
string(REPLACE ${PROJECT_NAME} opm-common _opm_common_leaf ${_leaf_dir_name})
|
||||
if(NOT _leaf_dir_name STREQUAL _opm_common_leaf
|
||||
AND IS_DIRECTORY ${_parent_full_dir}/${_opm_common_leaf})
|
||||
# We are using build directories named <prefix><module-name><postfix>
|
||||
set(opm-common_DIR ${_parent_full_dir}/${_opm_common_leaf})
|
||||
elseif(IS_DIRECTORY ${_parent_full_dir}/opm-common)
|
||||
# All modules are in a common build dir
|
||||
set(opm-common_DIR "${_parent_full_dir}/opm-common}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(opm-common_DIR AND NOT IS_DIRECTORY ${opm-common_DIR})
|
||||
message(WARNING "Value ${opm-common_DIR} passed to variable"
|
||||
" opm-common_DIR is not a directory")
|
||||
endif()
|
||||
|
||||
if (opm-common_FOUND)
|
||||
include(OpmInit)
|
||||
else()
|
||||
unset(opm-common_FOUND)
|
||||
find_package(opm-common REQUIRED)
|
||||
|
||||
if (NOT OPM_COMMON_ROOT AND SIBLING_SEARCH)
|
||||
set(OPM_COMMON_ROOT ${PROJECT_SOURCE_DIR}/../opm-common)
|
||||
endif()
|
||||
|
||||
if (OPM_COMMON_ROOT)
|
||||
list(APPEND CMAKE_MODULE_PATH "${OPM_COMMON_ROOT}/cmake/Modules")
|
||||
include (OpmInit OPTIONAL RESULT_VARIABLE OPM_INIT)
|
||||
set(OPM_MACROS_ROOT ${OPM_COMMON_ROOT})
|
||||
endif()
|
||||
|
||||
if (NOT OPM_INIT)
|
||||
message("" )
|
||||
message(" /---------------------------------------------------------------------------------\\")
|
||||
message(" | Could not locate the opm build macros. The opm build macros |")
|
||||
message(" | are in a separate repository - instructions to proceed: |")
|
||||
message(" | |")
|
||||
message(" | 1. Clone the repository: git clone git@github.com:OPM/opm-common.git |")
|
||||
message(" | |")
|
||||
message(" | 2. Run cmake in the current project with -DOPM_COMMON_ROOT=<path>/opm-common |")
|
||||
message(" | |")
|
||||
message(" \\---------------------------------------------------------------------------------/")
|
||||
message("" )
|
||||
message(FATAL_ERROR "Could not find OPM Macros")
|
||||
endif()
|
||||
endif()
|
||||
include(OpmInit)
|
||||
|
||||
# not the same location as most of the other projects; this hook overrides
|
||||
macro (dir_hook)
|
||||
@ -73,35 +70,6 @@ include (${project}-prereqs)
|
||||
include (CMakeLists_files.cmake)
|
||||
|
||||
macro (config_hook)
|
||||
if (NOT ERT_FOUND)
|
||||
set (HAVE_ERT_ECL_TYPE_H 0)
|
||||
else ()
|
||||
# ERT_FOUND
|
||||
cmake_push_check_state ()
|
||||
|
||||
set (CMAKE_REQUIRED_INCLUDES ${ERT_INCLUDE_DIR})
|
||||
set (CMAKE_REQUIRED_LIBRARIES ${ERT_LIBRARIES})
|
||||
|
||||
check_cxx_source_compiles (
|
||||
"
|
||||
#include <iostream>
|
||||
|
||||
#include <ert/ecl/ecl_kw.h>
|
||||
#include <ert/ecl/ecl_type.h>
|
||||
|
||||
int main ()
|
||||
{
|
||||
ecl_kw_type* kw = nullptr;
|
||||
|
||||
std::cout << ecl_type_get_type(ecl_kw_get_data_type(kw)) << std::endl;
|
||||
}
|
||||
"
|
||||
HAVE_ERT_ECL_TYPE_H)
|
||||
|
||||
cmake_pop_check_state ()
|
||||
endif ()
|
||||
|
||||
list (APPEND "${project}_CONFIG_VARS" HAVE_ERT_ECL_TYPE_H)
|
||||
endmacro (config_hook)
|
||||
|
||||
macro (prereqs_hook)
|
||||
|
@ -21,12 +21,20 @@
|
||||
# the library needs it.
|
||||
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
opm/utility/ECLCaseUtilities.cpp
|
||||
opm/utility/ECLEndPointScaling.cpp
|
||||
opm/utility/ECLFluxCalc.cpp
|
||||
opm/utility/ECLGraph.cpp
|
||||
opm/utility/ECLPropTable.cpp
|
||||
opm/utility/ECLPvtCommon.cpp
|
||||
opm/utility/ECLPvtCurveCollection.cpp
|
||||
opm/utility/ECLPvtGas.cpp
|
||||
opm/utility/ECLPvtOil.cpp
|
||||
opm/utility/ECLPvtWater.cpp
|
||||
opm/utility/ECLRegionMapping.cpp
|
||||
opm/utility/ECLResultData.cpp
|
||||
opm/utility/ECLSaturationFunc.cpp
|
||||
opm/utility/ECLTableInterpolation1D.cpp
|
||||
opm/utility/ECLUnitHandling.cpp
|
||||
opm/utility/ECLWellSolution.cpp
|
||||
)
|
||||
@ -34,28 +42,42 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_eclendpointscaling.cpp
|
||||
tests/test_eclproptable.cpp
|
||||
tests/test_eclpvtcommon.cpp
|
||||
tests/test_eclregionmapping.cpp
|
||||
tests/test_eclsimple1dinterpolant.cpp
|
||||
tests/test_eclunithandling.cpp
|
||||
)
|
||||
|
||||
list (APPEND EXAMPLE_SOURCE_FILES
|
||||
examples/computeFlowStorageCurve.cpp
|
||||
examples/computeLocalSolutions.cpp
|
||||
examples/computePhaseFluxes.cpp
|
||||
examples/computeToFandTracers.cpp
|
||||
examples/computeTracers.cpp
|
||||
examples/extractFromRestart.cpp
|
||||
examples/extractPropCurves.cpp
|
||||
tests/runAcceptanceTest.cpp
|
||||
tests/runLinearisedCellDataTest.cpp
|
||||
tests/runTransTest.cpp
|
||||
)
|
||||
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/utility/ECLCaseUtilities.hpp
|
||||
opm/utility/ECLEndPointScaling.hpp
|
||||
opm/utility/ECLFluxCalc.hpp
|
||||
opm/utility/ECLGraph.hpp
|
||||
opm/utility/ECLPhaseIndex.hpp
|
||||
opm/utility/ECLPiecewiseLinearInterpolant.hpp
|
||||
opm/utility/ECLPropTable.hpp
|
||||
opm/utility/ECLPvtCommon.hpp
|
||||
opm/utility/ECLPvtCurveCollection.hpp
|
||||
opm/utility/ECLPvtGas.hpp
|
||||
opm/utility/ECLPvtOil.hpp
|
||||
opm/utility/ECLPvtWater.hpp
|
||||
opm/utility/ECLRegionMapping.hpp
|
||||
opm/utility/ECLResultData.hpp
|
||||
opm/utility/ECLSaturationFunc.hpp
|
||||
opm/utility/ECLTableInterpolation1D.hpp
|
||||
opm/utility/ECLUnitHandling.hpp
|
||||
opm/utility/ECLWellSolution.hpp
|
||||
)
|
||||
|
@ -25,49 +25,158 @@
|
||||
#include "exampleSetup.hpp"
|
||||
#include <opm/flowdiagnostics/CellSet.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <type_traits>
|
||||
|
||||
namespace {
|
||||
|
||||
using StartSets = std::vector< ::Opm::FlowDiagnostics::CellSet>;
|
||||
|
||||
/// Return completion cells of single well that both satisfy the
|
||||
/// select_completion criterion and are active cells.
|
||||
template <class WellData, class SelectCompletion>
|
||||
std::vector<int>
|
||||
activeCompletions(const Opm::ECLGraph& graph,
|
||||
const WellData& well,
|
||||
SelectCompletion&& select_completion)
|
||||
{
|
||||
std::vector<int> completion_cells;
|
||||
completion_cells.reserve(well.completions.size());
|
||||
for (const auto& completion : well.completions) {
|
||||
if (select_completion(completion)) {
|
||||
const int cell_index = graph.activeCell(completion.ijk, completion.gridName);
|
||||
if (cell_index >= 0) {
|
||||
completion_cells.push_back(cell_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return completion_cells;
|
||||
}
|
||||
|
||||
/// Return (active-cell) completions from all wells that satisfy
|
||||
/// the select_completion criterion, grouped by well.
|
||||
template <class SelectCompletion>
|
||||
StartSets
|
||||
getCompletionsFromWells(const example::Setup& setup,
|
||||
SelectCompletion&& select_completion)
|
||||
{
|
||||
auto start = StartSets{};
|
||||
for (const auto& well : setup.well_fluxes) {
|
||||
std::vector<int> completion_cells = activeCompletions(setup.graph, well, select_completion);
|
||||
if (!completion_cells.empty()) {
|
||||
start.emplace_back(Opm::FlowDiagnostics::CellSetID(well.name),
|
||||
completion_cells);
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
StartSets injectors(const example::Setup& setup)
|
||||
{
|
||||
using Completion = Opm::ECLWellSolution::WellData::Completion;
|
||||
|
||||
return getCompletionsFromWells(setup, [](const Completion& completion)
|
||||
{ return completion.reservoir_inflow_rate > 0.0; });
|
||||
}
|
||||
|
||||
StartSets producers(const example::Setup& setup)
|
||||
{
|
||||
using Completion = Opm::ECLWellSolution::WellData::Completion;
|
||||
|
||||
return getCompletionsFromWells(setup, [](const Completion& completion)
|
||||
{ return completion.reservoir_inflow_rate < 0.0; });
|
||||
}
|
||||
|
||||
void printSolution(const ::Opm::FlowDiagnostics::CellSetValues& x,
|
||||
const ::Opm::FlowDiagnostics::CellSetID& id,
|
||||
const std::string& varname)
|
||||
{
|
||||
const auto filename =
|
||||
varname + '-' + id.to_string() + ".out";
|
||||
|
||||
std::ofstream os(filename);
|
||||
|
||||
if (os) {
|
||||
os.precision(16);
|
||||
os.setf(std::ios_base::scientific);
|
||||
|
||||
for (const auto& item : x) {
|
||||
os << item.first << ' ' << item.second << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
extractCellIDs(const ::Opm::FlowDiagnostics::CellSetValues& x)
|
||||
{
|
||||
auto i = std::vector<int>{};
|
||||
i.reserve(x.size());
|
||||
|
||||
for (const auto& xi : x) { i.push_back(xi.first); }
|
||||
|
||||
std::sort(std::begin(i), std::end(i));
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
bool
|
||||
sameReachability(const ::Opm::FlowDiagnostics::CellSetValues& tof,
|
||||
const ::Opm::FlowDiagnostics::CellSetValues& conc)
|
||||
{
|
||||
if (tof.size() != conc.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto tof_id = extractCellIDs(tof);
|
||||
const auto conc_id = extractCellIDs(conc);
|
||||
|
||||
return tof_id == conc_id;
|
||||
}
|
||||
|
||||
void runAnalysis(const StartSets& start,
|
||||
const ::Opm::FlowDiagnostics::Solution& sol,
|
||||
const bool is_inj)
|
||||
{
|
||||
auto ok = std::vector<bool>{};
|
||||
ok.reserve(start.size());
|
||||
|
||||
for (const auto& pt : start) {
|
||||
const auto& id = pt.id();
|
||||
|
||||
const auto& tof = sol.timeOfFlight (id);
|
||||
const auto& conc = sol.concentration(id);
|
||||
|
||||
const std::string injprod = is_inj ? "inj" : "prod";
|
||||
printSolution(tof , id, "tof-" + injprod);
|
||||
printSolution(conc, id, "conc-" + injprod);
|
||||
|
||||
if (! sameReachability(tof, conc)) {
|
||||
std::cout << id.to_string() << ": FAIL\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Syntax (typical):
|
||||
// computeToFandTracers case=<ecl_case_prefix> step=<report_number>
|
||||
// computeLocalSolutions case=<ecl_case_prefix> step=<report_number>
|
||||
int main(int argc, char* argv[])
|
||||
try {
|
||||
example::Setup setup(argc, argv);
|
||||
auto& fdTool = setup.toolbox;
|
||||
|
||||
// Create start sets from injector wells.
|
||||
using ID = Opm::FlowDiagnostics::CellSetID;
|
||||
std::vector<Opm::FlowDiagnostics::CellSet> start;
|
||||
for (const auto& well : setup.well_fluxes) {
|
||||
if (!well.is_injector_well) {
|
||||
continue;
|
||||
}
|
||||
std::vector<int> completion_cells;
|
||||
completion_cells.reserve(well.completions.size());
|
||||
for (const auto& completion : well.completions) {
|
||||
const auto& gridName = completion.gridName;
|
||||
const auto& ijk = completion.ijk;
|
||||
const int cell_index = setup.graph.activeCell(ijk, gridName);
|
||||
if (cell_index >= 0) {
|
||||
completion_cells.push_back(cell_index);
|
||||
}
|
||||
}
|
||||
start.emplace_back(ID(well.name), completion_cells);
|
||||
{
|
||||
const auto inj = injectors(setup);
|
||||
const auto fwd = fdTool.computeInjectionDiagnostics(inj);
|
||||
|
||||
runAnalysis(inj, fwd.fd, true);
|
||||
}
|
||||
|
||||
{
|
||||
const auto prod = producers(setup);
|
||||
const auto rev = fdTool.computeProductionDiagnostics(prod);
|
||||
|
||||
// Solve for injection time of flight and tracers.
|
||||
auto sol = fdTool.computeInjectionDiagnostics(start);
|
||||
|
||||
// Choose injector id, default to first injector.
|
||||
const std::string id_string = setup.param.getDefault("id", start.front().id().to_string());
|
||||
const ID id(id_string);
|
||||
|
||||
// Get local data for injector.
|
||||
const bool tracer = setup.param.getDefault("tracer", false);
|
||||
const auto& data = tracer ? sol.fd.concentration(id) : sol.fd.timeOfFlight(id);
|
||||
|
||||
// Write it to standard out.
|
||||
std::cout.precision(16);
|
||||
for (auto item : data) {
|
||||
std::cout << item.first << " " << item.second << '\n';
|
||||
runAnalysis(prod, rev.fd, false);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
229
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/examples/computePhaseFluxes.cpp
vendored
Normal file
229
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/examples/computePhaseFluxes.cpp
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF ICT, Applied Mathematics.
|
||||
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 <examples/exampleSetup.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace {
|
||||
template <class OStream, class Task>
|
||||
void timeIt(OStream& os, Task&& task)
|
||||
{
|
||||
const auto start = ::std::chrono::steady_clock::now();
|
||||
|
||||
task();
|
||||
|
||||
const auto stop = ::std::chrono::steady_clock::now();
|
||||
|
||||
os << std::setprecision(3) << std::scientific
|
||||
<< std::chrono::duration<double>(stop - start).count()
|
||||
<< " [s]" << std::endl;
|
||||
}
|
||||
|
||||
std::string phaseName(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
switch (p) {
|
||||
case Opm::ECLPhaseIndex::Aqua: return "water";
|
||||
case Opm::ECLPhaseIndex::Liquid: return "oil";
|
||||
case Opm::ECLPhaseIndex::Vapour: return "gas";
|
||||
}
|
||||
|
||||
throw std::invalid_argument {
|
||||
"Invalid Phase ID"
|
||||
};
|
||||
}
|
||||
|
||||
int numDigits(const std::vector<int>& steps)
|
||||
{
|
||||
if (steps.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto m =
|
||||
*std::max_element(std::begin(steps), std::end(steps));
|
||||
|
||||
if (m == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert (m > 0);
|
||||
|
||||
return std::floor(std::log10(static_cast<double>(m))) + 1;
|
||||
}
|
||||
|
||||
void openRestartSet(const Opm::ECLCaseUtilities::ResultSet& rset,
|
||||
const int step,
|
||||
std::unique_ptr<Opm::ECLRestartData>& rstrt)
|
||||
{
|
||||
if (! (rset.isUnifiedRestart() && rstrt)) {
|
||||
// Not a unified restart file or this is the first time we're
|
||||
// seeing the result set.
|
||||
rstrt.reset(new Opm::ECLRestartData(rset.restartFile(step)));
|
||||
}
|
||||
}
|
||||
|
||||
void savePhaseVector(const std::string& quant,
|
||||
const std::string& phase,
|
||||
const std::string& type,
|
||||
const int step,
|
||||
const int ndgt,
|
||||
const std::vector<double>& pflux)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
const auto dir = fs::path{ quant } / phase;
|
||||
|
||||
boost::system::error_code ec{};
|
||||
if (fs::create_directories(dir, ec) ||
|
||||
(ec.value() == boost::system::errc::errc_t::success))
|
||||
{
|
||||
auto fn = dir;
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << type << '-'
|
||||
<< std::setw(ndgt) << std::setfill('0')
|
||||
<< step << ".txt";
|
||||
|
||||
fn /= os.str();
|
||||
}
|
||||
|
||||
fs::ofstream os(fn);
|
||||
if (os) {
|
||||
os.precision(16);
|
||||
os.setf(std::ios::scientific);
|
||||
|
||||
for (const auto& qi : pflux) {
|
||||
os << qi << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void saveNeighbours(const ::Opm::ECLGraph& G)
|
||||
{
|
||||
std::ofstream os("neigh.txt");
|
||||
|
||||
if (!os) { return; }
|
||||
|
||||
const auto& neigh = G.neighbours();
|
||||
|
||||
for (auto nconn = neigh.size() / 2,
|
||||
conn = 0*nconn; conn < nconn; ++conn)
|
||||
{
|
||||
os << neigh[2*conn + 0 ] << ' ' << neigh[2*conn + 1] << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void computePhaseFluxes(const Opm::ECLGraph& G,
|
||||
const Opm::ECLCaseUtilities::ResultSet& rset,
|
||||
const Opm::ECLFluxCalc& fcalc,
|
||||
const int step,
|
||||
const int ndgt,
|
||||
std::unique_ptr<Opm::ECLRestartData>& rstrt)
|
||||
{
|
||||
openRestartSet(rset, step, rstrt);
|
||||
|
||||
if (! (rstrt && rstrt->selectReportStep(step))) {
|
||||
std::cout << " Failed (No Such Report Step)\n";
|
||||
}
|
||||
|
||||
for (const auto& phase : G.activePhases()) {
|
||||
const auto pname = phaseName(phase);
|
||||
auto pflux = std::vector<double>{};
|
||||
|
||||
timeIt(std::cout, [&fcalc, &rstrt, phase, &pname, &pflux]()
|
||||
{
|
||||
pflux = fcalc.flux(*rstrt, phase);
|
||||
|
||||
std::cout << " - " << std::right
|
||||
<< std::setw(5) << std::setfill(' ')
|
||||
<< pname << ": ";
|
||||
});
|
||||
|
||||
if (! pflux.empty()) {
|
||||
savePhaseVector("flux", pname, "calc", step, ndgt, pflux);
|
||||
}
|
||||
|
||||
// Extract reference fluxes if available.
|
||||
pflux = G.flux(*rstrt, phase);
|
||||
if (! pflux.empty()) {
|
||||
savePhaseVector("flux", pname, "ref", step, ndgt, pflux);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Anonymous
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
try {
|
||||
const auto prm = example::initParam(argc, argv);
|
||||
const auto grav = prm.getDefault("grav" , 9.80665);
|
||||
const auto useEPS = prm.getDefault("useEPS", false);
|
||||
|
||||
const auto rset = example::identifyResultSet(prm);
|
||||
const auto steps = rset.reportStepIDs();
|
||||
const auto ndgt = numDigits(steps);
|
||||
const auto init = Opm::ECLInitFileData(rset.initFile());
|
||||
const auto graph = Opm::ECLGraph::load(rset.gridFile(), init);
|
||||
const auto fcalc = Opm::ECLFluxCalc(graph, init, grav, useEPS);
|
||||
|
||||
if (prm.getDefault("emitNeigh", false)) {
|
||||
saveNeighbours(graph);
|
||||
}
|
||||
|
||||
auto rstrt = std::unique_ptr<Opm::ECLRestartData>{};
|
||||
|
||||
for (const auto& step : steps) {
|
||||
timeIt(std::cout, [&rset, &graph, &rstrt, &fcalc, ndgt, step]()
|
||||
{
|
||||
std::cout << "Phase Fluxes for Report Step "
|
||||
<< std::setw(ndgt) << std::setfill('0')
|
||||
<< step << std::endl;
|
||||
|
||||
computePhaseFluxes(graph, rset, fcalc, step, ndgt, rstrt);
|
||||
|
||||
std::cout << " - Step Time: ";
|
||||
});
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Caught Exception: " << e.what() << '\n';
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
#include <opm/flowdiagnostics/ConnectionValues.hpp>
|
||||
#include <opm/flowdiagnostics/Toolbox.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
#include <opm/utility/ECLFluxCalc.hpp>
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
@ -36,52 +37,16 @@
|
||||
#include <opm/utility/ECLWellSolution.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <initializer_list>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace example {
|
||||
inline bool isFile(const boost::filesystem::path& p)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
auto is_regular_file = [](const fs::path& pth)
|
||||
{
|
||||
return fs::exists(pth) && fs::is_regular_file(pth);
|
||||
};
|
||||
|
||||
return is_regular_file(p)
|
||||
|| (fs::is_symlink(p) &&
|
||||
is_regular_file(fs::read_symlink(p)));
|
||||
}
|
||||
|
||||
inline boost::filesystem::path
|
||||
deriveFileName(boost::filesystem::path file,
|
||||
const std::vector<std::string>& extensions)
|
||||
{
|
||||
for (const auto& ext : extensions) {
|
||||
file.replace_extension(ext);
|
||||
|
||||
if (isFile(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
const auto prefix = file.parent_path() / file.stem();
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
os << "Unable to derive valid filename from model prefix "
|
||||
<< prefix.generic_string();
|
||||
|
||||
throw std::invalid_argument(os.str());
|
||||
}
|
||||
|
||||
|
||||
template <class FluxCalc>
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField(const Opm::ECLGraph& G,
|
||||
@ -123,35 +88,32 @@ namespace example {
|
||||
const bool useEPS)
|
||||
{
|
||||
if (compute_fluxes) {
|
||||
auto satfunc = ::Opm::ECLSaturationFunc(G, init, useEPS);
|
||||
const auto grav = 0.0;
|
||||
|
||||
Opm::ECLFluxCalc calc(G, std::move(satfunc));
|
||||
Opm::ECLFluxCalc calc(G, init, grav, useEPS);
|
||||
|
||||
auto getFlux = [&calc, &rstrt]
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
return extractFluxField(G, [&calc, &rstrt]
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
return calc.flux(rstrt, p);
|
||||
};
|
||||
|
||||
return extractFluxField(G, getFlux);
|
||||
});
|
||||
}
|
||||
|
||||
auto getFlux = [&G, &rstrt]
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
return extractFluxField(G, [&G, &rstrt]
|
||||
(const Opm::ECLPhaseIndex p)
|
||||
{
|
||||
return G.flux(rstrt, p);
|
||||
};
|
||||
|
||||
return extractFluxField(G, getFlux);
|
||||
});
|
||||
}
|
||||
|
||||
template <class WellFluxes>
|
||||
Opm::FlowDiagnostics::CellSetValues
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues>
|
||||
extractWellFlows(const Opm::ECLGraph& G,
|
||||
const WellFluxes& well_fluxes)
|
||||
{
|
||||
Opm::FlowDiagnostics::CellSetValues inflow;
|
||||
std::map<Opm::FlowDiagnostics::CellSetID, Opm::FlowDiagnostics::CellSetValues> well_flows;
|
||||
for (const auto& well : well_fluxes) {
|
||||
Opm::FlowDiagnostics::CellSetValues& inflow = well_flows[Opm::FlowDiagnostics::CellSetID(well.name)];
|
||||
for (const auto& completion : well.completions) {
|
||||
const auto& gridName = completion.gridName;
|
||||
const auto& ijk = completion.ijk;
|
||||
@ -167,32 +129,40 @@ namespace example {
|
||||
}
|
||||
}
|
||||
|
||||
return inflow;
|
||||
return well_flows;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct FilePaths
|
||||
inline Opm::ECLCaseUtilities::ResultSet
|
||||
identifyResultSet(const Opm::ParameterGroup& param)
|
||||
{
|
||||
FilePaths(const Opm::ParameterGroup& param)
|
||||
for (const auto* p : { "case", "grid", "init", "restart" })
|
||||
{
|
||||
const string casename = param.getDefault<string>("case", "DEFAULT_CASE_NAME");
|
||||
grid = param.has("grid") ? param.get<string>("grid")
|
||||
: deriveFileName(casename, { ".EGRID", ".FEGRID", ".GRID", ".FGRID" });
|
||||
init = param.has("init") ? param.get<string>("init")
|
||||
: deriveFileName(casename, { ".INIT", ".FINIT" });
|
||||
restart = param.has("restart") ? param.get<string>("restart")
|
||||
: deriveFileName(casename, { ".UNRST", ".FUNRST" });
|
||||
if (param.has(p)) {
|
||||
return Opm::ECLCaseUtilities::ResultSet {
|
||||
param.get<std::string>(p)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using path = boost::filesystem::path;
|
||||
using string = std::string;
|
||||
throw std::invalid_argument {
|
||||
"No Valid Result Set Identified by Input Parameters"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
inline std::unordered_set<int>
|
||||
getAvailableSteps(const ::Opm::ECLCaseUtilities::ResultSet& result_set)
|
||||
{
|
||||
const auto steps = result_set.reportStepIDs();
|
||||
|
||||
return { std::begin(steps), std::end(steps) };
|
||||
}
|
||||
|
||||
path grid;
|
||||
path init;
|
||||
path restart;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -231,10 +201,10 @@ namespace example {
|
||||
{
|
||||
Setup(int argc, char** argv)
|
||||
: param (initParam(argc, argv))
|
||||
, file_paths (param)
|
||||
, init (file_paths.init)
|
||||
, rstrt (file_paths.restart)
|
||||
, graph (::Opm::ECLGraph::load(file_paths.grid, init))
|
||||
, result_set (identifyResultSet(param))
|
||||
, init (result_set.initFile())
|
||||
, graph (::Opm::ECLGraph::load(result_set.gridFile(), init))
|
||||
, available_steps(getAvailableSteps(result_set))
|
||||
, well_fluxes ()
|
||||
, toolbox (initToolbox(graph))
|
||||
, compute_fluxes_(param.getDefault("compute_fluxes", false))
|
||||
@ -247,7 +217,7 @@ namespace example {
|
||||
|
||||
os << "Report Step " << step
|
||||
<< " is Not Available in Result Set "
|
||||
<< file_paths.grid.stem();
|
||||
<< this->result_set.gridFile().stem();
|
||||
|
||||
throw std::domain_error(os.str());
|
||||
}
|
||||
@ -255,16 +225,33 @@ namespace example {
|
||||
|
||||
bool selectReportStep(const int step)
|
||||
{
|
||||
if (! rstrt.selectReportStep(step)) {
|
||||
if (this->available_steps.count(step) == 0) {
|
||||
// Requested report step not amongst those stored in the
|
||||
// result set.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! (this->result_set.isUnifiedRestart() &&
|
||||
bool(this->restart)))
|
||||
{
|
||||
// Non-unified (separate) restart files, or first time-step
|
||||
// selection in a unified restart file case.
|
||||
const auto restart_file =
|
||||
this->result_set.restartFile(step);
|
||||
|
||||
this->openRestartFile(restart_file);
|
||||
}
|
||||
|
||||
if (! this->restart->selectReportStep(step)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto wsol = Opm::ECLWellSolution{};
|
||||
well_fluxes = wsol.solution(rstrt, graph.activeGrids());
|
||||
well_fluxes = wsol.solution(*restart, graph.activeGrids());
|
||||
}
|
||||
|
||||
toolbox.assignConnectionFlux(extractFluxField(graph, init, rstrt,
|
||||
toolbox.assignConnectionFlux(extractFluxField(graph, init, *restart,
|
||||
compute_fluxes_, useEPS_));
|
||||
|
||||
toolbox.assignInflowFlux(extractWellFlows(graph, well_fluxes));
|
||||
@ -273,14 +260,22 @@ namespace example {
|
||||
}
|
||||
|
||||
Opm::ParameterGroup param;
|
||||
FilePaths file_paths;
|
||||
Opm::ECLCaseUtilities::ResultSet result_set;
|
||||
Opm::ECLInitFileData init;
|
||||
Opm::ECLRestartData rstrt;
|
||||
Opm::ECLGraph graph;
|
||||
std::unordered_set<int> available_steps;
|
||||
std::vector<Opm::ECLWellSolution::WellData> well_fluxes;
|
||||
Opm::FlowDiagnostics::Toolbox toolbox;
|
||||
bool compute_fluxes_ = false;
|
||||
bool useEPS_ = false;
|
||||
|
||||
std::shared_ptr<Opm::ECLRestartData> restart;
|
||||
|
||||
private:
|
||||
void openRestartFile(const boost::filesystem::path& rstrt)
|
||||
{
|
||||
this->restart.reset(new Opm::ECLRestartData{ rstrt });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
233
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/examples/extractPropCurves.cpp
vendored
Normal file
233
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/examples/extractPropCurves.cpp
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF ICT, Applied Mathematics.
|
||||
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 <examples/exampleSetup.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLPvtCurveCollection.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLSaturationFunc.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace {
|
||||
template <class OStream>
|
||||
void printGraph(OStream& os,
|
||||
const std::string& name,
|
||||
const Opm::FlowDiagnostics::Graph& graph)
|
||||
{
|
||||
const auto& x = graph.first;
|
||||
const auto& y = graph.second;
|
||||
|
||||
const auto oprec = os.precision(16);
|
||||
const auto oflags = os.setf(std::ios_base::scientific);
|
||||
|
||||
os << name << " = [\n";
|
||||
|
||||
for (auto n = x.size(), i = 0*n; i < n; ++i) {
|
||||
os << x[i] << ' ' << y[i] << '\n';
|
||||
}
|
||||
|
||||
os << "];\n\n";
|
||||
|
||||
os.setf(oflags);
|
||||
os.precision(oprec);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Relative permeability
|
||||
|
||||
void krg(const Opm::ECLSaturationFunc& sfunc,
|
||||
const int activeCell,
|
||||
const bool useEPS)
|
||||
{
|
||||
using RC = Opm::ECLSaturationFunc::RawCurve;
|
||||
|
||||
auto func = std::vector<RC>{};
|
||||
func.reserve(1);
|
||||
|
||||
// Request krg (gas rel-perm in oil-gas system)
|
||||
func.push_back(RC{
|
||||
RC::Function::RelPerm,
|
||||
RC::SubSystem::OilGas,
|
||||
Opm::ECLPhaseIndex::Vapour
|
||||
});
|
||||
|
||||
const auto graph =
|
||||
sfunc.getSatFuncCurve(func, activeCell, useEPS);
|
||||
|
||||
printGraph(std::cout, "krg", graph[0]);
|
||||
}
|
||||
|
||||
void krog(const Opm::ECLSaturationFunc& sfunc,
|
||||
const int activeCell,
|
||||
const bool useEPS)
|
||||
{
|
||||
using RC = Opm::ECLSaturationFunc::RawCurve;
|
||||
|
||||
auto func = std::vector<RC>{};
|
||||
func.reserve(1);
|
||||
|
||||
// Request krog (oil rel-perm in oil-gas system)
|
||||
func.push_back(RC{
|
||||
RC::Function::RelPerm,
|
||||
RC::SubSystem::OilGas,
|
||||
Opm::ECLPhaseIndex::Liquid
|
||||
});
|
||||
|
||||
const auto graph =
|
||||
sfunc.getSatFuncCurve(func, activeCell, useEPS);
|
||||
|
||||
printGraph(std::cout, "krog", graph[0]);
|
||||
}
|
||||
|
||||
void krow(const Opm::ECLSaturationFunc& sfunc,
|
||||
const int activeCell,
|
||||
const bool useEPS)
|
||||
{
|
||||
using RC = Opm::ECLSaturationFunc::RawCurve;
|
||||
|
||||
auto func = std::vector<RC>{};
|
||||
func.reserve(1);
|
||||
|
||||
// Request krow (oil rel-perm in oil-water system)
|
||||
func.push_back(RC{
|
||||
RC::Function::RelPerm,
|
||||
RC::SubSystem::OilWater,
|
||||
Opm::ECLPhaseIndex::Liquid
|
||||
});
|
||||
|
||||
const auto graph =
|
||||
sfunc.getSatFuncCurve(func, activeCell, useEPS);
|
||||
|
||||
printGraph(std::cout, "krow", graph[0]);
|
||||
}
|
||||
|
||||
void krw(const Opm::ECLSaturationFunc& sfunc,
|
||||
const int activeCell,
|
||||
const bool useEPS)
|
||||
{
|
||||
using RC = Opm::ECLSaturationFunc::RawCurve;
|
||||
|
||||
auto func = std::vector<RC>{};
|
||||
func.reserve(1);
|
||||
|
||||
// Request krw (water rel-perm in oil-water system)
|
||||
func.push_back(RC{
|
||||
RC::Function::RelPerm,
|
||||
RC::SubSystem::OilWater,
|
||||
Opm::ECLPhaseIndex::Aqua
|
||||
});
|
||||
|
||||
const auto graph =
|
||||
sfunc.getSatFuncCurve(func, activeCell, useEPS);
|
||||
|
||||
printGraph(std::cout, "krw", graph[0]);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// PVT Curves
|
||||
|
||||
void Bg(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCurves,
|
||||
const int activeCell)
|
||||
{
|
||||
using RC = Opm::ECLPVT::RawCurve;
|
||||
|
||||
const auto graph = pvtCurves
|
||||
.getPvtCurve(RC::FVF, Opm::ECLPhaseIndex::Vapour, activeCell);
|
||||
|
||||
printGraph(std::cout, "Bg", graph);
|
||||
}
|
||||
|
||||
void mu_g(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCurves,
|
||||
const int activeCell)
|
||||
{
|
||||
using RC = Opm::ECLPVT::RawCurve;
|
||||
|
||||
const auto graph = pvtCurves
|
||||
.getPvtCurve(RC::Viscosity, Opm::ECLPhaseIndex::Vapour, activeCell);
|
||||
|
||||
printGraph(std::cout, "mu_g", graph);
|
||||
}
|
||||
|
||||
void Bo(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCurves,
|
||||
const int activeCell)
|
||||
{
|
||||
using RC = Opm::ECLPVT::RawCurve;
|
||||
|
||||
const auto graph = pvtCurves
|
||||
.getPvtCurve(RC::FVF, Opm::ECLPhaseIndex::Liquid, activeCell);
|
||||
|
||||
printGraph(std::cout, "Bo", graph);
|
||||
}
|
||||
|
||||
void mu_o(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCurves,
|
||||
const int activeCell)
|
||||
{
|
||||
using RC = Opm::ECLPVT::RawCurve;
|
||||
|
||||
const auto graph = pvtCurves
|
||||
.getPvtCurve(RC::Viscosity, Opm::ECLPhaseIndex::Liquid, activeCell);
|
||||
|
||||
printGraph(std::cout, "mu_o", graph);
|
||||
}
|
||||
} // namespace Anonymous
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
try {
|
||||
const auto prm = example::initParam(argc, argv);
|
||||
const auto useEPS = prm.getDefault("useEPS", false);
|
||||
const auto cellID = prm.getDefault("cell", 0);
|
||||
|
||||
const auto rset = example::identifyResultSet(prm);
|
||||
const auto init = Opm::ECLInitFileData(rset.initFile());
|
||||
const auto graph = Opm::ECLGraph::load(rset.gridFile(), init);
|
||||
const auto sfunc = Opm::ECLSaturationFunc(graph, init, useEPS);
|
||||
const auto pvtCC = Opm::ECLPVT::ECLPvtCurveCollection(graph, init);
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Relative permeability
|
||||
|
||||
if (prm.getDefault("krg" , false)) { krg (sfunc, cellID, useEPS); }
|
||||
if (prm.getDefault("krog", false)) { krog(sfunc, cellID, useEPS); }
|
||||
if (prm.getDefault("krow", false)) { krow(sfunc, cellID, useEPS); }
|
||||
if (prm.getDefault("krw" , false)) { krw (sfunc, cellID, useEPS); }
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// PVT Curves
|
||||
|
||||
if (prm.getDefault("Bg" , false)) { Bg (pvtCC, cellID); }
|
||||
if (prm.getDefault("mu_g", false)) { mu_g(pvtCC, cellID); }
|
||||
if (prm.getDefault("Bo" , false)) { Bo (pvtCC, cellID); }
|
||||
if (prm.getDefault("mu_o", false)) { mu_o(pvtCC, cellID); }
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Caught Exception: " << e.what() << '\n';
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
326
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLCaseUtilities.cpp
vendored
Normal file
326
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLCaseUtilities.cpp
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <ert/ecl/ecl_file.h>
|
||||
#include <ert/ecl/ecl_file_kw.h>
|
||||
#include <ert/ecl/ecl_file_view.h>
|
||||
#include <ert/util/ert_unique_ptr.hpp>
|
||||
|
||||
/// Function object that matches strings with a common prefix (i.e., a file
|
||||
/// name base) followed by .F or .X and then exactly four digits and nothing
|
||||
/// further.
|
||||
///
|
||||
/// In other words, if prefix is "NORNE_ATW2013", then the function will
|
||||
/// return true for strings of the form
|
||||
///
|
||||
/// -* NORNE_ATW2013.F0000
|
||||
/// -* NORNE_ATW2013.X1234
|
||||
///
|
||||
/// but it will return false for anything else like strings of the form
|
||||
///
|
||||
/// -* NORNE_ATW2013.S0123
|
||||
/// -* NORNE_ATW2013.FUNRST
|
||||
/// -* NORNE_ATW2013.X12345
|
||||
/// -* NORNE_ATW2013.F012
|
||||
/// -* NORNE_ATW2013.X012Y
|
||||
/// -* NORNE_ATW2013.X0123.4
|
||||
/// -* NORNE_ATW2014.F0001
|
||||
///
|
||||
class IsSeparateRestart
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] prefix Common string prefix (file name base).
|
||||
IsSeparateRestart(const std::string& prefix)
|
||||
: restart_(prefix + R"~~(\.(F|X)\d{4}$)~~")
|
||||
{
|
||||
// Common prefix + '.' + (F or X) + four digits + "end of string".
|
||||
}
|
||||
|
||||
/// Match string against stored regular expression engine.
|
||||
///
|
||||
/// \param[in] e Name, possibly full path, of file system element.
|
||||
/// Typically names a regular file.
|
||||
///
|
||||
/// \return True if the element matches the entire stored regular
|
||||
/// expression (\code regex_match() \endcode rather than \code
|
||||
/// regex_search() \endcode), false otherwise.
|
||||
bool operator()(const boost::filesystem::path& e) const
|
||||
{
|
||||
return std::regex_match(e.filename().generic_string(),
|
||||
this->restart_);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Regular expression against which to match filesystem elements.
|
||||
std::regex restart_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <class String>
|
||||
boost::filesystem::path
|
||||
operator+(boost::filesystem::path p, const String& e)
|
||||
{
|
||||
return p += e;
|
||||
}
|
||||
|
||||
bool isFile(const boost::filesystem::path& p)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
auto is_regular_file = [](const fs::path& pth)
|
||||
{
|
||||
return fs::exists(pth) && fs::is_regular_file(pth);
|
||||
};
|
||||
|
||||
return is_regular_file(p)
|
||||
|| (fs::is_symlink(p) &&
|
||||
is_regular_file(fs::read_symlink(p)));
|
||||
}
|
||||
|
||||
boost::filesystem::path
|
||||
deriveFileName(boost::filesystem::path file,
|
||||
std::initializer_list<const char*> ext)
|
||||
{
|
||||
for (const auto& e : ext) {
|
||||
file.replace_extension(e);
|
||||
|
||||
if (isFile(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Handle the possibility that a case prefix ends in a substring that
|
||||
// looks like a file extension (e.g., "path/to/CASE.01"). This
|
||||
// substring would be stripped off during deriveFileName() processing.
|
||||
boost::filesystem::path
|
||||
safeCasePrefix(const boost::filesystem::path& casename)
|
||||
{
|
||||
auto casefile = boost::filesystem::path{};
|
||||
|
||||
for (const auto* ignore : { "", ".@@@-HACK-@@@" }) {
|
||||
casefile = deriveFileName(casename + ignore,
|
||||
{ ".DATA" ,
|
||||
".EGRID", ".FEGRID" ,
|
||||
".GRID" , ".FGRID" });
|
||||
|
||||
if (! casefile.empty()) {
|
||||
// Valid case file found.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (casefile.empty()) {
|
||||
return casefile;
|
||||
}
|
||||
|
||||
return casefile.parent_path() / (casefile.stem() + ".Imp-Detail-Hack");
|
||||
}
|
||||
|
||||
// Look for separate restart files (.X000n or .F000n) that match the
|
||||
// case-name prefix in the directory implied by prefix. Pick the most
|
||||
// recent one (as defined by the element's write/modification time).
|
||||
//
|
||||
// Return empty if there are no separate restart files (that match the
|
||||
// prefix).
|
||||
boost::filesystem::path
|
||||
mostRecentSeparateRestart(const boost::filesystem::path& prefix)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
const auto isSepRstrt = IsSeparateRestart {
|
||||
prefix.stem().generic_string()
|
||||
};
|
||||
|
||||
auto most_recent = fs::path{};
|
||||
const fs::path parent_path = prefix.parent_path() == "" ? "." : prefix.parent_path();
|
||||
auto max_mtime = 0 * fs::last_write_time(parent_path);
|
||||
|
||||
for (auto i = fs::directory_iterator(parent_path),
|
||||
e = fs::directory_iterator();
|
||||
i != e; ++i)
|
||||
{
|
||||
if (! fs::is_regular_file(i->status())) {
|
||||
// Not a file. Ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& elem = i->path();
|
||||
|
||||
if (! isSepRstrt(elem)) {
|
||||
// Not a (separate) restart file. Ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto mtime = fs::last_write_time(elem);
|
||||
|
||||
if (mtime > max_mtime) {
|
||||
max_mtime = mtime;
|
||||
most_recent = elem;
|
||||
}
|
||||
}
|
||||
|
||||
return most_recent;
|
||||
}
|
||||
|
||||
// Determine whether or not result set uses a unified restart file.
|
||||
//
|
||||
// Steps:
|
||||
// 1) Return false if no unified restart file matches prefix.
|
||||
//
|
||||
// 2) Otherwise, return true if no *separate* restart file matches
|
||||
// prefix.
|
||||
//
|
||||
// 3) Otherwise, return true if unified restart file is more recent
|
||||
// (in terms of modification/write time) than most recent separate
|
||||
// restart file that matches prefix.
|
||||
bool restartIsUnified(const boost::filesystem::path& prefix)
|
||||
{
|
||||
const auto unif = deriveFileName(prefix, { ".UNRST", ".FUNRST" });
|
||||
|
||||
if (unif.empty()) {
|
||||
// No unified restart file matches the 'prefix'. Definitely not
|
||||
// a unified restart case.
|
||||
return false;
|
||||
}
|
||||
|
||||
// There *is* a unified restart file, but there might be separate
|
||||
// restart files too--e.g., if the .DATA file was modified between
|
||||
// runs. Look for those, and pick the one with the most recent
|
||||
// write time (i.e., stat::m_time on POSIX).
|
||||
const auto separate = mostRecentSeparateRestart(prefix);
|
||||
|
||||
if (separate.empty()) {
|
||||
// There are no separate restart files that match the 'prefix'.
|
||||
// This is definitely a unified restart case.
|
||||
return true;
|
||||
}
|
||||
|
||||
// There are *both* unified and separte candidate restart files.
|
||||
// Choose the unified file if more recent than most recent separate
|
||||
// file.
|
||||
using boost::filesystem::last_write_time;
|
||||
|
||||
return ! (last_write_time(unif) < last_write_time(separate));
|
||||
}
|
||||
|
||||
boost::filesystem::path
|
||||
separateRestartFile(const boost::filesystem::path& prefix,
|
||||
const int reportStepID)
|
||||
{
|
||||
auto makeExt = [reportStepID](const std::string& cat) -> std::string
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << cat << std::setw(4) << std::setfill('0') << reportStepID;
|
||||
|
||||
return os.str();
|
||||
};
|
||||
|
||||
// Formatted
|
||||
const auto F = makeExt("F");
|
||||
|
||||
// Unformatted
|
||||
const auto X = makeExt("X");
|
||||
|
||||
// Note: .c_str() is a bit of a hack--needed to match
|
||||
// initializer_list<const char*>.
|
||||
return deriveFileName(prefix, { X.c_str(), F.c_str() });
|
||||
}
|
||||
}
|
||||
|
||||
Opm::ECLCaseUtilities::ResultSet::ResultSet(const Path& casename)
|
||||
: prefix_(safeCasePrefix(casename))
|
||||
{
|
||||
if (this->prefix_.empty()) {
|
||||
throw std::invalid_argument {
|
||||
casename.generic_string() +
|
||||
" Is Not a Valid ECL Result Set Name"
|
||||
};
|
||||
}
|
||||
|
||||
// Don't check for unified/separate until we've verified that this is
|
||||
// even a valid result set.
|
||||
this->isUnified_ = restartIsUnified(this->prefix_);
|
||||
}
|
||||
|
||||
Opm::ECLCaseUtilities::ResultSet::Path
|
||||
Opm::ECLCaseUtilities::ResultSet::gridFile() const
|
||||
{
|
||||
return deriveFileName(this->prefix_,
|
||||
{ ".EGRID", ".FEGRID",
|
||||
".GRID" , ".FGRID" });
|
||||
}
|
||||
|
||||
Opm::ECLCaseUtilities::ResultSet::Path
|
||||
Opm::ECLCaseUtilities::ResultSet::initFile() const
|
||||
{
|
||||
return deriveFileName(this->prefix_,
|
||||
{ ".INIT", ".FINIT" });
|
||||
}
|
||||
|
||||
Opm::ECLCaseUtilities::ResultSet::Path
|
||||
Opm::ECLCaseUtilities::ResultSet::restartFile(const int reportStepID) const
|
||||
{
|
||||
if (this->isUnifiedRestart()) {
|
||||
return deriveFileName(this->prefix_, { ".UNRST", ".FUNRST" });
|
||||
}
|
||||
|
||||
return separateRestartFile(this->prefix_, reportStepID);
|
||||
}
|
||||
|
||||
bool
|
||||
Opm::ECLCaseUtilities::ResultSet::isUnifiedRestart() const
|
||||
{
|
||||
return this->isUnified_;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
Opm::ECLCaseUtilities::ResultSet::reportStepIDs() const
|
||||
{
|
||||
using FilePtr = ::ERT::
|
||||
ert_unique_ptr<ecl_file_type, ecl_file_close>;
|
||||
|
||||
const auto rsspec_fn =
|
||||
deriveFileName(this->prefix_, { ".RSSPEC", ".FRSSPEC" });
|
||||
|
||||
// Read-only, keep open between requests
|
||||
const auto open_flags = 0;
|
||||
|
||||
auto rsspec = FilePtr{
|
||||
ecl_file_open(rsspec_fn.generic_string().c_str(), open_flags)
|
||||
};
|
||||
|
||||
auto* globView = ecl_file_get_global_view(rsspec.get());
|
||||
|
||||
const auto* ITIME_kw = "ITIME";
|
||||
const auto n = ecl_file_view_get_num_named_kw(globView, ITIME_kw);
|
||||
|
||||
auto steps = std::vector<int>(n);
|
||||
|
||||
for (auto i = 0*n; i < n; ++i) {
|
||||
const auto* itime =
|
||||
ecl_file_view_iget_named_kw(globView, ITIME_kw, i);
|
||||
|
||||
const auto* itime_data =
|
||||
static_cast<const int*>(ecl_kw_iget_ptr(itime, 0));
|
||||
|
||||
steps[i] = itime_data[0];
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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_ECLCASEUTILITIES_HEADER_INCLUDED
|
||||
#define OPM_ECLCASEUTILITIES_HEADER_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Opm { namespace ECLCaseUtilities {
|
||||
|
||||
/// Basic information about an ECL result set.
|
||||
class ResultSet
|
||||
{
|
||||
public:
|
||||
using Path = boost::filesystem::path;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] casename File name prefix or full path to one of a
|
||||
/// result set's common representative files. Usually one of the
|
||||
/// '.DATA', '.EGRID' (or .GRID) or .UNRST files.
|
||||
explicit ResultSet(const Path& casename);
|
||||
|
||||
/// Retrieve name of result set's grid file.
|
||||
Path gridFile() const;
|
||||
|
||||
/// Retrieve name of result set's init file.
|
||||
Path initFile() const;
|
||||
|
||||
/// Retrieve name of result set's restart file corresponding to a
|
||||
/// particular report/restart step ID.
|
||||
///
|
||||
/// \param[in] reportStepID Numeric report (restart) step
|
||||
/// identifier. Must be non-negative. Typically one of the IDs
|
||||
/// produced by member function reportStepIDs().
|
||||
///
|
||||
/// \return name of result set's restart file corresponding to \p
|
||||
/// reportStepID.
|
||||
Path restartFile(const int reportStepID) const;
|
||||
|
||||
/// Predicate for whether or not this particular result set uses a
|
||||
/// unified restart file.
|
||||
///
|
||||
/// If this function returns true, then all calls to function
|
||||
/// restartFile() will return the same file name. In other words,
|
||||
/// the restart file need not be reopened when switching from one
|
||||
/// report step to another.
|
||||
///
|
||||
/// \return Whether or not this result set uses a unified restart
|
||||
/// file.
|
||||
bool isUnifiedRestart() const;
|
||||
|
||||
/// Retrieve set of report step IDs stored in result set.
|
||||
///
|
||||
/// Starts at zero (i.e., \code reportStepIDs()[0] == 0 \endcode) if
|
||||
/// the run is configured to output the initial solution state.
|
||||
/// Otherwise, typically starts at one. Uses the restart
|
||||
/// specification (RSSPEC) file to identify the stored report steps.
|
||||
///
|
||||
/// Note: Report step IDs will be increasing, but need not be
|
||||
/// contiguous. Non-contiguous IDs arise when the simulation run
|
||||
/// does not store all of its report/restart steps (Mnemonics
|
||||
/// 'BASIC' and 'FREQ' of input keyword RPTRST).
|
||||
///
|
||||
/// \return Sequence of increasing report step IDs.
|
||||
std::vector<int> reportStepIDs() const;
|
||||
|
||||
private:
|
||||
/// Case prefix. Typical form is "path/to/case", but might be
|
||||
/// "path/to/case.01" too.
|
||||
boost::filesystem::path prefix_;
|
||||
|
||||
/// Whether or not this result set has a unified restart file
|
||||
/// (.UNRST, .FUNRST).
|
||||
bool isUnified_;
|
||||
};
|
||||
|
||||
}} // Opm::ECLCaseUtilities
|
||||
|
||||
#endif // OPM_ECLCASEUTILITIES_HEADER_INCLUDED
|
@ -102,6 +102,10 @@ public:
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const;
|
||||
|
||||
std::vector<double>
|
||||
reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const;
|
||||
|
||||
private:
|
||||
std::vector<double> smin_;
|
||||
std::vector<double> smax_;
|
||||
@ -146,6 +150,48 @@ Impl::eval(const TableEndPoints& tep,
|
||||
return effsat;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFunc::TwoPointScaling::
|
||||
Impl::reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const
|
||||
{
|
||||
const auto srng = tep.high - tep.low;
|
||||
|
||||
auto unscaledsat = std::vector<double>{};
|
||||
unscaledsat.reserve(sp.size());
|
||||
|
||||
for (const auto& eval_pt : sp) {
|
||||
const auto cell = eval_pt.cell;
|
||||
|
||||
const auto sLO = this->smin_[cell];
|
||||
const auto sHI = this->smax_[cell];
|
||||
|
||||
unscaledsat.push_back(0.0);
|
||||
auto& s_unsc = unscaledsat.back();
|
||||
|
||||
if (! (eval_pt.sat > tep.low)) {
|
||||
// s <= minimum tabulated saturation.
|
||||
// Map to Minimum Input Saturation in cell (sLO).
|
||||
s_unsc = sLO;
|
||||
}
|
||||
else if (! (eval_pt.sat < tep.high)) {
|
||||
// s >= maximum tabulated saturation.
|
||||
// Map to Maximum Input Saturation in cell (sHI).
|
||||
s_unsc = sHI;
|
||||
}
|
||||
else {
|
||||
// s in tabulated interval (tep.low, tep.high)
|
||||
// Map to Input Saturation in (sLO, sHI)
|
||||
const auto t =
|
||||
(eval_pt.sat - tep.low) / srng;
|
||||
|
||||
s_unsc = sLO + t*(sHI - sLO);
|
||||
}
|
||||
}
|
||||
|
||||
return unscaledsat;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Class Opm::ThreePointScaling::Impl
|
||||
// ---------------------------------------------------------------------
|
||||
@ -174,6 +220,10 @@ public:
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const;
|
||||
|
||||
std::vector<double>
|
||||
reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const;
|
||||
|
||||
private:
|
||||
std::vector<double> smin_;
|
||||
std::vector<double> sdisp_;
|
||||
@ -223,6 +273,57 @@ Impl::eval(const TableEndPoints& tep,
|
||||
return effsat;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFunc::ThreePointScaling::
|
||||
Impl::reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const
|
||||
{
|
||||
auto unscaledsat = std::vector<double>{};
|
||||
unscaledsat.reserve(sp.size());
|
||||
|
||||
for (const auto& eval_pt : sp) {
|
||||
const auto cell = eval_pt.cell;
|
||||
|
||||
unscaledsat.push_back(0.0);
|
||||
auto& s_unsc = unscaledsat.back();
|
||||
|
||||
const auto sLO = this->smin_ [cell];
|
||||
const auto sR = this->sdisp_[cell];
|
||||
const auto sHI = this->smax_ [cell];
|
||||
|
||||
if (! (eval_pt.sat > tep.low)) {
|
||||
// s <= minimum tabulated saturation.
|
||||
// Map to Minimum Input Saturation in cell (sLO).
|
||||
s_unsc = sLO;
|
||||
}
|
||||
else if (! (eval_pt.sat < tep.high)) {
|
||||
// s >= maximum tabulated saturation.
|
||||
// Map to Maximum Input Saturation in cell (sHI).
|
||||
s_unsc = sHI;
|
||||
}
|
||||
else if (eval_pt.sat < tep.disp) {
|
||||
// s in tabulated interval (tep.low, tep.disp)
|
||||
// Map to Input Saturation in (sLO, sR)
|
||||
const auto t =
|
||||
(eval_pt.sat - tep.low)
|
||||
/ (tep.disp - tep.low);
|
||||
|
||||
s_unsc = sLO + t*(sR - sLO);
|
||||
}
|
||||
else {
|
||||
// s in tabulated interval (tep.disp, tep.high)
|
||||
// Map to Input Saturation in (sR, sHI)
|
||||
const auto t =
|
||||
(eval_pt.sat - tep.disp)
|
||||
/ (tep.high - tep.disp);
|
||||
|
||||
s_unsc = sR + t*(sHI - sR);
|
||||
}
|
||||
}
|
||||
|
||||
return unscaledsat;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EPS factory functions for two-point and three-point scaling options
|
||||
// ---------------------------------------------------------------------
|
||||
@ -1058,6 +1159,13 @@ Opm::SatFunc::TwoPointScaling::eval(const TableEndPoints& tep,
|
||||
return this->pImpl_->eval(tep, sp);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFunc::TwoPointScaling::reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const
|
||||
{
|
||||
return this->pImpl_->reverse(tep, sp);
|
||||
}
|
||||
|
||||
std::unique_ptr<Opm::SatFunc::EPSEvalInterface>
|
||||
Opm::SatFunc::TwoPointScaling::clone() const
|
||||
{
|
||||
@ -1109,6 +1217,13 @@ Opm::SatFunc::ThreePointScaling::eval(const TableEndPoints& tep,
|
||||
return this->pImpl_->eval(tep, sp);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::SatFunc::ThreePointScaling::reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const
|
||||
{
|
||||
return this->pImpl_->reverse(tep, sp);
|
||||
}
|
||||
|
||||
std::unique_ptr<Opm::SatFunc::EPSEvalInterface>
|
||||
Opm::SatFunc::ThreePointScaling::clone() const
|
||||
{
|
||||
|
@ -87,6 +87,27 @@ namespace Opm { namespace SatFunc {
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const = 0;
|
||||
|
||||
/// Derive unscaled (raw) input saturations from a sequence of table
|
||||
/// points (independent variate in a tabulated saturation function).
|
||||
///
|
||||
/// This function maps the result of \code eval() \endcode back to
|
||||
/// its original arguments. In other words, this is the inverse of
|
||||
/// member function \code eval() \endcode.
|
||||
///
|
||||
/// \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 input 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>
|
||||
reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const = 0;
|
||||
|
||||
/// Virtual copy constructor.
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const = 0;
|
||||
|
||||
/// Destructor. Must be virtual.
|
||||
@ -152,7 +173,7 @@ namespace Opm { namespace SatFunc {
|
||||
/// 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.
|
||||
/// \code tep.disp \endcode is never referenced.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points. The maximum cell
|
||||
/// index (\code sp[i].cell \endcode) must be strictly less than
|
||||
@ -166,6 +187,29 @@ namespace Opm { namespace SatFunc {
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
/// Derive unscaled (raw) input saturations from a sequence of table
|
||||
/// points (independent variate in a tabulated saturation function).
|
||||
///
|
||||
/// This function maps the result of \code eval() \endcode back to
|
||||
/// its original arguments. In other words, this is the inverse of
|
||||
/// member function \code eval() \endcode.
|
||||
///
|
||||
/// \param[in] tep Static end points that identify the saturation
|
||||
/// scaling intervals of a particular tabulated saturation
|
||||
/// function. The reverse mapping procedure considers only \code
|
||||
/// tep.low \endcode and \code tep.high \endcode. The value of
|
||||
/// \code tep.disp \endcode is never referenced.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points.
|
||||
///
|
||||
/// \return Sequence of input 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>
|
||||
reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
/// Virtual copy constructor.
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const override;
|
||||
|
||||
private:
|
||||
@ -237,9 +281,9 @@ namespace Opm { namespace SatFunc {
|
||||
///
|
||||
/// \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.
|
||||
/// function. All defined end points---\code tep.low \endcode,
|
||||
/// \code \tep.disp \endcode, and \code tep.high \endcode---are
|
||||
/// relevant and referenced in the evaluation procedure.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points. The maximum cell
|
||||
/// index (\code sp[i].cell \endcode) must be strictly less than
|
||||
@ -253,6 +297,29 @@ namespace Opm { namespace SatFunc {
|
||||
eval(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
/// Derive unscaled (raw) input saturations from a sequence of table
|
||||
/// points (independent variate in a tabulated saturation function).
|
||||
///
|
||||
/// This function maps the result of \code eval() \endcode back to
|
||||
/// its original arguments. In other words, this is the inverse of
|
||||
/// member function \code eval() \endcode.
|
||||
///
|
||||
/// \param[in] tep Static end points that identify the saturation
|
||||
/// scaling intervals of a particular tabulated saturation
|
||||
/// function. All defined end points---\code tep.low \endcode,
|
||||
/// \code \tep.disp \endcode, and \code tep.high \endcode---are
|
||||
/// relevant and referenced in the reverse mapping procedure.
|
||||
///
|
||||
/// \param[in] sp Sequence of saturation points.
|
||||
///
|
||||
/// \return Sequence of input 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>
|
||||
reverse(const TableEndPoints& tep,
|
||||
const SaturationPoints& sp) const override;
|
||||
|
||||
/// Virtual copy constructor.
|
||||
virtual std::unique_ptr<EPSEvalInterface> clone() const override;
|
||||
|
||||
private:
|
||||
|
@ -18,22 +18,186 @@
|
||||
*/
|
||||
|
||||
#include <opm/utility/ECLFluxCalc.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<double>
|
||||
computeGravDZ(const std::vector<int>& neigh,
|
||||
const double grav,
|
||||
const std::vector<double>& depth)
|
||||
{
|
||||
const auto nf = neigh.size() / 2;
|
||||
|
||||
auto gdz = std::vector<double>{};
|
||||
gdz.reserve(nf);
|
||||
|
||||
for (auto f = 0*nf; f < nf; ++f) {
|
||||
const auto c1 = neigh[2*f + 0];
|
||||
const auto c2 = neigh[2*f + 1];
|
||||
|
||||
gdz.push_back(grav * (depth[c2] - depth[c1]));
|
||||
}
|
||||
|
||||
return gdz;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
pvtnumVector(const ::Opm::ECLGraph& G,
|
||||
const ::Opm::ECLInitFileData& init)
|
||||
{
|
||||
auto pvtnum = G.rawLinearisedCellData<int>(init, "PVTNUM");
|
||||
|
||||
if (pvtnum.empty()) {
|
||||
// PVTNUM missing in one or more of the grids managed by 'G'.
|
||||
// Put all cells in PVTNUM region 1.
|
||||
pvtnum.assign(G.numCells(), 1);
|
||||
}
|
||||
|
||||
return pvtnum;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
depthVector(const ::Opm::ECLGraph& G,
|
||||
const ::Opm::ECLInitFileData& init)
|
||||
{
|
||||
// Note: ECLGraph does not support unit conversion of INIT data so
|
||||
// we need to perform the requisite conversions ourselves.
|
||||
auto depth = G.rawLinearisedCellData<double>(init, "DEPTH");
|
||||
|
||||
if (depth.empty()) {
|
||||
// DEPTH missing in one or more of the grids managed by 'G'.
|
||||
// Put all cells at zero depth, which turns off gravity.
|
||||
depth.assign(G.numCells(), 0.0);
|
||||
}
|
||||
|
||||
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
|
||||
const auto usys = ::Opm::ECLUnits::
|
||||
createUnitSystem(ih[ INTEHEAD_UNIT_INDEX ]);
|
||||
|
||||
const auto depthscale = usys->depth();
|
||||
|
||||
for (auto& zi : depth) {
|
||||
zi = ::Opm::unit::convert::from(zi, depthscale);
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
disgasVector(const ::Opm::ECLGraph& G,
|
||||
const bool is_liveoil,
|
||||
const ::Opm::ECLRestartData& rstrt)
|
||||
{
|
||||
auto disgas = std::vector<double>{};
|
||||
|
||||
if (! is_liveoil) {
|
||||
// Oil model does not use dissolved gas. We don't need to
|
||||
// provide Rs data, so return zero (simplifies calling code).
|
||||
|
||||
disgas.assign(G.numCells(), 0.0);
|
||||
|
||||
return disgas;
|
||||
}
|
||||
|
||||
// Gas model does use vaporised oil. Extract Rs data or throw if
|
||||
// unavailable.
|
||||
disgas = G.linearisedCellData(rstrt, "RS",
|
||||
&::Opm::ECLUnits::UnitSystem::
|
||||
dissolvedGasOilRat);
|
||||
|
||||
if (disgas.empty()) {
|
||||
throw std::invalid_argument {
|
||||
"Restart Data Does Not Provide "
|
||||
"Dissolved Gas/Oil Ratio Data "
|
||||
"for Live Oil PVT Model"
|
||||
};
|
||||
}
|
||||
|
||||
return disgas;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
vapoilVector(const ::Opm::ECLGraph& G,
|
||||
const bool is_wetgas,
|
||||
const ::Opm::ECLRestartData& rstrt)
|
||||
{
|
||||
auto vapoil = std::vector<double>{};
|
||||
|
||||
if (! is_wetgas) {
|
||||
// Gas model does not use vaporised oil. We don't need to
|
||||
// provide Rv data, so return zero (simplifies calling code).
|
||||
|
||||
vapoil.assign(G.numCells(), 0.0);
|
||||
|
||||
return vapoil;
|
||||
}
|
||||
|
||||
// Gas model does use vaporised oil. Extract Rv data or throw if
|
||||
// unavailable.
|
||||
vapoil = G.linearisedCellData(rstrt, "RV",
|
||||
&::Opm::ECLUnits::UnitSystem::
|
||||
vaporisedOilGasRat);
|
||||
|
||||
if (vapoil.empty()) {
|
||||
throw std::invalid_argument {
|
||||
"Restart Data Does Not Provide "
|
||||
"Vaporised Oil/Gas Ratio Data "
|
||||
"for Wet Gas PVT Model"
|
||||
};
|
||||
}
|
||||
|
||||
return vapoil;
|
||||
}
|
||||
|
||||
template <class PVTPtr>
|
||||
void verify_active_phase(const PVTPtr& pvt,
|
||||
const std::string& phase)
|
||||
{
|
||||
if (! pvt) {
|
||||
throw std::logic_error {
|
||||
"Cannot Compute " + phase +
|
||||
" PVT Unless " + phase +
|
||||
" is an Active Phase"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
ECLFluxCalc::ECLFluxCalc(const ECLGraph& graph,
|
||||
ECLSaturationFunc&& satfunc)
|
||||
ECLFluxCalc::ECLFluxCalc(const ECLGraph& graph,
|
||||
const ECLInitFileData& init,
|
||||
const double grav,
|
||||
const bool useEPS)
|
||||
: graph_(graph)
|
||||
, satfunc_(std::move(satfunc))
|
||||
, satfunc_(graph, init, useEPS)
|
||||
, rmap_(pvtnumVector(graph, init))
|
||||
, neighbours_(graph.neighbours())
|
||||
, transmissibility_(graph.transmissibility())
|
||||
, gravDz_(computeGravDZ(neighbours_, grav, depthVector(graph, init)))
|
||||
, pvtGas_(ECLPVT::CreateGasPVTInterpolant::fromECLOutput(init))
|
||||
, pvtOil_(ECLPVT::CreateOilPVTInterpolant::fromECLOutput(init))
|
||||
, pvtWat_(ECLPVT::CreateWaterPVTInterpolant::fromECLOutput(init))
|
||||
{
|
||||
const auto& lh = init.keywordData<bool>(LOGIHEAD_KW);
|
||||
|
||||
this->disgas_ = lh[ LOGIHEAD_RS_INDEX ]; // Live Oil?
|
||||
this->vapoil_ = lh[ LOGIHEAD_RV_INDEX ]; // Wet Gas?
|
||||
}
|
||||
|
||||
|
||||
@ -42,16 +206,10 @@ namespace Opm
|
||||
|
||||
std::vector<double>
|
||||
ECLFluxCalc::flux(const ECLRestartData& rstrt,
|
||||
const ECLPhaseIndex phase) const
|
||||
const ECLPhaseIndex phase) const
|
||||
{
|
||||
// Obtain dynamic data.
|
||||
DynamicData dyn_data;
|
||||
dyn_data.pressure = graph_
|
||||
.linearisedCellData(rstrt, "PRESSURE",
|
||||
&ECLUnits::UnitSystem::pressure);
|
||||
|
||||
dyn_data.relperm = this->satfunc_
|
||||
.relperm(this->graph_, rstrt, phase);
|
||||
const auto dyn_data = this->phaseProperties(rstrt, phase);
|
||||
|
||||
// Compute fluxes per connection.
|
||||
const int num_conn = transmissibility_.size();
|
||||
@ -71,16 +229,282 @@ namespace Opm
|
||||
{
|
||||
const int c1 = neighbours_[2*connection];
|
||||
const int c2 = neighbours_[2*connection + 1];
|
||||
const double transmissibility = transmissibility_[connection];
|
||||
const double viscosity = 1.0 * prefix::centi * unit::Poise;
|
||||
const auto& pressure = dyn_data.pressure;
|
||||
|
||||
const int upwind_cell = (pressure[c2] > pressure[c1]) ? c2 : c1;
|
||||
const double kr = dyn_data.relperm[upwind_cell];
|
||||
// Phase pressure in connecting cells.
|
||||
const auto p1 = dyn_data.pressure[c1];
|
||||
const auto p2 = dyn_data.pressure[c2];
|
||||
|
||||
const double mobility = kr / viscosity;
|
||||
return mobility * transmissibility * (pressure[c1] - pressure[c2]);
|
||||
// Phase density at interface: Arith. avg. of cell values.
|
||||
const auto rho =
|
||||
(dyn_data.density[c1] + dyn_data.density[c2]) / 2.0;
|
||||
|
||||
// Phase potential drop across interface.
|
||||
const auto dh = p1 - p2 + rho*this->gravDz_[connection];
|
||||
|
||||
// Phase mobility at interface: Upstream weighting (phase pot).
|
||||
const auto ucell = (dh < 0.0) ? c2 : c1;
|
||||
const auto mob = dyn_data.mobility[ucell];
|
||||
|
||||
// Background (static) transmissibility.
|
||||
const auto T = this->transmissibility_[connection];
|
||||
|
||||
return mob * T * dh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ECLFluxCalc::DynamicData
|
||||
ECLFluxCalc::phaseProperties(const ECLRestartData& rstrt,
|
||||
const ECLPhaseIndex phase) const
|
||||
{
|
||||
auto dyn_data = DynamicData{};
|
||||
|
||||
// Step 1 of Phase Pressure Calculation.
|
||||
// Retrieve oil pressure directly from result set.
|
||||
dyn_data.pressure = this->graph_
|
||||
.linearisedCellData(rstrt, "PRESSURE",
|
||||
&ECLUnits::UnitSystem::pressure);
|
||||
|
||||
// Step 1 of Mobility Calculation.
|
||||
// Store phase's relative permeability values.
|
||||
dyn_data.mobility =
|
||||
this->satfunc_.relperm(this->graph_, rstrt, phase);
|
||||
|
||||
// Step 1 of Mass Density (Reservoir Conditions) Calculation.
|
||||
// Allocate space for storing the cell values.
|
||||
dyn_data.density.assign(this->graph_.numCells(), 0.0);
|
||||
|
||||
switch (phase) {
|
||||
case ECLPhaseIndex::Aqua:
|
||||
return this->watPVT(std::move(dyn_data));
|
||||
|
||||
case ECLPhaseIndex::Liquid:
|
||||
return this->oilPVT(rstrt, std::move(dyn_data));
|
||||
|
||||
case ECLPhaseIndex::Vapour:
|
||||
return this->gasPVT(rstrt, std::move(dyn_data));
|
||||
}
|
||||
|
||||
throw std::invalid_argument {
|
||||
"phaseProperties(): Invalid Phase Identifier"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ECLFluxCalc::DynamicData
|
||||
ECLFluxCalc::gasPVT(const ECLRestartData& rstrt,
|
||||
DynamicData&& dyn_data) const
|
||||
{
|
||||
verify_active_phase(this->pvtGas_, "Gas");
|
||||
|
||||
const auto rv = vapoilVector(this->graph_, this->vapoil_, rstrt);
|
||||
|
||||
this->regionLoop([this, &rv, &dyn_data]
|
||||
(const int regID)
|
||||
{
|
||||
// Note: This function assumes that 'regID' is a traditional
|
||||
// ECL-style one-based region ID such as PVTNUM. Subtract one,
|
||||
// where approriate, to generate zero-based region indices.
|
||||
|
||||
const auto Rv = ECLPVT::Gas::VaporizedOil {
|
||||
this->gatherRegionSubset(regID, rv)
|
||||
};
|
||||
|
||||
const auto Pg = ECLPVT::Gas::GasPressure {
|
||||
// Cheating. This is Po.
|
||||
this->gatherRegionSubset(regID, dyn_data.pressure)
|
||||
};
|
||||
|
||||
// Mass Density at Reservoir Conditions. Relies on setup code
|
||||
// having allocated sufficient space.
|
||||
{
|
||||
const auto rhoOS = this->vapoil_
|
||||
? this->pvtOil_->surfaceMassDensity(regID - 1)
|
||||
: 0.0;
|
||||
|
||||
const auto rhoGS =
|
||||
this->pvtGas_->surfaceMassDensity(regID - 1);
|
||||
|
||||
const auto Bg = this->pvtGas_
|
||||
->formationVolumeFactor(regID - 1, Rv, Pg);
|
||||
|
||||
auto rhoGr = std::vector<double>{};
|
||||
rhoGr.reserve(Bg.size());
|
||||
|
||||
std::transform(std::begin(Bg),
|
||||
std::end (Bg),
|
||||
std::begin(Rv.data),
|
||||
std::back_inserter(rhoGr),
|
||||
[rhoOS, rhoGS]
|
||||
(const double Bg_i, const double Rv_i)
|
||||
{
|
||||
return (rhoOS*Rv_i + rhoGS) / Bg_i;
|
||||
});
|
||||
|
||||
this->scatterRegionResults(regID, rhoGr, dyn_data.density);
|
||||
}
|
||||
|
||||
// Convert relative permeability values into mobility values
|
||||
// (divide by phase viscosity). Relies on setup code having
|
||||
// computed relative permeability for the phase.
|
||||
{
|
||||
const auto mu = this->pvtGas_->viscosity(regID - 1, Rv, Pg);
|
||||
|
||||
this->computePhaseMobility(regID, mu, dyn_data);
|
||||
}
|
||||
});
|
||||
|
||||
return std::move(dyn_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ECLFluxCalc::DynamicData
|
||||
ECLFluxCalc::oilPVT(const ECLRestartData& rstrt,
|
||||
DynamicData&& dyn_data) const
|
||||
{
|
||||
verify_active_phase(this->pvtOil_, "Oil");
|
||||
|
||||
const auto rs = disgasVector(this->graph_, this->disgas_, rstrt);
|
||||
|
||||
this->regionLoop([this, &rs, &dyn_data]
|
||||
(const int regID)
|
||||
{
|
||||
// Note: This section assumes that 'regID' is a traditional
|
||||
// ECL-style one-based region ID such as PVTNUM. Subtract one,
|
||||
// where approriate, to generate zero-based region indices.
|
||||
|
||||
const auto Rs = ECLPVT::Oil::DissolvedGas {
|
||||
this->gatherRegionSubset(regID, rs)
|
||||
};
|
||||
|
||||
const auto Po = ECLPVT::Oil::OilPressure {
|
||||
// Recall: dyn_data.pressure is Po directly from 'rstrt'.
|
||||
this->gatherRegionSubset(regID, dyn_data.pressure)
|
||||
};
|
||||
|
||||
// Mass Density at Reservoir Conditions. Relies on setup code
|
||||
// having allocated sufficient space.
|
||||
{
|
||||
const auto rhoOS =
|
||||
this->pvtOil_->surfaceMassDensity(regID - 1);
|
||||
|
||||
const auto rhoGS = this->disgas_
|
||||
? this->pvtGas_->surfaceMassDensity(regID - 1)
|
||||
: 0.0;
|
||||
|
||||
const auto Bo = this->pvtOil_
|
||||
->formationVolumeFactor(regID - 1, Rs, Po);
|
||||
|
||||
auto rhoOr = std::vector<double>{};
|
||||
rhoOr.reserve(Bo.size());
|
||||
|
||||
std::transform(std::begin(Bo),
|
||||
std::end (Bo),
|
||||
std::begin(Rs.data),
|
||||
std::back_inserter(rhoOr),
|
||||
[rhoOS, rhoGS]
|
||||
(const double Bo_i, const double Rs_i)
|
||||
{
|
||||
return (rhoOS + rhoGS*Rs_i) / Bo_i;
|
||||
});
|
||||
|
||||
this->scatterRegionResults(regID, rhoOr, dyn_data.density);
|
||||
}
|
||||
|
||||
// Convert relative permeability values into mobility values
|
||||
// (divide by phase viscosity). Relies on setup code having
|
||||
// computed relative permeability for the phase.
|
||||
{
|
||||
const auto mu = this->pvtOil_->viscosity(regID - 1, Rs, Po);
|
||||
|
||||
this->computePhaseMobility(regID, mu, dyn_data);
|
||||
}
|
||||
});
|
||||
|
||||
return std::move(dyn_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ECLFluxCalc::DynamicData
|
||||
ECLFluxCalc::watPVT(DynamicData&& dyn_data) const
|
||||
{
|
||||
verify_active_phase(this->pvtWat_, "Water");
|
||||
|
||||
this->regionLoop([this, &dyn_data]
|
||||
(const int regID)
|
||||
{
|
||||
// Note: This section assumes that 'regID' is a traditional
|
||||
// ECL-style one-based region ID such as PVTNUM. Subtract one,
|
||||
// where approriate, to generate zero-based region indices.
|
||||
|
||||
const auto Pw = ECLPVT::Water::WaterPressure {
|
||||
// Cheating. This is Po.
|
||||
this->gatherRegionSubset(regID, dyn_data.pressure)
|
||||
};
|
||||
|
||||
// Mass Density at Reservoir Conditions. Relies on setup code
|
||||
// having allocated sufficient space.
|
||||
{
|
||||
const auto rhoWS =
|
||||
this->pvtWat_->surfaceMassDensity(regID - 1);
|
||||
|
||||
const auto Bw = this->pvtWat_
|
||||
->formationVolumeFactor(regID - 1, Pw);
|
||||
|
||||
auto rhoWr = std::vector<double>{};
|
||||
rhoWr.reserve(Bw.size());
|
||||
|
||||
std::transform(std::begin(Bw),
|
||||
std::end (Bw),
|
||||
std::back_inserter(rhoWr),
|
||||
[rhoWS](const double Bw_i)
|
||||
{
|
||||
return rhoWS / Bw_i;
|
||||
});
|
||||
|
||||
this->scatterRegionResults(regID, rhoWr, dyn_data.density);
|
||||
}
|
||||
|
||||
// Convert relative permeability values into mobility values
|
||||
// (divide by phase viscosity). Relies on setup code having
|
||||
// computed relative permeability for the phase.
|
||||
{
|
||||
const auto mu = this->pvtWat_->viscosity(regID - 1, Pw);
|
||||
|
||||
this->computePhaseMobility(regID, mu, dyn_data);
|
||||
}
|
||||
});
|
||||
|
||||
return std::move(dyn_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
ECLFluxCalc::computePhaseMobility(const int regID,
|
||||
const std::vector<double>& mu,
|
||||
DynamicData& dyn_data) const
|
||||
{
|
||||
auto kr = this->gatherRegionSubset(regID, dyn_data.mobility);
|
||||
|
||||
std::transform(std::begin(kr), std::end (kr),
|
||||
std::begin(mu), std::begin(kr),
|
||||
std::divides<double>());
|
||||
|
||||
this->scatterRegionResults(regID, kr, dyn_data.mobility);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -22,23 +22,41 @@
|
||||
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPvtGas.hpp>
|
||||
#include <opm/utility/ECLPvtOil.hpp>
|
||||
#include <opm/utility/ECLPvtWater.hpp>
|
||||
#include <opm/utility/ECLRegionMapping.hpp>
|
||||
#include <opm/utility/ECLSaturationFunc.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
class ECLRestartData;
|
||||
class ECLInitFileData;
|
||||
|
||||
/// Class for computing connection fluxes in the absence of flux output.
|
||||
class ECLFluxCalc
|
||||
{
|
||||
public:
|
||||
/// Construct from ECLGraph.
|
||||
/// Construct from ECLGraph and Run Initialization Data.
|
||||
///
|
||||
/// \param[in] graph Connectivity data, as well as providing a means to read data from the restart file.
|
||||
explicit ECLFluxCalc(const ECLGraph& graph,
|
||||
ECLSaturationFunc&& satfunc);
|
||||
/// \param[in] graph Connectivity data, as well as providing a means
|
||||
/// to read data from the restart file.
|
||||
///
|
||||
/// \param[in] init ECLIPSE result set static initialization data
|
||||
/// ("INIT" file).
|
||||
///
|
||||
/// \param[in] grav Gravity constant (9.80665 m/s^2 at Tellus equator).
|
||||
///
|
||||
/// \param[in] useEPS Whether or not to include effects of
|
||||
/// saturation function end-point scaling if activated in the
|
||||
/// result set.
|
||||
ECLFluxCalc(const ECLGraph& graph,
|
||||
const ECLInitFileData& init,
|
||||
const double grav,
|
||||
const bool useEPS);
|
||||
|
||||
/// Retrive phase flux on all connections defined by \code
|
||||
/// graph.neighbours() \endcode.
|
||||
@ -59,16 +77,79 @@ namespace Opm
|
||||
struct DynamicData
|
||||
{
|
||||
std::vector<double> pressure;
|
||||
std::vector<double> relperm;
|
||||
std::vector<double> mobility;
|
||||
std::vector<double> density;
|
||||
};
|
||||
|
||||
double singleFlux(const int connection,
|
||||
const DynamicData& dyn_data) const;
|
||||
|
||||
DynamicData phaseProperties(const ECLRestartData& rstrt,
|
||||
const ECLPhaseIndex phase) const;
|
||||
|
||||
DynamicData gasPVT(const ECLRestartData& rstrt,
|
||||
DynamicData&& dyn_data) const;
|
||||
|
||||
DynamicData oilPVT(const ECLRestartData& rstrt,
|
||||
DynamicData&& dyn_data) const;
|
||||
|
||||
DynamicData watPVT(DynamicData&& dyn_data) const;
|
||||
|
||||
void computePhaseMobility(const int regID,
|
||||
const std::vector<double>& mu,
|
||||
DynamicData& dyn_data) const;
|
||||
|
||||
template <typename T>
|
||||
std::vector<T>
|
||||
gatherRegionSubset(const int reg,
|
||||
const std::vector<T>& x) const
|
||||
{
|
||||
auto y = std::vector<T>{};
|
||||
|
||||
if (x.empty()) {
|
||||
return y;
|
||||
}
|
||||
|
||||
for (const auto& ix : this->rmap_.getRegionIndices(reg)) {
|
||||
y.push_back(x[ix]);
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void scatterRegionResults(const int reg,
|
||||
const std::vector<T>& x_reg,
|
||||
std::vector<T>& x) const
|
||||
{
|
||||
auto i = static_cast<decltype(x_reg.size())>(0);
|
||||
|
||||
for (const auto& ix : this->rmap_.getRegionIndices(reg)) {
|
||||
x[ix] = x_reg[i++];
|
||||
}
|
||||
}
|
||||
|
||||
template <class RegOp>
|
||||
void regionLoop(RegOp&& regOp) const
|
||||
{
|
||||
for (const auto& regID : this->rmap_.activeRegions()) {
|
||||
regOp(regID);
|
||||
}
|
||||
}
|
||||
|
||||
const ECLGraph& graph_;
|
||||
ECLSaturationFunc satfunc_;
|
||||
ECLRegionMapping rmap_;
|
||||
std::vector<int> neighbours_;
|
||||
std::vector<double> transmissibility_;
|
||||
std::vector<double> gravDz_;
|
||||
|
||||
bool disgas_{false};
|
||||
bool vapoil_{false};
|
||||
|
||||
std::unique_ptr<ECLPVT::Gas> pvtGas_;
|
||||
std::unique_ptr<ECLPVT::Oil> pvtOil_;
|
||||
std::unique_ptr<ECLPVT::Water> pvtWat_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -0,0 +1,348 @@
|
||||
/*
|
||||
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_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
|
||||
#define OPM_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLTableInterpolation1D.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm { namespace Interp1D { namespace PiecewisePolynomial {
|
||||
|
||||
/// Piecewise linear interpolation in set of result columns.
|
||||
///
|
||||
/// \tparam Extrapolation Policy class for determining how to
|
||||
/// extrapolate results outside the range covered by the independent
|
||||
/// variate. Must support member functions \c left and \c right that
|
||||
/// extrapolate a tabulated function to the left and right of the
|
||||
/// input range, respectively. Typically a policy class from
|
||||
/// namespace ExtrapolationPolicy.
|
||||
///
|
||||
/// \tparam IsAscendingRange Flag for whether or not the input range is
|
||||
/// sorted ascendingly or descendingly. Class \c Linear assumes that
|
||||
/// the input range is sorted, so if \code IsAscendingRange = false
|
||||
/// \endcode, this implies that the input range is treated as being
|
||||
/// sorted descendingly (i.e., as if by \code std::sort(begin, end,
|
||||
/// std::greater<>{}) \endcode.
|
||||
template <class Extrapolation, bool IsAscendingRange = true>
|
||||
class Linear
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// Essentially a hack. This creates an invalid interpolant and
|
||||
/// exists only to support creating an object backed by an empty
|
||||
/// range that will be subsequently discarded and not actually used.
|
||||
/// This, in turn, is useful in the construction of live oil/wet gas
|
||||
/// property interpolants that have padded tables.
|
||||
///
|
||||
/// \param[in] extrap Instance of the configured extrapolation
|
||||
/// policy class.
|
||||
explicit Linear(Extrapolation&& extrap)
|
||||
: extrap_(std::forward<Extrapolation>(extrap))
|
||||
, nCols_ (0)
|
||||
{}
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \tparam ElmIterator Iterator over the elements of an input range
|
||||
/// or a result column. Typically \code
|
||||
/// std::vector<double>::const_iterator \endcode.
|
||||
///
|
||||
/// \tparam ValueTransform Representation of a value transformation
|
||||
/// that, often, effects unit conversion of input tables.
|
||||
/// Assumed to implement a function call operator that supports
|
||||
/// the syntax
|
||||
/// \code
|
||||
/// w = ValueTransform(v)
|
||||
/// \endcode
|
||||
///
|
||||
/// \param[in] extrap Instance of the configured extrapolation
|
||||
/// policy class.
|
||||
///
|
||||
/// \param[in] xBegin Start of range of independent variate.
|
||||
///
|
||||
/// \param[in] xEnd One past the end of the range of indpendent
|
||||
/// variate.
|
||||
///
|
||||
/// \param[in,out] colIt Sequence of ranges of dependent variates.
|
||||
/// On input, points to beginning of ranges. On output, each
|
||||
/// range pointer is advanced across \code std::distance(xBegin,
|
||||
/// xEnd) \endcode entries. This assumes that the underlying
|
||||
/// ranges are formatted according to ECL result set conventions
|
||||
/// (TAB vector from the INIT file).
|
||||
///
|
||||
/// \param[in] xTransform Value transformation for the independent
|
||||
/// variate. Called for each "valid" element of the input range.
|
||||
///
|
||||
/// \param[in] colTransform Sequence of value transformations for
|
||||
/// the dependent variates. In particular, \code
|
||||
/// colTransform[i]() \endcode is invoked on the \c i-th
|
||||
/// dependent variate if and only if the corresponding element of
|
||||
/// the input range is "valid".
|
||||
template <class ElmIterator, class ValueTransform>
|
||||
Linear(Extrapolation&& extrap,
|
||||
ElmIterator xBegin,
|
||||
ElmIterator xEnd,
|
||||
std::vector<ElmIterator>& colIt,
|
||||
const ValueTransform& xTransform,
|
||||
const std::vector<ValueTransform>& colTransform);
|
||||
|
||||
/// Classify an input point according to the range of the
|
||||
/// interpolant's configured independent variate.
|
||||
///
|
||||
/// Classification is performed according to the configured policy
|
||||
/// for treating the sort order of the input range.
|
||||
///
|
||||
/// \param[in] x Input point.
|
||||
///
|
||||
/// \return Classification of the input point \p x.
|
||||
LocalInterpPoint classifyPoint(const double x) const
|
||||
{
|
||||
return LocalInterpPoint::identify(this->x_, x, Ascending_P{});
|
||||
}
|
||||
|
||||
/// Evaluate interpolant of particular dependent variable at
|
||||
/// particular input point.
|
||||
///
|
||||
/// \param[in] col Column ID of particular dependent variable.
|
||||
///
|
||||
/// \param[in] pt Input point. Result of previous call to member
|
||||
/// function \code classifyPoint() \endcode.
|
||||
///
|
||||
/// \return Value of dependent variable \p col at interpolation
|
||||
/// point \p pt.
|
||||
double evaluate(const std::size_t col,
|
||||
const LocalInterpPoint& pt) const
|
||||
{
|
||||
if (pt.cat == PointCategory::InRange) {
|
||||
// Common case. Input point is within range of x_. Placed
|
||||
// first to enable early return.
|
||||
return this->interpolate(pt, col);
|
||||
}
|
||||
|
||||
auto yval = [this](const std::size_t i,
|
||||
const std::size_t j) -> double
|
||||
{
|
||||
return this->y(i, j);
|
||||
};
|
||||
|
||||
if (pt.cat == PointCategory::LeftOfRange) {
|
||||
// Extrapolate function ot the left of input range.
|
||||
const auto xmin = this->x_.front();
|
||||
|
||||
return this->extrap_.left(this->x_, xmin + pt.t, col, yval);
|
||||
}
|
||||
|
||||
assert (pt.cat == PointCategory::RightOfRange);
|
||||
|
||||
// Extrapolate function ot the right of input range.
|
||||
const auto xmax = this->x_.back();
|
||||
return this->extrap_.right(this->x_, xmax + pt.t, col, yval);
|
||||
}
|
||||
|
||||
/// Retrieve abscissas of interpolant's independent variate.
|
||||
const std::vector<double>& independentVariable() const
|
||||
{
|
||||
return this->x_;
|
||||
}
|
||||
|
||||
/// Retrieve ordinates of one of the interpolant's dependent
|
||||
/// variates.
|
||||
///
|
||||
/// \param[in] col Column ID of particular dependent variable.
|
||||
///
|
||||
/// \return Ordinates corresponding to particular dependent variate,
|
||||
/// with the \c i-th element matching the \c i-th element of the
|
||||
/// independent variate.
|
||||
std::vector<double> resultVariable(const std::size_t col) const
|
||||
{
|
||||
auto result = std::vector<double>{};
|
||||
|
||||
if (col >= this->nCols_) {
|
||||
throw std::domain_error {
|
||||
"Result Column Identifier Ouf of Bounds"
|
||||
};
|
||||
}
|
||||
|
||||
result.reserve(this->x_.size());
|
||||
for (auto n = this->x_.size(), i = 0*n; i < n; ++i) {
|
||||
result.push_back(this->y(i, col));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Predicate for ascendingly sorted input ranges. True_type for
|
||||
/// ascending ranges, false_type for descendingly sorted ranges.
|
||||
using Ascending_P =
|
||||
std::integral_constant<bool, IsAscendingRange>;
|
||||
|
||||
/// Instance of extrapolation policy object. Invoked when input
|
||||
/// points are outside the range of the table's independent variate.
|
||||
Extrapolation extrap_;
|
||||
|
||||
/// Number of result columns.
|
||||
std::size_t nCols_;
|
||||
|
||||
/// Abscissas (independent variate), compressed to valid points
|
||||
/// only.
|
||||
std::vector<double> x_;
|
||||
|
||||
/// Ordinates of dependent variates, compressed to valid points of
|
||||
/// input range. Stored with column index (dependent variate ID)
|
||||
/// cycling the most rapidly.
|
||||
std::vector<double> y_;
|
||||
|
||||
/// Evaluate interpolant of particular dependent variable at
|
||||
/// particular input point.
|
||||
///
|
||||
/// Implements case of input point being within range of table's
|
||||
/// independent variate.
|
||||
///
|
||||
/// \param[in] col Column ID of particular dependent variable.
|
||||
///
|
||||
/// \param[in] pt Input point. Result of previous call to member
|
||||
/// function \code classifyPoint() \endcode.
|
||||
///
|
||||
/// \return Value of dependent variable \p col at interpolation
|
||||
/// point \p pt.
|
||||
double interpolate(const LocalInterpPoint& pt,
|
||||
const std::size_t col) const
|
||||
{
|
||||
assert (pt.cat == PointCategory::InRange);
|
||||
assert (pt.interval + 1 < this->x_.size());
|
||||
|
||||
const auto left = pt.interval + 0;
|
||||
const auto xl = this->x_[left];
|
||||
const auto yl = this->y (left, col);
|
||||
|
||||
const auto right = pt.interval + 1;
|
||||
const auto xr = this->x_[right];
|
||||
const auto yr = this->y (right, col);
|
||||
|
||||
const auto t = pt.t / (xr - xl);
|
||||
|
||||
return t*yr + (1.0 - t)*yl;
|
||||
}
|
||||
|
||||
/// Retrieve value of ordinate at specified row and column pair.
|
||||
///
|
||||
/// \param[in] row Row index.
|
||||
///
|
||||
/// \param[in] col Column index.
|
||||
///
|
||||
/// \return Ordinate at index \code (row, col) \endcode.
|
||||
double y(const std::size_t row,
|
||||
const std::size_t col) const
|
||||
{
|
||||
// Recall: this->y_ stored with column index cycling the most
|
||||
// rapidly.
|
||||
|
||||
assert (col < this->nCols_);
|
||||
assert (row*this->nCols_ + col < this->y_.size());
|
||||
|
||||
return this->y_[row*this->nCols_ + col];
|
||||
}
|
||||
};
|
||||
|
||||
template <class Extrapolation, bool IsAscendingRange>
|
||||
template <class ElmIterator, class ValueTransform>
|
||||
Linear<Extrapolation, IsAscendingRange>::
|
||||
Linear(Extrapolation&& extrap,
|
||||
ElmIterator xBegin,
|
||||
ElmIterator xEnd,
|
||||
std::vector<ElmIterator>& colIt,
|
||||
const ValueTransform& xTransform,
|
||||
const std::vector<ValueTransform>& colTransform)
|
||||
: extrap_(std::forward<Extrapolation>(extrap))
|
||||
, nCols_ (colIt.size())
|
||||
{
|
||||
// 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(xTransform(*xBegin));
|
||||
|
||||
auto colID = 0*colTransform.size();
|
||||
for (auto ci : colIt) {
|
||||
// Store 'y_' with column index cycling most rapidly.
|
||||
this->y_.push_back(colTransform[colID++](*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"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}}} // Opm::Interp1D::PiecewisePolynomial
|
||||
|
||||
#endif // OPM_ECLSIMPLE1DINTERPOLANT_HEADER_INCLUDED
|
@ -19,147 +19,30 @@
|
||||
|
||||
#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,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElmIt>& colIt)
|
||||
: interp_(Extrap{}, xBegin, xEnd, colIt,
|
||||
convert.indep, convert.column)
|
||||
{
|
||||
// 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
|
||||
interpolate(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();
|
||||
const auto pt = this->interp_.classifyPoint(xi);
|
||||
|
||||
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);
|
||||
}
|
||||
y.push_back(this->interp_.evaluate(c.i, pt));
|
||||
}
|
||||
|
||||
return y;
|
||||
@ -168,13 +51,12 @@ interpolate(const ECLPropTableRawData::SizeType nCols,
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::connateSat() const
|
||||
{
|
||||
return this->x_.front();
|
||||
return this->interp_.independentVariable().front();
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::
|
||||
criticalSat(const ECLPropTableRawData::SizeType nCols,
|
||||
const ResultColumn& c) const
|
||||
criticalSat(const ResultColumn& c) const
|
||||
{
|
||||
// Note: Relative permeability functions are presented as non-decreasing
|
||||
// functions of the corresponding phase saturation. The internal table
|
||||
@ -184,11 +66,13 @@ criticalSat(const ECLPropTableRawData::SizeType nCols,
|
||||
// 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();
|
||||
const auto y = this->interp_.resultVariable(c.i);
|
||||
|
||||
const auto nRows = y.size();
|
||||
|
||||
auto row = 0 * nRows;
|
||||
for (; row < nRows; ++row) {
|
||||
if (this->y(nCols, row, c) > 0.0) { break; }
|
||||
if (y[row] > 0.0) { break; }
|
||||
}
|
||||
|
||||
if (row == 0) {
|
||||
@ -197,52 +81,48 @@ criticalSat(const ECLPropTableRawData::SizeType nCols,
|
||||
};
|
||||
}
|
||||
|
||||
return this->x_[row - 1];
|
||||
return this->interp_.independentVariable()[row - 1];
|
||||
}
|
||||
|
||||
double
|
||||
Opm::SatFuncInterpolant::SingleTable::maximumSat() const
|
||||
{
|
||||
return this->x_.back();
|
||||
return this->interp_.independentVariable().back();
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
Opm::SatFuncInterpolant::SingleTable::saturationPoints() const
|
||||
{
|
||||
return this->interp_.independentVariable();
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
Opm::SatFuncInterpolant::SatFuncInterpolant(const ECLPropTableRawData& raw)
|
||||
Opm::SatFuncInterpolant::SatFuncInterpolant(const ECLPropTableRawData& raw,
|
||||
const ConvertUnits& convert)
|
||||
: nResCols_(raw.numCols - 1)
|
||||
{
|
||||
using ElmIt = ::Opm::ECLPropTableRawData::ElementIterator;
|
||||
|
||||
if (raw.numPrimary != 1) {
|
||||
throw std::invalid_argument {
|
||||
"Saturation Interpolant Does Not Support Multiple Sub-Tables"
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
this->table_ = MakeInterpolants<SingleTable>::fromRawData(raw,
|
||||
[&convert](ElmIt xBegin, ElmIt xEnd, std::vector<ElmIt>& colIt)
|
||||
{
|
||||
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));
|
||||
}
|
||||
// Note: this constructor needs to advance each 'colIt' across
|
||||
// distance(xBegin, xEnd) entries.
|
||||
return SingleTable(xBegin, xEnd, convert, colIt);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
@ -262,7 +142,7 @@ Opm::SatFuncInterpolant::interpolate(const InTable& t,
|
||||
};
|
||||
}
|
||||
|
||||
return this->table_[t.i].interpolate(this->nResCols_, c, x);
|
||||
return this->table_[t.i].interpolate(c, x);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
@ -285,7 +165,7 @@ Opm::SatFuncInterpolant::criticalSat(const ResultColumn& c) const
|
||||
scrit.reserve(this->table_.size());
|
||||
|
||||
for (const auto& t : this->table_) {
|
||||
scrit.push_back(t.criticalSat(this->nResCols_, c));
|
||||
scrit.push_back(t.criticalSat(c));
|
||||
}
|
||||
|
||||
return scrit;
|
||||
@ -303,3 +183,15 @@ Opm::SatFuncInterpolant::maximumSat() const
|
||||
|
||||
return smax;
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
Opm::SatFuncInterpolant::saturationPoints(const InTable& t) const
|
||||
{
|
||||
if (t.i >= this->table_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Invalid Table ID"
|
||||
};
|
||||
}
|
||||
|
||||
return this->table_[t.i].saturationPoints();
|
||||
}
|
||||
|
@ -20,6 +20,10 @@
|
||||
#ifndef OPM_ECLPROPTABLE_HEADER_INCLUDED
|
||||
#define OPM_ECLPROPTABLE_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLPiecewiseLinearInterpolant.hpp>
|
||||
#include <opm/utility/ECLTableInterpolation1D.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
/// \file
|
||||
@ -43,19 +47,117 @@ namespace Opm {
|
||||
|
||||
/// Raw table data. Column major (Fortran) order. Typically
|
||||
/// copied/extracted directly from TAB vector of INIT result-set.
|
||||
DataVector data;
|
||||
/// Array of size \code numRows * numCols * numPrimary \endcode for
|
||||
/// each table, stored consecutively.
|
||||
DataVector data{};
|
||||
|
||||
/// Primary lookup key for 2D interpolation. Only relevant for PVT
|
||||
/// tables of wet gas and/or live oil (Pg or Rs, respectively).
|
||||
/// Array of size \c numPrimary elements for each table, stored
|
||||
/// consecutively.
|
||||
DataVector primaryKey{};
|
||||
|
||||
/// Number of primary key elements for each individual table. Only
|
||||
/// relevant (i.e., != 1) for PVT tables of wet gas and/or live oil.
|
||||
SizeType numPrimary{0};
|
||||
|
||||
/// 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;
|
||||
/// primary key. Typically corresponds to setting in one of the
|
||||
/// *DIMS keywords. Should normally be at least two for saturation
|
||||
/// functions.
|
||||
SizeType numRows{0};
|
||||
|
||||
/// Number of columns in this table. Varies by keyword/table.
|
||||
SizeType numCols;
|
||||
SizeType numCols{0};
|
||||
|
||||
/// Number of tables of this type. Must match the corresponding
|
||||
/// region keyword.
|
||||
SizeType numTables;
|
||||
SizeType numTables{0};
|
||||
};
|
||||
|
||||
/// Build a sequence of table interpolants from raw tabulated data,
|
||||
/// assuming table conventions in the INIT file's TABDIMS/TAB vectors.
|
||||
///
|
||||
/// \tparam Interpolant Representation of a table interpolant.
|
||||
template <class Interpolant>
|
||||
struct MakeInterpolants
|
||||
{
|
||||
/// Create sequence of table interpolants.
|
||||
///
|
||||
/// This function is aware of the internal layout of the INIT file's
|
||||
/// tabulated function and knows how to identify table data ranges
|
||||
/// corresponding to a single table. In particular we know how to
|
||||
/// the data according to region IDs and how to apply further
|
||||
/// partitioning according to primary lookup keys (e.g., for RS
|
||||
/// nodes in PVTO tables).
|
||||
///
|
||||
/// \tparam Factory Interpolant construction function. Usually a
|
||||
/// class constructor wrapped in a lambda. The call
|
||||
/// \code
|
||||
/// I = Factory(xBegin, xEnd, colIt);
|
||||
/// \endcode
|
||||
/// must construct an instance \c I of type \p Interpolant.
|
||||
/// Here, \c xBegin and \c xEnd demarcate the range of a single
|
||||
/// table's independent variate and \c colIt are column iterators
|
||||
/// positioned at the beginning of each of the table's dependent
|
||||
/// (result) column.
|
||||
///
|
||||
/// Note: The construction function is expected to advance each
|
||||
/// column iterator across \code distance(xBegin, xEnd) \endcode
|
||||
/// entries.
|
||||
///
|
||||
/// \param[in] raw Raw tabulated data. Must correspond to a single
|
||||
/// table vector, e.g. the SWFN data.
|
||||
///
|
||||
/// \param[in] construct Callback function that knows how to build a
|
||||
/// single interpolant given a sequence ranges of of independent
|
||||
/// and dependent tabulated function values. Must advance the
|
||||
/// dependent column iterators and perform appropriate unit
|
||||
/// conversion on the table data if needed (e.g., for capillary
|
||||
/// pressure data or viscosity values).
|
||||
template <class Factory>
|
||||
static std::vector<Interpolant>
|
||||
fromRawData(const ECLPropTableRawData& raw,
|
||||
Factory&& construct)
|
||||
{
|
||||
auto interp = std::vector<Interpolant>{};
|
||||
|
||||
const auto numInterp = raw.numTables * raw.numPrimary;
|
||||
|
||||
// Table format: numRows*numInterp values of first column
|
||||
// (indep. var) followed by numCols-1 dependent variable
|
||||
// (function value result) columns of numRows*numInterp values
|
||||
// each, one column at a time.
|
||||
const auto colStride = raw.numRows * numInterp;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Construct actual interpolants by invoking the
|
||||
// constructor/factory function on each sub-table.
|
||||
for (auto i = 0*numInterp;
|
||||
i < numInterp; ++i, xBegin += raw.numRows)
|
||||
{
|
||||
auto xEnd = xBegin + raw.numRows;
|
||||
|
||||
// Layering violation:
|
||||
// The constructor is expected to advance the result
|
||||
// column iterators across 'numRows' entries.
|
||||
interp.push_back(construct(xBegin, xEnd, colIt));
|
||||
}
|
||||
|
||||
return interp;
|
||||
}
|
||||
};
|
||||
|
||||
/// Collection of 1D interpolants from tabulated functions (e.g., the
|
||||
@ -63,10 +165,29 @@ namespace Opm {
|
||||
class SatFuncInterpolant
|
||||
{
|
||||
public:
|
||||
/// Protocol for converting raw table input data to strict SI unit
|
||||
/// conventions.
|
||||
struct ConvertUnits
|
||||
{
|
||||
/// Convenience type alias for a value transformation.
|
||||
using Converter = std::function<double(const double)>;
|
||||
|
||||
/// How to convert the independent variate (1st column)
|
||||
Converter indep;
|
||||
|
||||
/// How to convert the dependent variates (2nd... columns).
|
||||
std::vector<Converter> column;
|
||||
};
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] raw Raw table data for this collection.
|
||||
explicit SatFuncInterpolant(const ECLPropTableRawData& raw);
|
||||
///
|
||||
/// \param[in] convert Unit conversion support. Mostly applicable
|
||||
/// to capillary pressure. Assumed to convert raw table data to
|
||||
/// strict SI unit conventions.
|
||||
SatFuncInterpolant(const ECLPropTableRawData& raw,
|
||||
const ConvertUnits& convert);
|
||||
|
||||
/// Wrapper type to disambiguate API usage. Represents a table ID.
|
||||
struct InTable {
|
||||
@ -105,6 +226,15 @@ namespace Opm {
|
||||
/// Retrieve maximum saturation in all tables.
|
||||
std::vector<double> maximumSat() const;
|
||||
|
||||
/// Retrieve unscaled sample points of independent variable in
|
||||
/// particular sub-table (saturation region).
|
||||
///
|
||||
/// \param[in] t ID of sub-table of interpolant.
|
||||
///
|
||||
/// \return Abscissas of tabulated saturation function corresponding
|
||||
/// to particular saturation region.
|
||||
const std::vector<double>& saturationPoints(const InTable& t) const;
|
||||
|
||||
private:
|
||||
/// Single tabulated 1D interpolant.
|
||||
class SingleTable
|
||||
@ -120,6 +250,10 @@ namespace Opm {
|
||||
/// \param[in] xEnd One past the end of linear range of
|
||||
/// independent variable values.
|
||||
///
|
||||
/// \param[in] convert Unit conversion support. Mostly
|
||||
/// applicable to capillary pressure. Assumed to convert raw
|
||||
/// table data to strict SI unit conventions.
|
||||
///
|
||||
/// \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
|
||||
@ -128,6 +262,7 @@ namespace Opm {
|
||||
/// for the next table if relevant (and called in a loop).
|
||||
SingleTable(ElmIt xBegin,
|
||||
ElmIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElmIt>& colIt);
|
||||
|
||||
/// Evaluate 1D interpolant in sequence of points.
|
||||
@ -141,33 +276,32 @@ namespace Opm {
|
||||
/// \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;
|
||||
interpolate(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;
|
||||
double criticalSat(const ResultColumn& c) const;
|
||||
|
||||
/// Retrieve maximum saturation in table.
|
||||
double maximumSat() const;
|
||||
|
||||
/// Retrieve unscaled sample points of independent variable.
|
||||
const std::vector<double>& saturationPoints() const;
|
||||
|
||||
private:
|
||||
/// Independent variable.
|
||||
std::vector<double> x_;
|
||||
/// Extrapolation policy for property evaluator/interpolant.
|
||||
using Extrap = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
ExtrapolationPolicy::Constant;
|
||||
|
||||
/// Dependent variable (or variables). Row major (i.e., C)
|
||||
/// ordering. Number of elements: x_.size() * host.nCols_.
|
||||
std::vector<double> y_;
|
||||
/// Type of fundamental table interpolant.
|
||||
using Backend = ::Opm::Interp1D::
|
||||
PiecewisePolynomial::Linear<Extrap>;
|
||||
|
||||
/// Value of dependent variable at position (row,c).
|
||||
double y(const ECLPropTableRawData::SizeType nCols,
|
||||
const ECLPropTableRawData::SizeType row,
|
||||
const ResultColumn& c) const;
|
||||
Backend interp_;
|
||||
};
|
||||
|
||||
/// Number of result/dependent variables (== #table cols - 1).
|
||||
|
350
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtCommon.cpp
vendored
Normal file
350
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtCommon.cpp
vendored
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
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/ECLPvtCommon.hpp>
|
||||
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
|
||||
namespace {
|
||||
double fvfScale(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// B = [rVolume / sVolume(Liquid)]
|
||||
return usys.reservoirVolume()
|
||||
/ usys.surfaceVolumeLiquid();
|
||||
}
|
||||
|
||||
double fvfGasScale(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// B = [rVolume / sVolume(Gas)]
|
||||
return usys.reservoirVolume()
|
||||
/ usys.surfaceVolumeGas();
|
||||
}
|
||||
|
||||
double rsScale(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// Rs = [sVolume(Gas) / sVolume(Liquid)]
|
||||
return usys.surfaceVolumeGas()
|
||||
/ usys.surfaceVolumeLiquid();
|
||||
}
|
||||
|
||||
double rvScale(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// Rv = [sVolume(Liq) / sVolume(Gas)]
|
||||
return usys.surfaceVolumeLiquid()
|
||||
/ usys.surfaceVolumeGas();
|
||||
}
|
||||
|
||||
::Opm::ECLPVT::ConvertUnits::Converter
|
||||
createConverterToSI(const double uscale)
|
||||
{
|
||||
return ::Opm::ECLPVT::ConvertUnits::Converter {
|
||||
[uscale](const double q) -> double
|
||||
{
|
||||
return ::Opm::unit::convert::from(q, uscale);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
density(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(usys.density());
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
pressure(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(usys.pressure());
|
||||
}
|
||||
|
||||
::Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
compressibility(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(1.0 / usys.pressure());
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
disGas(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(rsScale(usys));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
vapOil(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(rvScale(usys));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvf(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(1.0 / fvfScale(usys));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfDerivPress(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/B)/dp
|
||||
const auto B_scale = fvfScale(usys);
|
||||
const auto P_scale = usys.pressure();
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * P_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/B)/dRv
|
||||
const auto B_scale = fvfScale(usys);
|
||||
const auto Rv_scale = rvScale(usys);
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * Rv_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfVisc(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
const auto Bscale = fvfScale(usys);
|
||||
const auto visc_scale = usys.viscosity();
|
||||
|
||||
return createConverterToSI(1.0 / (Bscale * visc_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfViscDerivPress(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/(B*mu))/dp
|
||||
const auto B_scale = fvfScale(usys);
|
||||
const auto P_scale = usys.pressure();
|
||||
const auto mu_scale = usys.viscosity();
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * mu_scale * P_scale));
|
||||
}
|
||||
|
||||
::Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfViscDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/(B*mu))/dRv
|
||||
const auto B_scale = fvfScale(usys);
|
||||
const auto mu_scale = usys.viscosity();
|
||||
const auto Rv_scale = rvScale(usys);
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * mu_scale * Rv_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGas(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
return createConverterToSI(1.0 / fvfGasScale(usys));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGasDerivPress(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/B)/dp
|
||||
const auto B_scale = fvfGasScale(usys);
|
||||
const auto P_scale = usys.pressure();
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * P_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGasDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/B)/dRv
|
||||
const auto B_scale = fvfGasScale(usys);
|
||||
const auto Rv_scale = rvScale(usys);
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * Rv_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGasVisc(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
const auto Bscale = fvfGasScale(usys);
|
||||
const auto visc_scale = usys.viscosity();
|
||||
|
||||
return createConverterToSI(1.0 / (Bscale * visc_scale));
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGasViscDerivPress(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/(B*mu))/dp
|
||||
const auto B_scale = fvfGasScale(usys);
|
||||
const auto P_scale = usys.pressure();
|
||||
const auto mu_scale = usys.viscosity();
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * mu_scale * P_scale));
|
||||
}
|
||||
|
||||
::Opm::ECLPVT::ConvertUnits::Converter
|
||||
Opm::ECLPVT::CreateUnitConverter::ToSI::
|
||||
recipFvfGasViscDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
// d(1/(B*mu))/dRv
|
||||
const auto B_scale = fvfGasScale(usys);
|
||||
const auto mu_scale = usys.viscosity();
|
||||
const auto Rv_scale = rvScale(usys);
|
||||
|
||||
return createConverterToSI(1.0 / (B_scale * mu_scale * Rv_scale));
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
Opm::ECLPVT::PVDx::PVDx(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt)
|
||||
: interp_(Extrap{}, xBegin, xEnd, colIt,
|
||||
convert.indep, convert.column)
|
||||
{}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::PVDx::formationVolumeFactor(const std::vector<double>& p) const
|
||||
{
|
||||
return this->computeQuantity(p,
|
||||
[this](const EvalPt& pt) -> double
|
||||
{
|
||||
// 1 / (1 / B)
|
||||
return 1.0 / this->fvf_recip(pt);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::PVDx::viscosity(const std::vector<double>& p) const
|
||||
{
|
||||
return this->computeQuantity(p,
|
||||
[this](const EvalPt& pt) -> double
|
||||
{
|
||||
// (1 / B) / (1 / (B * mu)
|
||||
return this->fvf_recip(pt) / this->fvf_mu_recip(pt);
|
||||
});
|
||||
}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::PVDx::getPvtCurve(const RawCurve curve) const
|
||||
{
|
||||
assert ((curve == RawCurve::FVF) ||
|
||||
(curve == RawCurve::Viscosity));
|
||||
|
||||
const auto colID = (curve == RawCurve::FVF)
|
||||
? std::size_t{0} : std::size_t{1};
|
||||
|
||||
auto x = this->interp_.independentVariable();
|
||||
auto y = this->interp_.resultVariable(colID);
|
||||
|
||||
assert ((x.size() == y.size()) && "Setup Error");
|
||||
|
||||
// Post-process ordinates according to which curve is requested.
|
||||
if (curve == RawCurve::FVF) {
|
||||
// y == 1/B. Convert to proper FVF.
|
||||
for (auto& yi : y) {
|
||||
yi = 1.0 / yi;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// y == 1/(B*mu). Extract viscosity term through the usual
|
||||
// conversion formula:
|
||||
//
|
||||
// (1 / B) / (1 / (B*mu)).
|
||||
const auto b = this->interp_.resultVariable(0); // 1/B
|
||||
|
||||
assert ((b.size() == y.size()) && "Setup Error");
|
||||
|
||||
for (auto n = y.size(), i = 0*n; i < n; ++i) {
|
||||
y[i] = b[i] / y[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Graph == pair<vector<double>, vector<double>>
|
||||
return FlowDiagnostics::Graph { std::move(x), std::move(y) };
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::surfaceMassDensity(const ECLInitFileData& init,
|
||||
const ECLPhaseIndex phase)
|
||||
{
|
||||
const auto col = [phase]() -> std::size_t
|
||||
{
|
||||
// Column order: 0 <-> oil, 1 <-> water, 2 <-> gas
|
||||
|
||||
switch (phase) {
|
||||
case ECLPhaseIndex::Aqua: return std::size_t{ 1 };
|
||||
case ECLPhaseIndex::Liquid: return std::size_t{ 0 };
|
||||
case ECLPhaseIndex::Vapour: return std::size_t{ 2 };
|
||||
}
|
||||
|
||||
throw std::invalid_argument {
|
||||
"Unsupported Phase ID"
|
||||
};
|
||||
}();
|
||||
|
||||
const auto& tabdims = init.keywordData<int>("TABDIMS");
|
||||
const auto& tab = init.keywordData<double>("TAB");
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_IBDENS_OFFSET_ITEM ] - 1;
|
||||
const auto nreg = tabdims[ TABDIMS_NTDENS_ITEM ];
|
||||
|
||||
// Phase densities for 'phase' constitute 'nreg' consecutive entries
|
||||
// of TAB, starting at an appropriate column offset from the table's
|
||||
// 'start'.
|
||||
auto rho = std::vector<double> {
|
||||
&tab[ start + nreg*(col + 0) ],
|
||||
&tab[ start + nreg*(col + 1) ]
|
||||
};
|
||||
|
||||
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
|
||||
const auto u = ECLUnits::createUnitSystem(ih[ INTEHEAD_UNIT_INDEX ]);
|
||||
|
||||
const auto dens_scale = u->density();
|
||||
|
||||
for (auto& rho_i : rho) {
|
||||
rho_i = unit::convert::from(rho_i, dens_scale);
|
||||
}
|
||||
|
||||
return rho;
|
||||
}
|
768
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtCommon.hpp
vendored
Normal file
768
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtCommon.hpp
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
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_ECLPVTCOMMON_HEADER_INCLUDED
|
||||
#define OPM_ECLPVTCOMMON_HEADER_INCLUDED
|
||||
|
||||
#include <opm/flowdiagnostics/DerivedQuantities.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPiecewiseLinearInterpolant.hpp>
|
||||
#include <opm/utility/ECLPropTable.hpp>
|
||||
#include <opm/utility/ECLTableInterpolation1D.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// Facility for evaluating pressure-dependent fluid properties (formation
|
||||
/// volume factor, viscosities &c) for oil or gas based on tabulated
|
||||
/// descriptions as represented in an ECL result set (INIT file 'TAB'
|
||||
/// vector).
|
||||
|
||||
namespace Opm {
|
||||
class ECLInitFileData;
|
||||
} // Opm
|
||||
|
||||
namespace Opm { namespace ECLPVT {
|
||||
|
||||
/// Protocol for converting raw table input data to strict SI unit
|
||||
/// conventions.
|
||||
struct ConvertUnits
|
||||
{
|
||||
/// Convenience type alias for a value transformation.
|
||||
using Converter = std::function<double(const double)>;
|
||||
|
||||
/// How to convert the independent variate (1st column)
|
||||
Converter indep;
|
||||
|
||||
/// How to convert the dependent variates (2nd... columns).
|
||||
std::vector<Converter> column;
|
||||
};
|
||||
|
||||
/// Collection of unit converters for PVT quantities tabulated in a
|
||||
/// result set's INIT file.
|
||||
struct CreateUnitConverter {
|
||||
/// Convert quantities from native representations to strict SI
|
||||
/// units of measure.
|
||||
struct ToSI {
|
||||
/// Convert quantities of type mass density (\rho) to
|
||||
/// strict SI units of measure (i.e., to kg/m^3).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
density(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type pressure to strict SI units of
|
||||
/// measure (i.e., to Pascal units).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
pressure(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type compressibility to strict SI
|
||||
/// units of measure (i.e., to Pascal^-1 units).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
compressibility(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type dissolved gas-oil ratio (Rs) to
|
||||
/// strict SI units of measure (i.e., to Sm^3/Sm^3).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
disGas(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type vaporised oil-gas ratio (Rv) to
|
||||
/// strict SI units of measure (i.e., to Sm^3/Sm^3).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
vapOil(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type reciprocal formation volume
|
||||
/// factor (1/B) to strict SI units of measure (i.e., to
|
||||
/// Sm^3/Rm^3).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvf(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal
|
||||
/// formation volume factor (1/B) with respect to fluid pressure
|
||||
/// to strict SI units of measure (i.e., to Sm^3/(Rm^3 * Pa)).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfDerivPress(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal
|
||||
/// formation volume factor (1/B) with respect to vaporised
|
||||
/// oil-gas ratio to strict SI units of measure (i.e., to
|
||||
/// Sm^3/(Rm^3 * (Sm^3 / Sm^3))).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type reciprocal product of formation
|
||||
/// volume factor and phase viscosity (1/(B * mu)) to strict SI
|
||||
/// units of measure (i.e., to Sm^3/(Rm^3 * Pa*s)).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfVisc(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal product
|
||||
/// of formation volume factor and phase viscosity (1/(B * mu))
|
||||
/// with respect to fluid pressure to strict SI units of measure
|
||||
/// (i.e., to Sm^3/(Rm^3 * Pa*s * Pa)).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfViscDerivPress(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal product
|
||||
/// of formation volume factor and phase viscosity (1/(B * mu))
|
||||
/// with respect to vaporised oil-gas ratio to strict SI units
|
||||
/// of measure (i.e., to Sm^3/(Rm^3 * Pa*s * (Sm^3/Sm^3))).
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfViscDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type reciprocal formation volume
|
||||
/// factor (1/B) to strict SI units of measure (i.e., to
|
||||
/// Sm^3/Rm^3).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGas(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal
|
||||
/// formation volume factor (1/B) with respect to fluid pressure
|
||||
/// to strict SI units of measure (i.e., to Sm^3/(Rm^3 * Pa)).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGasDerivPress(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal
|
||||
/// formation volume factor (1/B) with respect to vaporised
|
||||
/// oil-gas ratio to strict SI units of measure (i.e., to
|
||||
/// Sm^3/(Rm^3 * (Sm^3 / Sm^3))).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGasDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert quantities of type reciprocal product of formation
|
||||
/// volume factor and phase viscosity (1/(B * mu)) to strict SI
|
||||
/// units of measure (i.e., to Sm^3/(Rm^3 * Pa*s)).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGasVisc(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal product
|
||||
/// of formation volume factor and phase viscosity (1/(B * mu))
|
||||
/// with respect to fluid pressure to strict SI units of measure
|
||||
/// (i.e., to Sm^3/(Rm^3 * Pa*s * Pa)).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGasViscDerivPress(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
/// Convert derivatives of quantities of type reciprocal product
|
||||
/// of formation volume factor and phase viscosity (1/(B * mu))
|
||||
/// with respect to vaporised oil-gas ratio to strict SI units
|
||||
/// of measure (i.e., to Sm^3/(Rm^3 * Pa*s * (Sm^3/Sm^3))).
|
||||
///
|
||||
/// Specialisation for Gas.
|
||||
///
|
||||
/// \param[in] usys Native unit system for particular result
|
||||
/// set.
|
||||
///
|
||||
/// \return Value transformation function affecting requisite
|
||||
/// unit conversion.
|
||||
static ConvertUnits::Converter
|
||||
recipFvfGasViscDerivVapOil(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
};
|
||||
};
|
||||
|
||||
enum class RawCurve {
|
||||
/// Formation volume factor (B_\alpha)
|
||||
FVF,
|
||||
|
||||
/// Viscosity
|
||||
Viscosity,
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
class DenseVector {
|
||||
public:
|
||||
explicit DenseVector(const std::array<double, N>& other)
|
||||
: x_(other)
|
||||
{}
|
||||
|
||||
DenseVector& operator+=(const DenseVector& rhs)
|
||||
{
|
||||
std::transform(std::begin(this->x_),
|
||||
std::end (this->x_),
|
||||
std::begin(rhs .x_),
|
||||
std::begin(this->x_),
|
||||
std::plus<double>());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseVector& operator-=(const DenseVector& rhs)
|
||||
{
|
||||
std::transform(std::begin(this->x_),
|
||||
std::end (this->x_),
|
||||
std::begin(rhs .x_),
|
||||
std::begin(this->x_),
|
||||
std::minus<double>());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseVector& operator*=(const double rhs)
|
||||
{
|
||||
std::transform(std::begin(this->x_),
|
||||
std::end (this->x_),
|
||||
std::begin(this->x_),
|
||||
[rhs](const double xi)
|
||||
{
|
||||
return rhs * xi;
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseVector& operator/=(const double rhs)
|
||||
{
|
||||
std::transform(std::begin(this->x_),
|
||||
std::end (this->x_),
|
||||
std::begin(this->x_),
|
||||
[rhs](const double xi)
|
||||
{
|
||||
return xi / rhs;
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::array<double, N>& array() const
|
||||
{
|
||||
return this->x_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<double, N> x_;
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
DenseVector<N> operator/(DenseVector<N> v, const double a)
|
||||
{
|
||||
return v *= 1.0 / a;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
DenseVector<N> operator*(const double a, DenseVector<N> v)
|
||||
{
|
||||
return v *= a;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
DenseVector<N> operator*(DenseVector<N> v, const double a)
|
||||
{
|
||||
return v *= a;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
DenseVector<N> operator+(DenseVector<N> u, const DenseVector<N>& v)
|
||||
{
|
||||
return u += v;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
DenseVector<N> operator-(DenseVector<N> u, const DenseVector<N>& v)
|
||||
{
|
||||
return u -= v;
|
||||
}
|
||||
|
||||
/// Evaluate pressure-dependent properties (formation volume factor,
|
||||
/// viscosity &c) for dead oil (PVDO) or dry gas (PVDG) from tabulated
|
||||
/// functions as represented in an ECL result set (ECLInitData).
|
||||
class PVDx
|
||||
{
|
||||
public:
|
||||
/// Convenience type alias.
|
||||
using ElemIt = ECLPropTableRawData::ElementIterator;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] xBegin Starting position of range of independent
|
||||
/// variable (phase pressure).
|
||||
///
|
||||
/// \param[in] xEnd One past the end of range of independent
|
||||
/// variable. Must be reachable from \p xBegin.
|
||||
///
|
||||
/// \param[in,out] colIt Column iterators that reference the
|
||||
/// dependent variables (reciprocal FVF &c). Should be size 4 to
|
||||
/// represent the FVF, the viscosity and the derivatives with
|
||||
/// respect to phase pressure. On input, positioned at the
|
||||
/// beginning of a single table's dependent variables columns.
|
||||
/// On output, advanced across \code std::distance(xBegin, xEnd)
|
||||
/// \endcode rows/entries.
|
||||
PVDx(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt);
|
||||
|
||||
/// Evaluate the phase FVF in selection of pressure points.
|
||||
///
|
||||
/// \param[in] p Set of phase pressure points.
|
||||
///
|
||||
/// \return Phase Formation volume factors for each pressure point.
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& p) const;
|
||||
|
||||
/// Evaluate the phase viscosity in selection of pressure points.
|
||||
///
|
||||
/// \param[in] p Set of phase pressure points.
|
||||
///
|
||||
/// \return Phase viscosity for each pressure point.
|
||||
std::vector<double>
|
||||
viscosity(const std::vector<double>& p) const;
|
||||
|
||||
/// Retrieve 2D graph representation PVT property function.
|
||||
///
|
||||
/// \param[in] curve PVT property curve descriptor
|
||||
///
|
||||
/// \return 2D graph for PVT property curve identified by
|
||||
/// requests represented by \p func.
|
||||
///
|
||||
/// Example: Retrieve formation volume factor curve.
|
||||
///
|
||||
/// \code
|
||||
/// const auto graph =
|
||||
/// pvdx.getPvtCurve(ECLPVT::RawCurve::FVF);
|
||||
/// \endcode
|
||||
FlowDiagnostics::Graph getPvtCurve(const RawCurve curve) const;
|
||||
|
||||
private:
|
||||
/// Extrapolation policy for property evaluator/interpolant.
|
||||
using Extrap = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
ExtrapolationPolicy::Linearly;
|
||||
|
||||
/// Type of fundamental table interpolant.
|
||||
using Backend = ::Opm::Interp1D::PiecewisePolynomial::Linear<Extrap>;
|
||||
|
||||
/// Convenience type alias representing the interpolant's evaluation
|
||||
/// point conventions.
|
||||
using EvalPt = std::decay<
|
||||
decltype(std::declval<Backend>().classifyPoint(0.0))
|
||||
>::type;
|
||||
|
||||
/// Interpolant for table of
|
||||
///
|
||||
/// [1/B, 1/(B*mu), d(1/B)/dp, d(1/(B*mu))/dp]
|
||||
///
|
||||
/// versus "pressure" p--typically phase pressure for oil (Po) or
|
||||
/// gas (Pg).
|
||||
Backend interp_;
|
||||
|
||||
/// Translate pressure value to evaluation point and identify
|
||||
/// relevant extrapolation case if needed.
|
||||
EvalPt getInterpPoint(const double p) const
|
||||
{
|
||||
return this->interp_.classifyPoint(p);
|
||||
}
|
||||
|
||||
/// Interpolate reciprocal FVF at evaluation point.
|
||||
double fvf_recip(const EvalPt& pt) const
|
||||
{
|
||||
const auto col = std::size_t{0};
|
||||
|
||||
return this->interp_.evaluate(col, pt);
|
||||
}
|
||||
|
||||
/// Interpolate reciprocal product of FVF and viscosity at
|
||||
/// evaluation point.
|
||||
double fvf_mu_recip(const EvalPt& pt) const
|
||||
{
|
||||
const auto col = std::size_t{1};
|
||||
|
||||
return this->interp_.evaluate(col, pt);
|
||||
}
|
||||
|
||||
/// Compute a dynamic quantity such as the FVF or viscosity for a
|
||||
/// selection of pressure values.
|
||||
template <class EvalDynamicQuant>
|
||||
std::vector<double>
|
||||
computeQuantity(const std::vector<double>& p,
|
||||
EvalDynamicQuant&& eval) const
|
||||
{
|
||||
auto result = std::vector<double>{};
|
||||
result.reserve(p.size());
|
||||
|
||||
for (const auto& pi : p) {
|
||||
result.push_back(eval(this->getInterpPoint(pi)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Evaluate pressure-dependent properties (formation volume factor,
|
||||
/// viscosity &c) for live oil (PVTO) or wet gas (PVTG) from tabulated
|
||||
/// functions as represented in an ECL result set (ECLInitData).
|
||||
template <class SubtableInterpolant>
|
||||
class PVTx
|
||||
{
|
||||
public:
|
||||
PVTx(std::vector<double> key,
|
||||
std::vector<SubtableInterpolant> propInterp)
|
||||
: key_ (std::move(key))
|
||||
, propInterp_(std::move(propInterp))
|
||||
{
|
||||
if (this->key_.size() != this->propInterp_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Size of Key Table Does Not Match "
|
||||
"Number of Sub-Table Interpolants"
|
||||
};
|
||||
}
|
||||
|
||||
if (this->key_.size() < 2) {
|
||||
throw std::invalid_argument {
|
||||
"Mixing-Dependent Property Evaluator "
|
||||
"Must Have At Least Two Inner Tables"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
struct PrimaryKey
|
||||
{
|
||||
const std::vector<double>& data;
|
||||
};
|
||||
|
||||
struct InnerVariate
|
||||
{
|
||||
const std::vector<double>& data;
|
||||
};
|
||||
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const PrimaryKey& key,
|
||||
const InnerVariate& x) const
|
||||
{
|
||||
const auto col = std::size_t{0};
|
||||
|
||||
return this->computeQuantity(key, x,
|
||||
[this, col](const std::size_t curve,
|
||||
const InnerEvalPoint& pt) -> double
|
||||
{ // IFunc: Interpolate 1 / B.
|
||||
return this->propInterp_[curve].evaluate(col, pt);
|
||||
},
|
||||
[](const double recipFvF) -> double
|
||||
{
|
||||
// OFunc: Convert reciprocal FvF to ordinary FvF.
|
||||
return 1.0 / recipFvF;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
viscosity(const PrimaryKey& key,
|
||||
const InnerVariate& x) const
|
||||
{
|
||||
return this->computeQuantity(key, x,
|
||||
[this](const std::size_t curve,
|
||||
const InnerEvalPoint& pt) -> DenseVector<2>
|
||||
{
|
||||
// IFunc: Interpolate 1/B and 1/(B*mu)
|
||||
const auto& I = this->propInterp_[curve];
|
||||
|
||||
const auto fvf_recip = I.evaluate(0, pt);
|
||||
const auto fvf_mu_recip = I.evaluate(1, pt);
|
||||
|
||||
// { (1 / B) , (1 / (B*mu)) }
|
||||
return DenseVector<2>{
|
||||
std::array<double,2>{
|
||||
{ fvf_recip, fvf_mu_recip }
|
||||
}
|
||||
};
|
||||
},
|
||||
[](const DenseVector<2>& recipFvFVisc) -> double
|
||||
{
|
||||
// OFunc: Compute viscosity as
|
||||
// (1 / B) / (1 / B*mu)
|
||||
|
||||
const auto& v = recipFvFVisc.array();
|
||||
|
||||
return v[0] / v[1];
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
using InnerEvalPoint = typename std::decay<
|
||||
decltype(std::declval<SubtableInterpolant>().classifyPoint(0.0))
|
||||
>::type;
|
||||
|
||||
using OuterInterpPoint =
|
||||
::Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint;
|
||||
|
||||
struct InnerInterpPoint {
|
||||
InnerEvalPoint left;
|
||||
InnerEvalPoint right;
|
||||
};
|
||||
|
||||
std::vector<double> key_;
|
||||
std::vector<SubtableInterpolant> propInterp_;
|
||||
|
||||
InnerInterpPoint getInterpPoint(const std::size_t i,
|
||||
const double x) const
|
||||
{
|
||||
assert ((i + 1) < this->propInterp_.size());
|
||||
|
||||
return {
|
||||
this->propInterp_[i + 0].classifyPoint(x),
|
||||
this->propInterp_[i + 1].classifyPoint(x)
|
||||
};
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
auto interpolate(Function&& func,
|
||||
const OuterInterpPoint& outer,
|
||||
const double x) const
|
||||
-> decltype(func(outer.interval, std::declval<InnerEvalPoint>()))
|
||||
{
|
||||
assert (outer.cat == ::Opm::Interp1D::PointCategory::InRange);
|
||||
|
||||
const auto pt =
|
||||
this->getInterpPoint(outer.interval, x);
|
||||
|
||||
const auto yLeft = func(outer.interval + 0, pt.left);
|
||||
const auto yRight = func(outer.interval + 1, pt.right);
|
||||
|
||||
const auto t = outer.t / (this->key_[outer.interval + 1] -
|
||||
this->key_[outer.interval + 0]);
|
||||
|
||||
// t == 0 => yLeft, t == 1 => yRight
|
||||
return t*yRight + (1.0 - t)*yLeft;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
auto extrapLeft(Function&& func,
|
||||
const OuterInterpPoint& outer,
|
||||
const double x) const
|
||||
-> decltype(func(outer.interval, std::declval<InnerEvalPoint>()))
|
||||
{
|
||||
assert (outer.cat == ::Opm::Interp1D::PointCategory::LeftOfRange);
|
||||
assert (outer.interval == 0*this->key_.size());
|
||||
|
||||
const auto pt =
|
||||
this->getInterpPoint(outer.interval, x);
|
||||
|
||||
const auto yLeft = func(0, pt.left);
|
||||
const auto yRight = func(1, pt.right);
|
||||
|
||||
const auto dydk =
|
||||
(yRight - yLeft) / (this->key_[1] - this->key_[0]);
|
||||
|
||||
return yLeft + outer.t*dydk;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
auto extrapRight(Function&& func,
|
||||
const OuterInterpPoint& outer,
|
||||
const double x) const
|
||||
-> decltype(func(outer.interval, std::declval<InnerEvalPoint>()))
|
||||
{
|
||||
const auto nIntervals = this->key_.size() - 1;
|
||||
|
||||
assert (outer.cat == ::Opm::Interp1D::PointCategory::RightOfRange);
|
||||
assert (outer.interval == nIntervals);
|
||||
|
||||
const auto pt = this->getInterpPoint(nIntervals - 1, x);
|
||||
|
||||
const auto yLeft = func(nIntervals - 1, pt.left);
|
||||
const auto yRight = func(nIntervals - 0, pt.right);
|
||||
|
||||
const auto dydk =
|
||||
(yRight - yLeft) / (this->key_[nIntervals - 0] -
|
||||
this->key_[nIntervals - 1]);
|
||||
|
||||
return yRight + outer.t*dydk;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
auto evaluate(const double key,
|
||||
const double x,
|
||||
Function&& func) const
|
||||
-> decltype(func(std::declval<OuterInterpPoint>().interval,
|
||||
std::declval<InnerEvalPoint>()))
|
||||
{
|
||||
const auto outer = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
LocalInterpPoint::identify(this->key_, key);
|
||||
|
||||
switch (outer.cat) {
|
||||
case ::Opm::Interp1D::PointCategory::InRange:
|
||||
return this->interpolate(std::forward<Function>(func),
|
||||
outer, x);
|
||||
|
||||
case ::Opm::Interp1D::PointCategory::LeftOfRange:
|
||||
return this->extrapLeft(std::forward<Function>(func),
|
||||
outer, x);
|
||||
|
||||
case ::Opm::Interp1D::PointCategory::RightOfRange:
|
||||
return this->extrapRight(std::forward<Function>(func),
|
||||
outer, x);
|
||||
}
|
||||
|
||||
throw std::logic_error {
|
||||
"Outer/Primary Key Cannot Be Classified"
|
||||
};
|
||||
}
|
||||
|
||||
template <class InnerFunction, class OuterFunction>
|
||||
std::vector<double>
|
||||
computeQuantity(const PrimaryKey& key,
|
||||
const InnerVariate& x,
|
||||
InnerFunction&& ifunc,
|
||||
OuterFunction ofunc) const
|
||||
{
|
||||
auto result = std::vector<double>{};
|
||||
|
||||
const auto nVals = key.data.size();
|
||||
|
||||
if (x.data.size() != nVals) {
|
||||
throw std::invalid_argument {
|
||||
"Number of Inner Sampling Points Does Not Match "
|
||||
"Number of Outer Sampling Points"
|
||||
};
|
||||
}
|
||||
|
||||
result.reserve(nVals);
|
||||
|
||||
for (auto i = 0*nVals; i < nVals; ++i) {
|
||||
const auto q =
|
||||
this->evaluate(key.data[i], x.data[i],
|
||||
std::forward<InnerFunction>(ifunc));
|
||||
|
||||
result.push_back(ofunc(q));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Extract component mass density at surface conditions.
|
||||
///
|
||||
/// \param[in] init ECL result set INIT file representation.
|
||||
///
|
||||
/// \param[in] phase
|
||||
std::vector<double>
|
||||
surfaceMassDensity(const ECLInitFileData& init,
|
||||
const ECLPhaseIndex phase);
|
||||
}} // Opm::ECLPVT
|
||||
|
||||
#endif // OPM_ECLPVTCOMMON_HEADER_INCLUDED
|
@ -0,0 +1,68 @@
|
||||
#include <opm/utility/ECLPvtCurveCollection.hpp>
|
||||
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
std::vector<int>
|
||||
pvtnumVector(const ::Opm::ECLGraph& G,
|
||||
const ::Opm::ECLInitFileData& init)
|
||||
{
|
||||
auto pvtnum = G.rawLinearisedCellData<int>(init, "PVTNUM");
|
||||
|
||||
if (pvtnum.empty()) {
|
||||
// PVTNUM missing in one or more of the grids managed by 'G'.
|
||||
// Put all cells in PVTNUM region 1.
|
||||
pvtnum.assign(G.numCells(), 1);
|
||||
}
|
||||
|
||||
return pvtnum;
|
||||
}
|
||||
}
|
||||
|
||||
Opm::ECLPVT::ECLPvtCurveCollection::
|
||||
ECLPvtCurveCollection(const ECLGraph& G,
|
||||
const ECLInitFileData& init)
|
||||
: pvtnum_(pvtnumVector(G, init))
|
||||
, gas_ (CreateGasPVTInterpolant::fromECLOutput(init)) // u_p<> -> s_p<>
|
||||
, oil_ (CreateOilPVTInterpolant::fromECLOutput(init)) // u_p<> -> s_p<>
|
||||
{}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::ECLPvtCurveCollection::
|
||||
getPvtCurve(const RawCurve curve,
|
||||
const ECLPhaseIndex phase,
|
||||
const int activeCell) const
|
||||
{
|
||||
if (phase == ECLPhaseIndex::Aqua) {
|
||||
// Not supported at this time.
|
||||
// Return empty.
|
||||
return FlowDiagnostics::Graph{};
|
||||
}
|
||||
|
||||
if (static_cast<decltype(this->pvtnum_.size())>(activeCell)
|
||||
>= this->pvtnum_.size())
|
||||
{
|
||||
// Active cell index out of bounds. Return empty.
|
||||
return FlowDiagnostics::Graph{};
|
||||
}
|
||||
|
||||
// PVTNUM is traditional one-based region identifier. Subtract one to
|
||||
// form valid index into std::vector<>s.
|
||||
const auto regID = this->pvtnum_[activeCell] - 1;
|
||||
|
||||
if (phase == ECLPhaseIndex::Liquid) {
|
||||
// return this->oil_->getPvtCurve(curve, regID);
|
||||
return FlowDiagnostics::Graph{};
|
||||
}
|
||||
|
||||
if (this->gas_) {
|
||||
return this->gas_->getPvtCurve(curve, regID);
|
||||
}
|
||||
else {
|
||||
// Result set does not provide tabulated gas properties. Return
|
||||
// empty.
|
||||
return FlowDiagnostics::Graph{};
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
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_ECLPVTCURVECOLLECTION_HEADER_INCLUDED
|
||||
#define OPM_ECLPVTCURVECOLLECTION_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLPvtGas.hpp>
|
||||
#include <opm/utility/ECLPvtOil.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// Facility for evaluating pressure-dependent fluid properties (formation
|
||||
/// volume factor, viscosities &c) for oil or gas based on tabulated
|
||||
/// descriptions as represented in an ECL result set (INIT file 'TAB'
|
||||
/// vector).
|
||||
|
||||
namespace Opm {
|
||||
class ECLInitFileData;
|
||||
} // Opm
|
||||
|
||||
namespace Opm { namespace ECLPVT {
|
||||
|
||||
class ECLPvtCurveCollection
|
||||
{
|
||||
public:
|
||||
ECLPvtCurveCollection(const ECLGraph& G,
|
||||
const ECLInitFileData& init);
|
||||
|
||||
FlowDiagnostics::Graph
|
||||
getPvtCurve(const RawCurve curve,
|
||||
const ECLPhaseIndex phase,
|
||||
const int activeCell) const;
|
||||
|
||||
private:
|
||||
/// Forward map: Cell -> PVT Region ID
|
||||
std::vector<int> pvtnum_;
|
||||
|
||||
/// Gas PVT property evaluator.
|
||||
std::shared_ptr<Gas> gas_; // shared => default special member funcs.
|
||||
|
||||
/// Oil PVT property evaluator.
|
||||
std::shared_ptr<Oil> oil_;
|
||||
};
|
||||
|
||||
}} // Opm::ECLPVT
|
||||
|
||||
#endif // OPM_ECLPVTCURVECOLLECTION_HEADER_INCLUDED
|
606
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtGas.cpp
vendored
Normal file
606
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtGas.cpp
vendored
Normal file
@ -0,0 +1,606 @@
|
||||
/*
|
||||
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/ECLPvtGas.hpp>
|
||||
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPropTable.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
|
||||
namespace {
|
||||
::Opm::ECLPVT::ConvertUnits
|
||||
createDryGasUnitConverter(const int usys)
|
||||
{
|
||||
using ToSI = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
const auto u = ::Opm::ECLUnits::createUnitSystem(usys);
|
||||
|
||||
// [ Pg, 1/B, 1/(B*mu), d(1/B)/dP, d(1/(B*mu))/dP ]
|
||||
return ::Opm::ECLPVT::ConvertUnits {
|
||||
ToSI::pressure(*u),
|
||||
{
|
||||
ToSI::recipFvfGas(*u),
|
||||
ToSI::recipFvfGasVisc(*u),
|
||||
ToSI::recipFvfGasDerivPress(*u),
|
||||
ToSI::recipFvfGasViscDerivPress(*u)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::pair< ::Opm::ECLPVT::ConvertUnits::Converter,
|
||||
::Opm::ECLPVT::ConvertUnits>
|
||||
wetGasUnitConverter(const int usys)
|
||||
{
|
||||
using ToSI = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
const auto u = ::Opm::ECLUnits::createUnitSystem(usys);
|
||||
|
||||
// Key = Pg
|
||||
// Table = [ Rv, 1/B, 1/(B*mu), d(1/B)/dRv, d(1/(B*mu))/dRv ]
|
||||
|
||||
auto cvrtTable = ::Opm::ECLPVT::ConvertUnits {
|
||||
ToSI::vapOil(*u),
|
||||
{
|
||||
ToSI::recipFvfGas(*u),
|
||||
ToSI::recipFvfGasVisc(*u),
|
||||
ToSI::recipFvfGasDerivVapOil(*u),
|
||||
ToSI::recipFvfGasViscDerivVapOil(*u)
|
||||
}
|
||||
};
|
||||
|
||||
return std::make_pair(ToSI::pressure(*u),
|
||||
std::move(cvrtTable));
|
||||
}
|
||||
}
|
||||
|
||||
// Enable runtime selection of dry or wet gas functions.
|
||||
class PVxGBase
|
||||
{
|
||||
public:
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const = 0;
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const = 0;
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve curve) const = 0;
|
||||
|
||||
virtual std::unique_ptr<PVxGBase> clone() const = 0;
|
||||
};
|
||||
|
||||
// =====================================================================
|
||||
|
||||
class DryGas : public PVxGBase
|
||||
{
|
||||
public:
|
||||
using ElemIt = ::Opm::ECLPVT::PVDx::ElemIt;
|
||||
using ConvertUnits = ::Opm::ECLPVT::ConvertUnits;
|
||||
|
||||
DryGas(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt)
|
||||
: interpolant_(xBegin, xEnd, convert, colIt)
|
||||
{}
|
||||
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& /* rv */,
|
||||
const std::vector<double>& pg) const override
|
||||
{
|
||||
return this->interpolant_.formationVolumeFactor(pg);
|
||||
}
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& /* rv */,
|
||||
const std::vector<double>& pg) const override
|
||||
{
|
||||
return this->interpolant_.viscosity(pg);
|
||||
}
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
|
||||
{
|
||||
return this->interpolant_.getPvtCurve(curve);
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<PVxGBase> clone() const override
|
||||
{
|
||||
return std::unique_ptr<PVxGBase>(new DryGas(*this));
|
||||
}
|
||||
|
||||
private:
|
||||
::Opm::ECLPVT::PVDx interpolant_;
|
||||
};
|
||||
|
||||
// =====================================================================
|
||||
|
||||
class WetGas : public PVxGBase
|
||||
{
|
||||
public:
|
||||
using Extrap = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
ExtrapolationPolicy::Linearly;
|
||||
|
||||
using SubtableInterpolant = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
Linear<Extrap, /* IsAscendingRange = */ false>;
|
||||
|
||||
WetGas(std::vector<double> key,
|
||||
std::vector<SubtableInterpolant> propInterp)
|
||||
: interp_(std::move(key), std::move(propInterp))
|
||||
{}
|
||||
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const override
|
||||
{
|
||||
// PKey Inner C0 C1 C2 C3
|
||||
// Pg Rv 1/B 1/(B*mu) d(1/B)/dRv d(1/(B*mu))/dRv
|
||||
// : : : : :
|
||||
const auto key = TableInterpolant::PrimaryKey { pg };
|
||||
const auto x = TableInterpolant::InnerVariate { rv };
|
||||
|
||||
return this->interp_.formationVolumeFactor(key, x);
|
||||
}
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const override
|
||||
{
|
||||
// PKey Inner C0 C1 C2 C3
|
||||
// Pg Rv 1/B 1/(B*mu) d(1/B)/dRv d(1/(B*mu))/dRv
|
||||
// : : : : :
|
||||
const auto key = TableInterpolant::PrimaryKey { pg };
|
||||
const auto x = TableInterpolant::InnerVariate { rv };
|
||||
|
||||
return this->interp_.viscosity(key, x);
|
||||
}
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve /* curve */) const override
|
||||
{
|
||||
throw std::runtime_error {
|
||||
"Property Evaluator for Wet Gas Does not "
|
||||
"Support Retrieving Raw Curves (Blame BSKA)"
|
||||
};
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<PVxGBase> clone() const override
|
||||
{
|
||||
return std::unique_ptr<PVxGBase>(new WetGas(*this));
|
||||
}
|
||||
|
||||
private:
|
||||
using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>;
|
||||
|
||||
TableInterpolant interp_;
|
||||
};
|
||||
|
||||
// #####################################################################
|
||||
|
||||
namespace {
|
||||
std::vector<std::unique_ptr<PVxGBase>>
|
||||
createDryGas(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
using PVTInterp = std::unique_ptr<PVxGBase>;
|
||||
using ElmIt = ::Opm::ECLPropTableRawData::ElementIterator;
|
||||
|
||||
assert ((raw.numPrimary == 1) &&
|
||||
"Can't Create Dry Gas Function From Wet Gas Table");
|
||||
|
||||
const auto cvrt = createDryGasUnitConverter(usys);
|
||||
|
||||
return ::Opm::MakeInterpolants<PVTInterp>::fromRawData(raw,
|
||||
[&cvrt](ElmIt xBegin, ElmIt xEnd, std::vector<ElmIt>& colIt)
|
||||
{
|
||||
return PVTInterp{ new DryGas(xBegin, xEnd, cvrt, colIt) };
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
extractPrimaryKey(const ::Opm::ECLPropTableRawData& raw,
|
||||
const ::Opm::ECLPropTableRawData::SizeType t,
|
||||
const ::Opm::ECLPVT::ConvertUnits::Converter& cvrtKey)
|
||||
{
|
||||
auto key = std::vector<double>{};
|
||||
key.reserve(raw.numPrimary);
|
||||
|
||||
for (auto begin = std::begin(raw.primaryKey) + (t + 0)*raw.numPrimary,
|
||||
end = begin + raw.numPrimary;
|
||||
begin != end; ++begin)
|
||||
{
|
||||
if (std::abs(*begin) < 1.0e20) {
|
||||
key.push_back(cvrtKey(*begin));
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxGBase>>
|
||||
createWetGas(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
auto ret = std::vector<std::unique_ptr<PVxGBase>>{};
|
||||
ret.reserve(raw.numTables);
|
||||
|
||||
using Extrap = WetGas::Extrap;
|
||||
using StI = WetGas::SubtableInterpolant;
|
||||
using ElemIt = ::Opm::ECLPropTableRawData::ElementIterator;
|
||||
|
||||
const auto cvrt = wetGasUnitConverter(usys);
|
||||
|
||||
auto sti = ::Opm::MakeInterpolants<StI>::fromRawData(raw,
|
||||
[&cvrt](ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
std::vector<ElemIt>& colIt) -> StI
|
||||
{
|
||||
try {
|
||||
return StI(Extrap{}, xBegin, xEnd, colIt,
|
||||
cvrt.second.indep,
|
||||
cvrt.second.column);
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
// No valid nodes. Return invalid.
|
||||
return StI(Extrap{});
|
||||
}
|
||||
});
|
||||
|
||||
for (auto t = 0*raw.numTables;
|
||||
t < raw.numTables; ++t)
|
||||
{
|
||||
auto key = extractPrimaryKey(raw, t, cvrt.first);
|
||||
|
||||
const auto begin = (t + 0)*raw.numPrimary;
|
||||
const auto end = begin + key.size();
|
||||
|
||||
ret.emplace_back(new WetGas(std::move(key),
|
||||
{
|
||||
std::make_move_iterator(std::begin(sti) + begin),
|
||||
std::make_move_iterator(std::begin(sti) + end)
|
||||
}));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxGBase>>
|
||||
createPVTFunction(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
if (raw.numPrimary == 0) {
|
||||
// Malformed Gas PVT table.
|
||||
throw std::invalid_argument {
|
||||
"Gas PVT Table Without Primary Lookup Key"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.numCols != 5) {
|
||||
throw std::invalid_argument {
|
||||
"PVT Table for Gas Must Have Five Columns"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.primaryKey.size() != (raw.numPrimary * raw.numTables)) {
|
||||
throw std::invalid_argument {
|
||||
"Size Mismatch in Pressure Nodes of PVT Table for Gas"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.data.size() !=
|
||||
(raw.numPrimary * raw.numRows * raw.numCols * raw.numTables))
|
||||
{
|
||||
throw std::invalid_argument {
|
||||
"Size Mismatch in Condensed Table Data "
|
||||
"of PVT Table for Gas"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.numPrimary == 1) {
|
||||
return createDryGas(raw, usys);
|
||||
}
|
||||
|
||||
return createWetGas(raw, usys);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxGBase>>
|
||||
clone(const std::vector<std::unique_ptr<PVxGBase>>& src)
|
||||
{
|
||||
auto dest = std::vector<std::unique_ptr<PVxGBase>>{};
|
||||
dest.reserve(src.size());
|
||||
|
||||
for (const auto& p : src) {
|
||||
dest.push_back(p->clone());
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
// #####################################################################
|
||||
// #####################################################################
|
||||
|
||||
// =====================================================================
|
||||
// Class ECLPVT::Gas::Impl
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
class Opm::ECLPVT::Gas::Impl
|
||||
{
|
||||
private:
|
||||
using EvalPtr = std::unique_ptr<PVxGBase>;
|
||||
|
||||
public:
|
||||
Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS);
|
||||
|
||||
Impl(const Impl& rhs);
|
||||
Impl(Impl&& rhs);
|
||||
|
||||
Impl& operator=(const Impl& rhs);
|
||||
Impl& operator=(Impl&& rhs);
|
||||
|
||||
using RegIdx = std::vector<EvalPtr>::size_type;
|
||||
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const RegIdx region,
|
||||
const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const;
|
||||
|
||||
std::vector<double>
|
||||
viscosity(const RegIdx region,
|
||||
const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const;
|
||||
|
||||
double surfaceMassDensity(const RegIdx region) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->rhoS_[region];
|
||||
}
|
||||
|
||||
FlowDiagnostics::Graph
|
||||
getPvtCurve(const RegIdx region,
|
||||
const RawCurve curve) const;
|
||||
|
||||
private:
|
||||
std::vector<EvalPtr> eval_;
|
||||
std::vector<double> rhoS_;
|
||||
|
||||
void validateRegIdx(const RegIdx region) const;
|
||||
};
|
||||
|
||||
Opm::ECLPVT::Gas::Impl::
|
||||
Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS)
|
||||
: eval_(createPVTFunction(raw, usys))
|
||||
, rhoS_(std::move(rhoS))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::Impl::Impl(const Impl& rhs)
|
||||
: eval_(clone(rhs.eval_))
|
||||
, rhoS_(rhs.rhoS_)
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::Impl::Impl(Impl&& rhs)
|
||||
: eval_(std::move(rhs.eval_))
|
||||
, rhoS_(std::move(rhs.rhoS_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::Impl&
|
||||
Opm::ECLPVT::Gas::Impl::operator=(const Impl& rhs)
|
||||
{
|
||||
this->eval_ = clone(rhs.eval_);
|
||||
this->rhoS_ = rhs.rhoS_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Opm::ECLPVT::Gas::Impl&
|
||||
Opm::ECLPVT::Gas::Impl::operator=(Impl&& rhs)
|
||||
{
|
||||
this->eval_ = std::move(rhs.eval_);
|
||||
this->rhoS_ = std::move(rhs.rhoS_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Gas::Impl::
|
||||
formationVolumeFactor(const RegIdx region,
|
||||
const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->formationVolumeFactor(rv, pg);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Gas::Impl::
|
||||
viscosity(const RegIdx region,
|
||||
const std::vector<double>& rv,
|
||||
const std::vector<double>& pg) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->viscosity(rv, pg);
|
||||
}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::Gas::Impl::
|
||||
getPvtCurve(const RegIdx region,
|
||||
const RawCurve curve) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->getPvtCurve(curve);
|
||||
}
|
||||
|
||||
void
|
||||
Opm::ECLPVT::Gas::Impl::validateRegIdx(const RegIdx region) const
|
||||
{
|
||||
if (region >= this->eval_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Region Index " +
|
||||
std::to_string(region) +
|
||||
" Outside Valid Range (0 .. " +
|
||||
std::to_string(this->eval_.size() - 1) + ')'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Class ECLPVT::Gas
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Opm::ECLPVT::Gas::Gas(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS)
|
||||
: pImpl_(new Impl(raw, usys, std::move(rhoS)))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::~Gas()
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::Gas(const Gas& rhs)
|
||||
: pImpl_(new Impl(*rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas::Gas(Gas&& rhs)
|
||||
: pImpl_(std::move(rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Gas&
|
||||
Opm::ECLPVT::Gas::operator=(const Gas& rhs)
|
||||
{
|
||||
this->pImpl_.reset(new Impl(*rhs.pImpl_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Opm::ECLPVT::Gas&
|
||||
Opm::ECLPVT::Gas::operator=(Gas&& rhs)
|
||||
{
|
||||
this->pImpl_ = std::move(rhs.pImpl_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Gas::
|
||||
formationVolumeFactor(const int region,
|
||||
const VaporizedOil& rv,
|
||||
const GasPressure& pg) const
|
||||
{
|
||||
return this->pImpl_->formationVolumeFactor(region, rv.data, pg.data);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Gas::
|
||||
viscosity(const int region,
|
||||
const VaporizedOil& rv,
|
||||
const GasPressure& pg) const
|
||||
{
|
||||
return this->pImpl_->viscosity(region, rv.data, pg.data);
|
||||
}
|
||||
|
||||
double Opm::ECLPVT::Gas::surfaceMassDensity(const int region) const
|
||||
{
|
||||
return this->pImpl_->surfaceMassDensity(region);
|
||||
}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::Gas::
|
||||
getPvtCurve(const RawCurve curve, const int region) const
|
||||
{
|
||||
return this->pImpl_->getPvtCurve(region, curve);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
std::unique_ptr<Opm::ECLPVT::Gas>
|
||||
Opm::ECLPVT::CreateGasPVTInterpolant::
|
||||
fromECLOutput(const ECLInitFileData& init)
|
||||
{
|
||||
using GPtr = ::std::unique_ptr<Opm::ECLPVT::Gas>;
|
||||
|
||||
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
|
||||
const auto iphs = static_cast<unsigned int>(ih[INTEHEAD_PHASE_INDEX]);
|
||||
|
||||
if ((iphs & (1u << 2)) == 0) {
|
||||
// Gas is not an active phase.
|
||||
// Return sentinel (null) pointer.
|
||||
return GPtr{};
|
||||
}
|
||||
|
||||
auto raw = ::Opm::ECLPropTableRawData{};
|
||||
|
||||
const auto& tabdims = init.keywordData<int>("TABDIMS");
|
||||
const auto& tab = init.keywordData<double>("TAB");
|
||||
|
||||
raw.numPrimary = tabdims[ TABDIMS_NRPVTG_ITEM ]; // #Composition nodes
|
||||
raw.numRows = tabdims[ TABDIMS_NPPVTG_ITEM ]; // #Rv (or Pg) nodes
|
||||
raw.numCols = 5; // [ Rv, 1/B, 1/(B*mu), d(1/B)/dRv, d(1/(B*mu))/dRv ]
|
||||
raw.numTables = tabdims[ TABDIMS_NTPVTG_ITEM ]; // # PVTG tables
|
||||
|
||||
// Extract Primary Key (Pg)
|
||||
{
|
||||
const auto nTabElem = raw.numPrimary * raw.numTables;
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_JBPVTG_OFFSET_ITEM ] - 1;
|
||||
|
||||
raw.primaryKey.assign(&tab[start], &tab[start] + nTabElem);
|
||||
}
|
||||
|
||||
// Extract Full Table
|
||||
{
|
||||
const auto nTabElem =
|
||||
raw.numPrimary * raw.numRows * raw.numCols * raw.numTables;
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1;
|
||||
|
||||
raw.data.assign(&tab[start], &tab[start] + nTabElem);
|
||||
}
|
||||
|
||||
auto rhoS = surfaceMassDensity(init, ECLPhaseIndex::Vapour);
|
||||
|
||||
return GPtr{
|
||||
new Gas(raw, ih[ INTEHEAD_UNIT_INDEX ], std::move(rhoS))
|
||||
};
|
||||
}
|
200
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtGas.hpp
vendored
Normal file
200
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtGas.hpp
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
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_ECLPVTGAS_HEADER_INCLUDED
|
||||
#define OPM_ECLPVTGAS_HEADER_INCLUDED
|
||||
|
||||
#include <opm/flowdiagnostics/DerivedQuantities.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace Opm {
|
||||
struct ECLPropTableRawData;
|
||||
class ECLInitFileData;
|
||||
} // Opm
|
||||
|
||||
namespace Opm { namespace ECLPVT {
|
||||
|
||||
/// Interpolant for Basic Gas PVT Relations.
|
||||
class Gas
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] raw Raw tabulated data. Must correspond to the PVTG
|
||||
/// vector of an ECL INIT file..
|
||||
///
|
||||
/// \param[in] usys Unit system convention of the result set from
|
||||
/// which \p raw was extracted. Must correspond to item 3 of the
|
||||
/// INTEHEAD keyword in the INIT file.
|
||||
///
|
||||
/// \param[in] rhoS Mass density of gas at surface conditions.
|
||||
/// Typically computed by \code ECLPVT::surfaceMassDensity()
|
||||
/// \endcode.
|
||||
Gas(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS);
|
||||
|
||||
/// Destructor.
|
||||
~Gas();
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing interpolant for Gas PVT relations.
|
||||
Gas(const Gas& rhs);
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing Gas PVT relation
|
||||
/// interpolant.
|
||||
///
|
||||
/// \param[in] rhs Existing Gas PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
Gas(Gas&& rhs);
|
||||
|
||||
/// Assignment operator
|
||||
///
|
||||
/// \param[in] rhs Existing Gas PVT relation interpolant.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Gas& operator=(const Gas& rhs);
|
||||
|
||||
/// Move assignment operator.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing Gas PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Gas& operator=(Gas&& rhs);
|
||||
|
||||
/// Representation of the Vaporised Oil-Gas Ratio (Rv).
|
||||
///
|
||||
/// Mostly as a convenience to disambiguate the client interface.
|
||||
struct VaporizedOil
|
||||
{
|
||||
/// Vaporised oil-gas ratio data for all sampling points.
|
||||
std::vector<double> data;
|
||||
};
|
||||
|
||||
/// Representation of Gas Phase Pressure (Pg).
|
||||
///
|
||||
/// Mostly as a convenience to disambiguate the client interface.
|
||||
struct GasPressure
|
||||
{
|
||||
/// Gas phase pressure for all sampling points.
|
||||
std::vector<double> data;
|
||||
};
|
||||
|
||||
/// Compute the gas phase formation volume factor in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] rv Vaporised oil-gas ratio. Unused in the case of a
|
||||
/// dry gas model. Size must match number of elements in \code
|
||||
/// pg.data \endcode in the case of wet gas model. Strict SI
|
||||
/// units of measurement.
|
||||
///
|
||||
/// \param[in] pg Gas phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Gas phase formation volume factor. Size equal to number
|
||||
/// of elements in \code po.data \endcode.
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const int region,
|
||||
const VaporizedOil& rv,
|
||||
const GasPressure& pg) const;
|
||||
|
||||
/// Compute the gas phase fluid viscosity in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] rv Vaporised oil-gas ratio. Unused in the case of a
|
||||
/// dry gas model. Size must match number of elements in \code
|
||||
/// pg.data \endcode in the case of wet gas model. Strict SI
|
||||
/// units of measurement.
|
||||
///
|
||||
/// \param[in] pg Gas phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Gas phase fluid viscosity. Size equal to number
|
||||
/// of elements in \code pg.data \endcode.
|
||||
std::vector<double>
|
||||
viscosity(const int region,
|
||||
const VaporizedOil& rv,
|
||||
const GasPressure& pg) const;
|
||||
|
||||
/// Retrieve constant mass density of gas at surface conditions.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \return Mass density of gas at surface in particular model
|
||||
/// region.
|
||||
double surfaceMassDensity(const int region) const;
|
||||
|
||||
/// Retrieve 2D graph representation of Gas PVT property function in
|
||||
/// partcular PVT region.
|
||||
///
|
||||
/// \param[in] curve PVT property curve descriptor
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \return 2D graph for PVT property curve identified by
|
||||
/// requests represented by \p func and \p region.
|
||||
///
|
||||
/// Example: Retrieve gas formation volume factor curve in PVT
|
||||
/// region 0 (zero based, i.e., cells for which PVTNUM==1).
|
||||
///
|
||||
/// \code
|
||||
/// const auto graph =
|
||||
/// pvtGas.getPvtCurve(ECLPVT::RawCurve::FVF, 0);
|
||||
/// \endcode
|
||||
FlowDiagnostics::Graph
|
||||
getPvtCurve(const RawCurve curve, const int region) const;
|
||||
|
||||
private:
|
||||
/// Implementation class.
|
||||
class Impl;
|
||||
|
||||
/// Pointer to implementation.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
/// Basic Gas PVT Relation Interpolant Factory Functions.
|
||||
struct CreateGasPVTInterpolant
|
||||
{
|
||||
/// Create Gas PVT interpolant directly from an ECL result set
|
||||
/// (i.e., from the tabulated functions in the 'TAB' vector.
|
||||
///
|
||||
/// \param[in] init ECL result set INIT file representation.
|
||||
///
|
||||
/// \return Gas PVT interpolant. Nullpointer if gas is not an
|
||||
/// active phase in the result set.
|
||||
static std::unique_ptr<Gas>
|
||||
fromECLOutput(const ECLInitFileData& init);
|
||||
};
|
||||
}} // Opm::ECLPVT
|
||||
|
||||
#endif // OPM_ECLPVTGAS_HEADER_INCLUDED
|
603
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtOil.cpp
vendored
Normal file
603
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtOil.cpp
vendored
Normal file
@ -0,0 +1,603 @@
|
||||
/*
|
||||
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/ECLPvtOil.hpp>
|
||||
|
||||
#include <opm/utility/ECLPropTable.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
|
||||
namespace {
|
||||
::Opm::ECLPVT::ConvertUnits
|
||||
deadOilUnitConverter(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
using ToSI = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
// [ Po, 1/B, 1/(B*mu), d(1/B)/dPo, d(1/(B*mu))/dPo ]
|
||||
return ::Opm::ECLPVT::ConvertUnits {
|
||||
ToSI::pressure(usys),
|
||||
{
|
||||
ToSI::recipFvf(usys),
|
||||
ToSI::recipFvfVisc(usys),
|
||||
ToSI::recipFvfDerivPress(usys),
|
||||
ToSI::recipFvfViscDerivPress(usys)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
::Opm::ECLPVT::ConvertUnits deadOilUnitConverter(const int usys)
|
||||
{
|
||||
const auto u = ::Opm::ECLUnits::createUnitSystem(usys);
|
||||
|
||||
return deadOilUnitConverter(*u);
|
||||
}
|
||||
|
||||
std::pair< ::Opm::ECLPVT::ConvertUnits::Converter,
|
||||
::Opm::ECLPVT::ConvertUnits>
|
||||
liveOilUnitConverter(const int usys)
|
||||
{
|
||||
using ToSI = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
const auto u = ::Opm::ECLUnits::createUnitSystem(usys);
|
||||
|
||||
// Key = Rs
|
||||
// Table = [ Po, 1/B, 1/(B*mu), d(1/B)/dPo, d(1/(B*mu))/dPo ]
|
||||
// = dead oil table format.
|
||||
|
||||
return std::make_pair(ToSI::disGas(*u),
|
||||
deadOilUnitConverter(*u));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Enable runtime selection of dead or live oil functions.
|
||||
class PVxOBase
|
||||
{
|
||||
public:
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const = 0;
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const = 0;
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve curve) const = 0;
|
||||
|
||||
virtual std::unique_ptr<PVxOBase> clone() const = 0;
|
||||
};
|
||||
|
||||
// =====================================================================
|
||||
|
||||
class DeadOil : public PVxOBase
|
||||
{
|
||||
public:
|
||||
using ElemIt = ::Opm::ECLPVT::PVDx::ElemIt;
|
||||
using ConvertUnits = ::Opm::ECLPVT::ConvertUnits;
|
||||
|
||||
DeadOil(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt)
|
||||
: interpolant_(xBegin, xEnd, convert, colIt)
|
||||
{}
|
||||
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& /* rs */,
|
||||
const std::vector<double>& po) const override
|
||||
{
|
||||
return this->interpolant_.formationVolumeFactor(po);
|
||||
}
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& /* rs */,
|
||||
const std::vector<double>& po) const override
|
||||
{
|
||||
return this->interpolant_.viscosity(po);
|
||||
}
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
|
||||
{
|
||||
return this->interpolant_.getPvtCurve(curve);
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<PVxOBase> clone() const override
|
||||
{
|
||||
return std::unique_ptr<PVxOBase>(new DeadOil(*this));
|
||||
}
|
||||
|
||||
private:
|
||||
::Opm::ECLPVT::PVDx interpolant_;
|
||||
};
|
||||
|
||||
// =====================================================================
|
||||
|
||||
class LiveOil : public PVxOBase
|
||||
{
|
||||
public:
|
||||
using Extrap = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
ExtrapolationPolicy::Linearly;
|
||||
|
||||
using SubtableInterpolant = ::Opm::Interp1D::PiecewisePolynomial::
|
||||
Linear<Extrap, /* IsAscendingRange = */ true>;
|
||||
|
||||
LiveOil(std::vector<double> key,
|
||||
std::vector<SubtableInterpolant> propInterp)
|
||||
: interp_(std::move(key), std::move(propInterp))
|
||||
{}
|
||||
|
||||
virtual std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const override
|
||||
{
|
||||
// PKey Inner C0 C1 C2 C3
|
||||
// Rs Po 1/B 1/(B*mu) d(1/B)/dPo d(1/(B*mu))/dPo
|
||||
// : : : : :
|
||||
const auto key = TableInterpolant::PrimaryKey { rs };
|
||||
const auto x = TableInterpolant::InnerVariate { po };
|
||||
|
||||
return this->interp_.formationVolumeFactor(key, x);
|
||||
}
|
||||
|
||||
virtual std::vector<double>
|
||||
viscosity(const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const override
|
||||
{
|
||||
// PKey Inner C0 C1 C2 C3
|
||||
// Rs Po 1/B 1/(B*mu) d(1/B)/dPo d(1/(B*mu))/dPo
|
||||
// : : : : :
|
||||
const auto key = TableInterpolant::PrimaryKey { rs };
|
||||
const auto x = TableInterpolant::InnerVariate { po };
|
||||
|
||||
return this->interp_.viscosity(key, x);
|
||||
}
|
||||
|
||||
virtual Opm::FlowDiagnostics::Graph
|
||||
getPvtCurve(const Opm::ECLPVT::RawCurve /* curve */) const override
|
||||
{
|
||||
throw std::runtime_error {
|
||||
"Property Evaluator for Live Oil Does not "
|
||||
"Support Retrieving Raw Curves (Blame BSKA)"
|
||||
};
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<PVxOBase> clone() const override
|
||||
{
|
||||
return std::unique_ptr<PVxOBase>(new LiveOil(*this));
|
||||
}
|
||||
|
||||
private:
|
||||
using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>;
|
||||
|
||||
TableInterpolant interp_;
|
||||
};
|
||||
|
||||
// #####################################################################
|
||||
|
||||
namespace {
|
||||
std::vector<std::unique_ptr<PVxOBase>>
|
||||
createDeadOil(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
using PVTInterp = std::unique_ptr<PVxOBase>;
|
||||
using ElmIt = ::Opm::ECLPropTableRawData::ElementIterator;
|
||||
|
||||
assert ((raw.numPrimary == 1) &&
|
||||
"Can't Create Dead Oil Function From Live Oil Table");
|
||||
|
||||
const auto cvrt = deadOilUnitConverter(usys);
|
||||
|
||||
return ::Opm::MakeInterpolants<PVTInterp>::fromRawData(raw,
|
||||
[&cvrt](ElmIt xBegin, ElmIt xEnd, std::vector<ElmIt>& colIt)
|
||||
{
|
||||
return PVTInterp{ new DeadOil(xBegin, xEnd, cvrt, colIt) };
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
extractPrimaryKey(const ::Opm::ECLPropTableRawData& raw,
|
||||
const ::Opm::ECLPropTableRawData::SizeType t,
|
||||
const ::Opm::ECLPVT::ConvertUnits::Converter& cvrtKey)
|
||||
{
|
||||
auto key = std::vector<double>{};
|
||||
key.reserve(raw.numPrimary);
|
||||
|
||||
for (auto begin = std::begin(raw.primaryKey) + (t + 0)*raw.numPrimary,
|
||||
end = begin + raw.numPrimary;
|
||||
begin != end; ++begin)
|
||||
{
|
||||
if (std::abs(*begin) < 1.0e20) {
|
||||
key.push_back(cvrtKey(*begin));
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxOBase>>
|
||||
createLiveOil(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
auto ret = std::vector<std::unique_ptr<PVxOBase>>{};
|
||||
ret.reserve(raw.numTables);
|
||||
|
||||
using Extrap = LiveOil::Extrap;
|
||||
using StI = LiveOil::SubtableInterpolant;
|
||||
using ElemIt = ::Opm::ECLPropTableRawData::ElementIterator;
|
||||
|
||||
const auto cvrt = liveOilUnitConverter(usys);
|
||||
|
||||
auto sti = ::Opm::MakeInterpolants<StI>::fromRawData(raw,
|
||||
[&cvrt](ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
std::vector<ElemIt>& colIt) -> StI
|
||||
{
|
||||
try {
|
||||
return StI(Extrap{}, xBegin, xEnd, colIt,
|
||||
cvrt.second.indep,
|
||||
cvrt.second.column);
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
// No valid nodes. Return invalid.
|
||||
return StI(Extrap{});
|
||||
}
|
||||
});
|
||||
|
||||
for (auto t = 0*raw.numTables;
|
||||
t < raw.numTables; ++t)
|
||||
{
|
||||
auto key = extractPrimaryKey(raw, t, cvrt.first);
|
||||
|
||||
const auto begin = (t + 0)*raw.numPrimary;
|
||||
const auto end = begin + key.size();
|
||||
|
||||
ret.emplace_back(new LiveOil(std::move(key),
|
||||
{
|
||||
std::make_move_iterator(std::begin(sti) + begin),
|
||||
std::make_move_iterator(std::begin(sti) + end)
|
||||
}));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxOBase>>
|
||||
createPVTFunction(const ::Opm::ECLPropTableRawData& raw,
|
||||
const int usys)
|
||||
{
|
||||
if (raw.numPrimary == 0) {
|
||||
// Malformed Gas PVT table.
|
||||
throw std::invalid_argument {
|
||||
"Oil PVT Table Without Primary Lookup Key"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.numCols != 5) {
|
||||
throw std::invalid_argument {
|
||||
"PVT Table for Oil Must Have Five Columns"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.primaryKey.size() != (raw.numPrimary * raw.numTables)) {
|
||||
throw std::invalid_argument {
|
||||
"Size Mismatch in RS Nodes of PVT Table for Oil"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.data.size() !=
|
||||
(raw.numPrimary * raw.numRows * raw.numCols * raw.numTables))
|
||||
{
|
||||
throw std::invalid_argument {
|
||||
"Size Mismatch in Condensed Table Data "
|
||||
"of PVT Table for Oil"
|
||||
};
|
||||
}
|
||||
|
||||
if (raw.numPrimary == 1) {
|
||||
return createDeadOil(raw, usys);
|
||||
}
|
||||
|
||||
return createLiveOil(raw, usys);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PVxOBase>>
|
||||
clone(const std::vector<std::unique_ptr<PVxOBase>>& src)
|
||||
{
|
||||
auto dest = std::vector<std::unique_ptr<PVxOBase>>{};
|
||||
dest.reserve(src.size());
|
||||
|
||||
for (const auto& p : src) {
|
||||
dest.push_back(p->clone());
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
// #####################################################################
|
||||
// #####################################################################
|
||||
|
||||
// =====================================================================
|
||||
// Class ECLPVT::Oil::Impl
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
class Opm::ECLPVT::Oil::Impl
|
||||
{
|
||||
private:
|
||||
using EvalPtr = std::unique_ptr<PVxOBase>;
|
||||
|
||||
public:
|
||||
Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS);
|
||||
|
||||
Impl(const Impl& rhs);
|
||||
Impl(Impl&& rhs);
|
||||
|
||||
Impl& operator=(const Impl& rhs);
|
||||
Impl& operator=(Impl&& rhs);
|
||||
|
||||
using RegIdx = std::vector<EvalPtr>::size_type;
|
||||
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const RegIdx region,
|
||||
const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const;
|
||||
|
||||
std::vector<double>
|
||||
viscosity(const RegIdx region,
|
||||
const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const;
|
||||
|
||||
double surfaceMassDensity(const RegIdx region) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->rhoS_[region];
|
||||
}
|
||||
|
||||
FlowDiagnostics::Graph
|
||||
getPvtCurve(const RegIdx region,
|
||||
const RawCurve curve) const;
|
||||
|
||||
private:
|
||||
std::vector<EvalPtr> eval_;
|
||||
std::vector<double> rhoS_;
|
||||
|
||||
void validateRegIdx(const RegIdx region) const;
|
||||
};
|
||||
|
||||
Opm::ECLPVT::Oil::Impl::
|
||||
Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS)
|
||||
: eval_(createPVTFunction(raw, usys))
|
||||
, rhoS_(std::move(rhoS))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::Impl::Impl(const Impl& rhs)
|
||||
: eval_(clone(rhs.eval_))
|
||||
, rhoS_(rhs.rhoS_)
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::Impl::Impl(Impl&& rhs)
|
||||
: eval_(std::move(rhs.eval_))
|
||||
, rhoS_(std::move(rhs.rhoS_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::Impl&
|
||||
Opm::ECLPVT::Oil::Impl::operator=(const Impl& rhs)
|
||||
{
|
||||
this->eval_ = clone(rhs.eval_);
|
||||
this->rhoS_ = rhs.rhoS_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Opm::ECLPVT::Oil::Impl&
|
||||
Opm::ECLPVT::Oil::Impl::operator=(Impl&& rhs)
|
||||
{
|
||||
this->eval_ = std::move(rhs.eval_);
|
||||
this->rhoS_ = std::move(rhs.rhoS_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Oil::Impl::
|
||||
formationVolumeFactor(const RegIdx region,
|
||||
const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->formationVolumeFactor(rs, po);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Oil::Impl::
|
||||
viscosity(const RegIdx region,
|
||||
const std::vector<double>& rs,
|
||||
const std::vector<double>& po) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->viscosity(rs, po);
|
||||
}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::Oil::Impl::
|
||||
getPvtCurve(const RegIdx region,
|
||||
const RawCurve curve) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region]->getPvtCurve(curve);
|
||||
}
|
||||
|
||||
void
|
||||
Opm::ECLPVT::Oil::Impl::validateRegIdx(const RegIdx region) const
|
||||
{
|
||||
if (region >= this->eval_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Region Index " +
|
||||
std::to_string(region) +
|
||||
" Outside Valid Range (0 .. " +
|
||||
std::to_string(this->eval_.size() - 1) + ')'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Class ECLPVT::Oil
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
Opm::ECLPVT::Oil::Oil(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS)
|
||||
: pImpl_(new Impl(raw, usys, std::move(rhoS)))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::~Oil()
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::Oil(const Oil& rhs)
|
||||
: pImpl_(new Impl(*rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil::Oil(Oil&& rhs)
|
||||
: pImpl_(std::move(rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Oil&
|
||||
Opm::ECLPVT::Oil::operator=(const Oil& rhs)
|
||||
{
|
||||
this->pImpl_.reset(new Impl(*rhs.pImpl_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Opm::ECLPVT::Oil&
|
||||
Opm::ECLPVT::Oil::operator=(Oil&& rhs)
|
||||
{
|
||||
this->pImpl_ = std::move(rhs.pImpl_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Oil::
|
||||
formationVolumeFactor(const int region,
|
||||
const DissolvedGas& rs,
|
||||
const OilPressure& po) const
|
||||
{
|
||||
return this->pImpl_->formationVolumeFactor(region, rs.data, po.data);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Oil::
|
||||
viscosity(const int region,
|
||||
const DissolvedGas& rs,
|
||||
const OilPressure& po) const
|
||||
{
|
||||
return this->pImpl_->viscosity(region, rs.data, po.data);
|
||||
}
|
||||
|
||||
double Opm::ECLPVT::Oil::surfaceMassDensity(const int region) const
|
||||
{
|
||||
return this->pImpl_->surfaceMassDensity(region);
|
||||
}
|
||||
|
||||
Opm::FlowDiagnostics::Graph
|
||||
Opm::ECLPVT::Oil::
|
||||
getPvtCurve(const RawCurve curve, const int region) const
|
||||
{
|
||||
return this->pImpl_->getPvtCurve(region, curve);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
std::unique_ptr<Opm::ECLPVT::Oil>
|
||||
Opm::ECLPVT::CreateOilPVTInterpolant::
|
||||
fromECLOutput(const ECLInitFileData& init)
|
||||
{
|
||||
using OPtr = std::unique_ptr<Opm::ECLPVT::Oil>;
|
||||
|
||||
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
|
||||
const auto iphs = static_cast<unsigned int>(ih[INTEHEAD_PHASE_INDEX]);
|
||||
|
||||
if ((iphs & (1u << 0)) == 0) {
|
||||
// Oil is not an active phase (unexpected).
|
||||
// Return sentinel (null) pointer.
|
||||
return OPtr{};
|
||||
}
|
||||
|
||||
auto raw = ::Opm::ECLPropTableRawData{};
|
||||
|
||||
const auto& tabdims = init.keywordData<int>("TABDIMS");
|
||||
const auto& tab = init.keywordData<double>("TAB");
|
||||
|
||||
raw.numPrimary = tabdims[ TABDIMS_NRPVTO_ITEM ]; // #Rs nodes/full table
|
||||
raw.numRows = tabdims[ TABDIMS_NPPVTO_ITEM ]; // #Po nodes/sub-table
|
||||
raw.numCols = 5; // [ Po, 1/B, 1/(B*mu), d(1/B)/dPo, d(1/(B*mu))/dPo ]
|
||||
raw.numTables = tabdims[ TABDIMS_NTPVTO_ITEM ]; // # PVTO tables
|
||||
|
||||
// Extract Primary Key (Rs)
|
||||
{
|
||||
const auto nTabElem = raw.numPrimary * raw.numTables;
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_JBPVTO_OFFSET_ITEM ] - 1;
|
||||
|
||||
raw.primaryKey.assign(&tab[start], &tab[start] + nTabElem);
|
||||
}
|
||||
|
||||
// Extract Full Table
|
||||
{
|
||||
const auto nTabElem =
|
||||
raw.numPrimary * raw.numRows * raw.numCols * raw.numTables;
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1;
|
||||
|
||||
raw.data.assign(&tab[start], &tab[start] + nTabElem);
|
||||
}
|
||||
|
||||
auto rhoS = surfaceMassDensity(init, ECLPhaseIndex::Liquid);
|
||||
|
||||
return OPtr{
|
||||
new Oil(raw, ih[ INTEHEAD_UNIT_INDEX ], std::move(rhoS))
|
||||
};
|
||||
}
|
197
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtOil.hpp
vendored
Normal file
197
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtOil.hpp
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
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_ECLPVTOIL_HEADER_INCLUDED
|
||||
#define OPM_ECLPVTOIL_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace Opm {
|
||||
struct ECLPropTableRawData;
|
||||
class ECLInitFileData;
|
||||
} // Opm
|
||||
|
||||
namespace Opm { namespace ECLPVT {
|
||||
|
||||
/// Interpolant for Basic Oil PVT Relations.
|
||||
class Oil
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] raw Raw tabulated data. Must correspond to the PVTO
|
||||
/// vector of an ECL INIT file..
|
||||
///
|
||||
/// \param[in] usys Unit system convention of the result set from
|
||||
/// which \p raw was extracted. Must correspond to item 3 of the
|
||||
/// INTEHEAD keyword in the INIT file.
|
||||
///
|
||||
/// \param[in] rhoS Mass density of oil at surface conditions.
|
||||
/// Typically computed by \code ECLPVT::surfaceMassDensity()
|
||||
/// \endcode.
|
||||
Oil(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
std::vector<double> rhoS);
|
||||
|
||||
/// Destructor.
|
||||
~Oil();
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing interpolant for Oil PVT relations.
|
||||
Oil(const Oil& rhs);
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing Oil PVT relation
|
||||
/// interpolant.
|
||||
///
|
||||
/// \param[in] rhs Existing Oil PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
Oil(Oil&& rhs);
|
||||
|
||||
/// Assignment operator
|
||||
///
|
||||
/// \param[in] rhs Existing Oil PVT relation interpolant.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Oil& operator=(const Oil& rhs);
|
||||
|
||||
/// Move assignment operator.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing Oil PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Oil& operator=(Oil&& rhs);
|
||||
|
||||
/// Representation of the Dissolved Gas-Oil Ratio (Rs).
|
||||
///
|
||||
/// Mostly as a convenience to disambiguate the client interface.
|
||||
struct DissolvedGas {
|
||||
/// Dissolved gas-oil ratio data for all sampling points.
|
||||
std::vector<double> data;
|
||||
};
|
||||
|
||||
/// Representation of Oil Phase Pressure (Po).
|
||||
///
|
||||
/// Mostly as a convenience to disambiguate the client interface.
|
||||
struct OilPressure {
|
||||
/// Oil phase pressure for all sampling points.
|
||||
std::vector<double> data;
|
||||
};
|
||||
|
||||
/// Compute the oil phase formation volume factor in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] rs Dissolved gas-oil ratio. Unused in the case of a
|
||||
/// dead oil model. Size must match number of elements in \code
|
||||
/// po.data \endcode in the case of live oil model. Strict SI
|
||||
/// units of measurement.
|
||||
///
|
||||
/// \param[in] po Oil phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Oil phase formation volume factor. Size equal to number
|
||||
/// of elements in \code po.data \endcode.
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const int region,
|
||||
const DissolvedGas& rs,
|
||||
const OilPressure& po) const;
|
||||
|
||||
/// Compute the oil phase fluid viscosity in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] rs Dissolved gas-oil ratio. Unused in the case of a
|
||||
/// dead oil model. Size must match number of elements in \code
|
||||
/// po.data \endcode in the case of live oil model. Strict SI
|
||||
/// units of measurement.
|
||||
///
|
||||
/// \param[in] po Oil phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Oil phase fluid viscosity. Size equal to number of
|
||||
/// elements in \code po.data \endcode.
|
||||
std::vector<double>
|
||||
viscosity(const int region,
|
||||
const DissolvedGas& rs,
|
||||
const OilPressure& po) const;
|
||||
|
||||
/// Retrieve constant mass density of oil at surface conditions.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \return Mass density of oil at surface in particular model
|
||||
/// region.
|
||||
double surfaceMassDensity(const int region) const;
|
||||
|
||||
/// Retrieve 2D graph representation of Oil PVT property function in
|
||||
/// partcular PVT region.
|
||||
///
|
||||
/// \param[in] curve PVT property curve descriptor
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \return 2D graph for PVT property curve identified by
|
||||
/// requests represented by \p func and \p region.
|
||||
///
|
||||
/// Example: Retrieve oil viscosity curve in PVT region 3 (zero
|
||||
/// based, i.e., those cells for which PVTNUM==4).
|
||||
///
|
||||
/// \code
|
||||
/// const auto graph =
|
||||
/// pvtOil.getPvtCurve(ECLPVT::RawCurve::Viscosity, 3);
|
||||
/// \endcode
|
||||
FlowDiagnostics::Graph
|
||||
getPvtCurve(const RawCurve curve, const int region) const;
|
||||
|
||||
private:
|
||||
/// Implementation class.
|
||||
class Impl;
|
||||
|
||||
/// Pointer to implementation.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
/// Basic Oil PVT Relation Interpolant Factory Functions.
|
||||
struct CreateOilPVTInterpolant
|
||||
{
|
||||
/// Create Oil PVT interpolant directly from an ECL result set
|
||||
/// (i.e., from the tabulated functions in the 'TAB' vector.
|
||||
///
|
||||
/// \param[in] init ECL result set INIT file representation.
|
||||
///
|
||||
/// \return Oil PVT interpolant. Nullpointer if oil is not an
|
||||
/// active phase in the result set (unexpected).
|
||||
static std::unique_ptr<Oil>
|
||||
fromECLOutput(const ECLInitFileData& init);
|
||||
};
|
||||
}} // Opm::ECLPVT
|
||||
|
||||
#endif // OPM_ECLPVTOIL_HEADER_INCLUDED
|
364
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtWater.cpp
vendored
Normal file
364
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtWater.cpp
vendored
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
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/ECLPvtWater.hpp>
|
||||
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
#include <opm/utility/ECLPropTable.hpp>
|
||||
#include <opm/utility/ECLPvtCommon.hpp>
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
|
||||
namespace {
|
||||
::Opm::ECLPVT::ConvertUnits waterUnitConverter(const int usys)
|
||||
{
|
||||
using ToSI = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
const auto u = ::Opm::ECLUnits::createUnitSystem(usys);
|
||||
|
||||
// [ Pref, 1/Bw, Cw, 1/(Bw*mu_w), Cw - Cv ]
|
||||
|
||||
return ::Opm::ECLPVT::ConvertUnits {
|
||||
ToSI::pressure(*u),
|
||||
{
|
||||
ToSI::recipFvf(*u),
|
||||
ToSI::compressibility(*u),
|
||||
ToSI::recipFvfVisc(*u),
|
||||
ToSI::compressibility(*u)
|
||||
}
|
||||
};
|
||||
}
|
||||
} // Anonymous
|
||||
|
||||
class PVTCurves
|
||||
{
|
||||
public:
|
||||
using ElemIt = std::vector<double>::const_iterator;
|
||||
using ConvertUnits = ::Opm::ECLPVT::ConvertUnits;
|
||||
|
||||
PVTCurves(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt);
|
||||
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const std::vector<double>& pw) const
|
||||
{
|
||||
return this->evaluate(pw, [this](const double p) -> double
|
||||
{
|
||||
// 1 / (1 / B)
|
||||
return 1.0 / this->recipFvf(p);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
viscosity(const std::vector<double>& pw) const
|
||||
{
|
||||
return this->evaluate(pw, [this](const double p) -> double
|
||||
{
|
||||
// (1 / B) / (1 / (B * mu))
|
||||
return this->recipFvf(p) / this->recipFvfVisc(p);
|
||||
});
|
||||
}
|
||||
|
||||
double& surfaceMassDensity()
|
||||
{
|
||||
return this->rhoS_;
|
||||
}
|
||||
|
||||
double surfaceMassDensity() const
|
||||
{
|
||||
return this->rhoS_;
|
||||
}
|
||||
|
||||
private:
|
||||
double pw_ref_ { 1.0 };
|
||||
double recipFvf_ { 1.0 }; // 1 / B
|
||||
double recipFvfVisc_ { 1.0 }; // 1 / (B*mu)
|
||||
double Cw_ { 1.0 };
|
||||
double diffCwCv_ { 0.0 }; // Cw - Cv
|
||||
double rhoS_ { 0.0 };
|
||||
|
||||
double recipFvf(const double pw) const
|
||||
{
|
||||
const auto x = this->Cw_ * (pw - this->pw_ref_);
|
||||
|
||||
return this->recipFvf_ * this->exp(x);
|
||||
}
|
||||
|
||||
double recipFvfVisc(const double pw) const
|
||||
{
|
||||
const auto y = this->diffCwCv_ * (pw - this->pw_ref_);
|
||||
|
||||
return this->recipFvfVisc_ * this->exp(y);
|
||||
}
|
||||
|
||||
double exp(const double x) const
|
||||
{
|
||||
return 1.0 + x*(1.0 + x/2.0);
|
||||
}
|
||||
|
||||
template <class CalcQuant>
|
||||
std::vector<double>
|
||||
evaluate(const std::vector<double>& pw,
|
||||
CalcQuant&& calculate) const
|
||||
{
|
||||
auto q = std::vector<double>{};
|
||||
q.reserve(pw.size());
|
||||
|
||||
for (const auto& pwi : pw) {
|
||||
q.push_back(calculate(pwi));
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
};
|
||||
|
||||
PVTCurves::PVTCurves(ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
const ConvertUnits& convert,
|
||||
std::vector<ElemIt>& colIt)
|
||||
{
|
||||
assert ((std::distance(xBegin, xEnd) == 1) &&
|
||||
"Logic Error in Defining PVTW Input Ranges");
|
||||
|
||||
#ifdef NDEBUG
|
||||
// Suppress "unusued variable" in release mode.
|
||||
static_cast<void>(xEnd);
|
||||
#endif // NDEBUG
|
||||
|
||||
// Recall: Table is
|
||||
//
|
||||
// [ Pw, 1/Bw, Cw, 1/(Bw*mu_w), Cw - Cv ]
|
||||
//
|
||||
// xBegin is Pw, colIt is remaining four columns.
|
||||
|
||||
this->recipFvf_ = convert.column[0](*colIt[0]); // 1/Bw
|
||||
this->Cw_ = convert.column[1](*colIt[1]); // Cw
|
||||
this->recipFvfVisc_ = convert.column[2](*colIt[2]); // 1/(Bw*mu_w)
|
||||
this->diffCwCv_ = convert.column[3](*colIt[3]); // Cw - Cv
|
||||
|
||||
// Honour requirement that constructor advances column iterators.
|
||||
for (auto& it : colIt) { ++it; }
|
||||
|
||||
if (! (std::abs(*xBegin) < 1.0e20)) {
|
||||
throw std::invalid_argument {
|
||||
"Invalid Input PVTW Table"
|
||||
};
|
||||
}
|
||||
|
||||
this->pw_ref_ = convert.indep(*xBegin);
|
||||
}
|
||||
|
||||
// #####################################################################
|
||||
// #####################################################################
|
||||
|
||||
// =====================================================================
|
||||
// Class ECLPVT::Water::Impl
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
class Opm::ECLPVT::Water::Impl
|
||||
{
|
||||
public:
|
||||
Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
const std::vector<double>& rhoS);
|
||||
|
||||
using RegIdx = std::vector<PVTCurves>::size_type;
|
||||
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const RegIdx region,
|
||||
const std::vector<double>& pw) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region].formationVolumeFactor(pw);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
viscosity(const RegIdx region,
|
||||
const std::vector<double>& pw) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region].viscosity(pw);
|
||||
}
|
||||
|
||||
double surfaceMassDensity(const RegIdx region) const
|
||||
{
|
||||
this->validateRegIdx(region);
|
||||
|
||||
return this->eval_[region].surfaceMassDensity();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PVTCurves> eval_;
|
||||
|
||||
void validateRegIdx(const RegIdx region) const;
|
||||
};
|
||||
|
||||
Opm::ECLPVT::Water::Impl::Impl(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
const std::vector<double>& rhoS)
|
||||
{
|
||||
using ElemIt = PVTCurves::ElemIt;
|
||||
|
||||
const auto cvrt = waterUnitConverter(usys);
|
||||
|
||||
this->eval_ = MakeInterpolants<PVTCurves>::fromRawData(raw,
|
||||
[&cvrt](ElemIt xBegin,
|
||||
ElemIt xEnd,
|
||||
std::vector<ElemIt>& colIt) -> PVTCurves
|
||||
{
|
||||
return PVTCurves(xBegin, xEnd, cvrt, colIt);
|
||||
});
|
||||
|
||||
assert (rhoS.size() == this->eval_.size());
|
||||
|
||||
for (auto n = this->eval_.size(), i = 0*n; i < n; ++i) {
|
||||
this->eval_[i].surfaceMassDensity() = rhoS[i];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Opm::ECLPVT::Water::Impl::validateRegIdx(const RegIdx region) const
|
||||
{
|
||||
if (region >= this->eval_.size()) {
|
||||
throw std::invalid_argument {
|
||||
"Region Index " +
|
||||
std::to_string(region) +
|
||||
" Outside Valid Range (0 .. " +
|
||||
std::to_string(this->eval_.size() - 1) + ')'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Class ECLPVT::Water
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
Opm::ECLPVT::Water::Water(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
const std::vector<double>& rhoS)
|
||||
: pImpl_(new Impl(raw, usys, rhoS))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Water::~Water()
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Water::Water(const Water& rhs)
|
||||
: pImpl_(new Impl(*rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Water::Water(Water&& rhs)
|
||||
: pImpl_(std::move(rhs.pImpl_))
|
||||
{}
|
||||
|
||||
Opm::ECLPVT::Water&
|
||||
Opm::ECLPVT::Water::operator=(const Water& rhs)
|
||||
{
|
||||
this->pImpl_.reset(new Impl(*rhs.pImpl_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Opm::ECLPVT::Water&
|
||||
Opm::ECLPVT::Water::operator=(Water&& rhs)
|
||||
{
|
||||
this->pImpl_ = std::move(rhs.pImpl_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Water::formationVolumeFactor(const int region,
|
||||
const WaterPressure& pw) const
|
||||
{
|
||||
return this->pImpl_->formationVolumeFactor(region, pw.data);
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
Opm::ECLPVT::Water::viscosity(const int region,
|
||||
const WaterPressure& pw) const
|
||||
{
|
||||
return this->pImpl_->viscosity(region, pw.data);
|
||||
}
|
||||
|
||||
double
|
||||
Opm::ECLPVT::Water::surfaceMassDensity(const int region) const
|
||||
{
|
||||
return this->pImpl_->surfaceMassDensity(region);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
std::unique_ptr<Opm::ECLPVT::Water>
|
||||
Opm::ECLPVT::CreateWaterPVTInterpolant::
|
||||
fromECLOutput(const ECLInitFileData& init)
|
||||
{
|
||||
using WPtr = ::std::unique_ptr<Opm::ECLPVT::Water>;
|
||||
|
||||
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
|
||||
const auto iphs = static_cast<unsigned int>(ih[INTEHEAD_PHASE_INDEX]);
|
||||
|
||||
if ((iphs & (1u << 1)) == 0) {
|
||||
// Water is not an active phase.
|
||||
// Return sentinel (null) pointer.
|
||||
return WPtr{};
|
||||
}
|
||||
|
||||
auto raw = ::Opm::ECLPropTableRawData{};
|
||||
|
||||
const auto& tabdims = init.keywordData<int>("TABDIMS");
|
||||
const auto& tab = init.keywordData<double>("TAB");
|
||||
|
||||
raw.numPrimary = 1; // Single record per region
|
||||
raw.numRows = 1; // Single record per region
|
||||
raw.numCols = 5; // [ Pw, 1/B, Cw, 1/(B*mu), Cw - Cv ]
|
||||
raw.numTables = tabdims[ TABDIMS_NTPVTW_ITEM ]; // # PVTW tables
|
||||
|
||||
// Extract Full Table
|
||||
{
|
||||
const auto nTabElem =
|
||||
raw.numPrimary * raw.numRows * raw.numCols * raw.numTables;
|
||||
|
||||
// Subtract one to account for 1-based indices.
|
||||
const auto start = tabdims[ TABDIMS_IBPVTW_OFFSET_ITEM ] - 1;
|
||||
|
||||
raw.data.assign(&tab[start], &tab[start] + nTabElem);
|
||||
}
|
||||
|
||||
const auto rhoS = surfaceMassDensity(init, ECLPhaseIndex::Aqua);
|
||||
|
||||
return WPtr{
|
||||
new Water(raw, ih[ INTEHEAD_UNIT_INDEX ], rhoS)
|
||||
};
|
||||
}
|
153
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtWater.hpp
vendored
Normal file
153
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLPvtWater.hpp
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
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_ECLPVTWATER_HEADER_INCLUDED
|
||||
#define OPM_ECLPVTWATER_HEADER_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
struct ECLPropTableRawData;
|
||||
class ECLInitFileData;
|
||||
} // Opm
|
||||
|
||||
namespace Opm { namespace ECLPVT {
|
||||
|
||||
/// Interpolant for Basic Water PVT Relations.
|
||||
class Water
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] raw Raw tabulated data. Must correspond to the PVTW
|
||||
/// vector of an ECL INIT file..
|
||||
///
|
||||
/// \param[in] usys Unit system convention of the result set from
|
||||
/// which \p raw was extracted. Must correspond to item 3 of the
|
||||
/// INTEHEAD keyword in the INIT file.
|
||||
///
|
||||
/// \param[in] rhoS Mass density of water at surface conditions.
|
||||
/// Typically computed by \code ECLPVT::surfaceMassDensity()
|
||||
/// \endcode.
|
||||
Water(const ECLPropTableRawData& raw,
|
||||
const int usys,
|
||||
const std::vector<double>& rhoS);
|
||||
|
||||
/// Destructor.
|
||||
~Water();
|
||||
|
||||
/// Copy constructor.
|
||||
///
|
||||
/// \param[in] rhs Existing interpolant for Water PVT relations.
|
||||
Water(const Water& rhs);
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// Subsumes the implementation of an existing Water PVT relation
|
||||
/// interpolant.
|
||||
///
|
||||
/// \param[in] rhs Existing Water PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
Water(Water&& rhs);
|
||||
|
||||
/// Assignment operator
|
||||
///
|
||||
/// \param[in] rhs Existing Oil Water relation interpolant.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Water& operator=(const Water& rhs);
|
||||
|
||||
/// Move assignment operator.
|
||||
///
|
||||
/// Subsumes the implementation of an existing object.
|
||||
///
|
||||
/// \param[in] rhs Existing Water PVT relation interpolant. Does not
|
||||
/// have a valid implementation when the constructor completes.
|
||||
///
|
||||
/// \return \code *this \endcode.
|
||||
Water& operator=(Water&& rhs);
|
||||
|
||||
/// Representation of Water Phase Pressure (Pw).
|
||||
///
|
||||
/// Mostly as a convenience to disambiguate the client interface.
|
||||
struct WaterPressure {
|
||||
/// Water phase pressure for all sampling points.
|
||||
std::vector<double> data;
|
||||
};
|
||||
|
||||
/// Compute the oil phase formation volume factor in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] pw Water phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Oil phase formation volume factor. Size equal to number
|
||||
/// of elements in \code pw.data \endcode.
|
||||
std::vector<double>
|
||||
formationVolumeFactor(const int region,
|
||||
const WaterPressure& pw) const;
|
||||
|
||||
/// Compute the water phase fluid viscosity in a single region.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \param[in] pw Water phase pressure. Strict SI units of measurement.
|
||||
///
|
||||
/// \return Oil phase fluid viscosity. Size equal to number of
|
||||
/// elements in \code pw.data \endcode.
|
||||
std::vector<double>
|
||||
viscosity(const int region,
|
||||
const WaterPressure& pw) const;
|
||||
|
||||
/// Retrieve constant mass density of water at surface conditions.
|
||||
///
|
||||
/// \param[in] region Region ID. Non-negative integer typically
|
||||
/// derived from the PVTNUM mapping vector.
|
||||
///
|
||||
/// \return Mass density of water at surface in particular model
|
||||
/// region.
|
||||
double surfaceMassDensity(const int region) const;
|
||||
|
||||
private:
|
||||
/// Implementation class.
|
||||
class Impl;
|
||||
|
||||
/// Pointer to implementation.
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
/// Basic Oil PVT Relation Interpolant Factory Functions.
|
||||
struct CreateWaterPVTInterpolant
|
||||
{
|
||||
/// Create Oil PVT interpolant directly from an ECL result set
|
||||
/// (i.e., from the tabulated functions in the 'TAB' vector.
|
||||
///
|
||||
/// \param[in] init ECL result set INIT file representation.
|
||||
///
|
||||
/// \return Oil PVT interpolant. Nullpointer if water is not an
|
||||
/// active phase in the result set.
|
||||
static std::unique_ptr<Water>
|
||||
fromECLOutput(const ECLInitFileData& init);
|
||||
};
|
||||
}} // Opm::ECLPVT
|
||||
|
||||
#endif // OPM_ECLPVTWATER_HEADER_INCLUDED
|
143
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLRegionMapping.cpp
vendored
Normal file
143
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLRegionMapping.cpp
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
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/ECLRegionMapping.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<int>
|
||||
makeRegionSubset(const std::vector<int>::size_type n,
|
||||
std::vector<int> regSubset)
|
||||
{
|
||||
auto ret = std::move(regSubset);
|
||||
|
||||
if (ret.empty()) {
|
||||
ret.resize(n);
|
||||
|
||||
std::iota(std::begin(ret), std::end(ret), 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // Anonymous
|
||||
|
||||
Opm::ECLRegionMapping::
|
||||
ECLRegionMapping(const std::vector<int>& region,
|
||||
const std::vector<int>& regSubset)
|
||||
: regSubset_(makeRegionSubset(region.size(), regSubset))
|
||||
{
|
||||
{
|
||||
auto i = 0;
|
||||
for (const auto& ix : this->regSubset_) {
|
||||
this->regionSubsetIndex_
|
||||
.addConnection(this->activeID(region[ix]), i++);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->next_ == this->start_) {
|
||||
// No active region IDs. Typically empty 'region' or 'regSubset'.
|
||||
// Invalid nonetheless so we can't continue here.
|
||||
throw std::invalid_argument {
|
||||
"Requested Region/Index Vector Pair "
|
||||
"Does not Form Valid Subset"
|
||||
};
|
||||
}
|
||||
|
||||
// Recall: next_ represents the dense, linear ID that would be assigned
|
||||
// to the "next" unseen region ID in a "start_"-based index sequence.
|
||||
//
|
||||
// Therefore next_ - start_ is the number of linear IDs assigned during
|
||||
// this construction process which is also the number of rows in the
|
||||
// sparse mapping matrix.
|
||||
this->regionSubsetIndex_.compress(this->next_ - this->start_);
|
||||
}
|
||||
|
||||
const std::vector<int>&
|
||||
Opm::ECLRegionMapping::regionSubset() const
|
||||
{
|
||||
return this->regSubset_;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
Opm::ECLRegionMapping::activeRegions() const
|
||||
{
|
||||
auto areg = std::vector<int>{};
|
||||
areg.reserve(activeID_.size());
|
||||
|
||||
for (const auto& reg : this->activeID_) {
|
||||
areg.push_back(reg.first);
|
||||
}
|
||||
|
||||
std::sort(std::begin(areg), std::end(areg));
|
||||
|
||||
return areg;
|
||||
}
|
||||
|
||||
Opm::ECLRegionMapping::IndexView
|
||||
Opm::ECLRegionMapping::getRegionIndices(const int region) const
|
||||
{
|
||||
const auto areg = this->activeID(region);
|
||||
|
||||
if (areg < 0) {
|
||||
throw std::logic_error {
|
||||
"Region ID not in configured subset"
|
||||
};
|
||||
}
|
||||
|
||||
const auto& start = this->regionSubsetIndex_.startPointers();
|
||||
const auto& ix = this->regionSubsetIndex_.neighbourhood();
|
||||
|
||||
auto begin = std::begin(ix) + start[areg + 0];
|
||||
auto end = std::begin(ix) + start[areg + 1];
|
||||
|
||||
return { begin, end };
|
||||
}
|
||||
|
||||
int Opm::ECLRegionMapping::activeID(const int regID)
|
||||
{
|
||||
auto& areg = this->activeID_[regID];
|
||||
|
||||
if (areg == 0) {
|
||||
// Region ID 'regID' not previously seen. Assign new active ID.
|
||||
areg = this->next_++;
|
||||
}
|
||||
|
||||
return areg - this->start_;
|
||||
}
|
||||
|
||||
int Opm::ECLRegionMapping::activeID(const int regID) const
|
||||
{
|
||||
auto i = this->activeID_.find(regID);
|
||||
|
||||
if (i == std::end(this->activeID_)) {
|
||||
// Region ID 'regID' not in configured subset of ID set defined on
|
||||
// active cells.
|
||||
return -1;
|
||||
}
|
||||
|
||||
return i->second - this->start_;
|
||||
}
|
130
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLRegionMapping.hpp
vendored
Normal file
130
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLRegionMapping.hpp
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
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_ECLREGIONMAPPING_HEADER_INCLUDED
|
||||
#define OPM_ECLREGIONMAPPING_HEADER_INCLUDED
|
||||
|
||||
#include <opm/utility/graph/AssembledConnections.hpp>
|
||||
#include <opm/utility/graph/AssembledConnectionsIteration.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
/// Mapping of region IDs to subsets of explicit cell ID collection.
|
||||
///
|
||||
/// The typical client code is
|
||||
/// \code
|
||||
/// const auto map = ECLRegionMapping(/* ... */);
|
||||
///
|
||||
/// /// ...
|
||||
///
|
||||
/// const auto& subset = map.regionSubset();
|
||||
///
|
||||
/// for (const auto& regID : map.activeRegions()) {
|
||||
/// for (const auto& ix : map.getRegionIndices(regID)) {
|
||||
/// use(regID, subset[ix]);
|
||||
/// }
|
||||
/// }
|
||||
/// \endcode
|
||||
class ECLRegionMapping
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param[in] region Container of region IDs. Typically one of the
|
||||
/// explicit region ID vectors in an ECL result set such as
|
||||
/// SATNUM or PVTNUM. Assumed to be defined on all active cells
|
||||
/// of an ECL result set.
|
||||
///
|
||||
/// \param[in] regSubset Index subset of region IDs. This object
|
||||
/// instance partitions the linear indices in
|
||||
/// \code
|
||||
/// [ 0 .. regSubset.size()-1 ]
|
||||
/// \endcode
|
||||
/// according to the corresponding unique region ID in the set
|
||||
/// \code
|
||||
/// { region[ i ] }_{i \in regSubset}
|
||||
/// \endcode
|
||||
///
|
||||
/// If empty or defaulted, \p regSubset is treated as if it were
|
||||
/// specified as \code [ 0 .. region.size() - 1] \endcode. The
|
||||
/// common use case for this is working on the entire set of
|
||||
/// active cells implied by the region ID vector.
|
||||
///
|
||||
/// The typical use case of an explicit region subset is when
|
||||
/// sampling PVT or saturation function curves for graphical
|
||||
/// representation.
|
||||
ECLRegionMapping(const std::vector<int>& region,
|
||||
const std::vector<int>& regSubset
|
||||
= std::vector<int>());
|
||||
|
||||
/// Retrieve index subset.
|
||||
///
|
||||
/// Identical to constructor argument \c regSubset if supplied,
|
||||
/// otherwise the vector \code [ 0 .. region.size()-1 ] \endcode.
|
||||
const std::vector<int>& regionSubset() const;
|
||||
|
||||
/// Retrieve sorted list of unique region IDs in the subset defined
|
||||
/// by \code { region[i] }_{i \in regionSubset()} \endcode.
|
||||
std::vector<int> activeRegions() const;
|
||||
|
||||
/// Convenience alias to simplify declaring return type of member
|
||||
/// function \code getRegionIndices() \endcode.
|
||||
using IndexView = SimpleIteratorRange<
|
||||
AssembledConnections::Neighbours::const_iterator>;
|
||||
|
||||
/// Retrive linear indices into \code regionSubset() \endcode that
|
||||
/// correspond to particular region ID.
|
||||
///
|
||||
/// \param[in] region Numeric region ID. Must be one of the unique
|
||||
/// region IDs implied by \code activeRegions() \endcode. If it
|
||||
/// is not one of those IDs, function \code getRegionIndices()
|
||||
/// \endcode throws an instance of \code std::logic_error
|
||||
/// \endcode.
|
||||
///
|
||||
/// \return Linear index view into \code regionSubset() \endcode.
|
||||
IndexView getRegionIndices(const int region) const;
|
||||
|
||||
private:
|
||||
/// Offset from which to start assigning linear, dense active IDs.
|
||||
int start_{1};
|
||||
|
||||
/// Next, unassigned linear ID.
|
||||
int next_{1};
|
||||
|
||||
/// Subset of full region ID mapping.
|
||||
std::vector<int> regSubset_;
|
||||
|
||||
/// Sparse mapping of region IDs to dense active IDs.
|
||||
std::unordered_map<int, int> activeID_;
|
||||
|
||||
/// Map active IDs to subsets of the initial index subset.
|
||||
AssembledConnections regionSubsetIndex_;
|
||||
|
||||
/// Translate region ID to dense linear active ID. Mutable version.
|
||||
int activeID(const int regID);
|
||||
|
||||
/// Translate region ID to dense linear active ID. Immutable
|
||||
/// version.
|
||||
int activeID(const int regID) const;
|
||||
};
|
||||
} // Opm
|
||||
|
||||
#endif // OPM_ECLREGIONMAPPING_HEADER_INCLUDED
|
@ -48,13 +48,10 @@
|
||||
#include <ert/ecl/ecl_kw.h>
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
#include <ert/ecl/ecl_nnc_export.h>
|
||||
#include <ert/ecl/ecl_type.h>
|
||||
#include <ert/ecl/ecl_util.h>
|
||||
#include <ert/util/ert_unique_ptr.hpp>
|
||||
|
||||
#if defined(HAVE_ERT_ECL_TYPE_H) && HAVE_ERT_ECL_TYPE_H
|
||||
#include <ert/ecl/ecl_type.h>
|
||||
#endif // defined(HAVE_ERT_ECL_TYPE_H) && HAVE_ERT_ECL_TYPE_H
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// Implementation of ECL Result-Set Interface.
|
||||
@ -63,13 +60,7 @@ namespace {
|
||||
inline ecl_type_enum
|
||||
getKeywordElementType(const ecl_kw_type* kw)
|
||||
{
|
||||
#if defined(HAVE_ERT_ECL_TYPE_H) && HAVE_ERT_ECL_TYPE_H
|
||||
return ecl_type_get_type(ecl_kw_get_data_type(kw));
|
||||
|
||||
#else // ! (defined(HAVE_ERT_ECL_TYPE_H) && HAVE_ERT_ECL_TYPE_H)
|
||||
|
||||
return ecl_kw_get_type(kw);
|
||||
#endif // defined(HAVE_ERT_ECL_TYPE_H) && HAVE_ERT_ECL_TYPE_H
|
||||
}
|
||||
|
||||
namespace ECLImpl {
|
||||
@ -259,13 +250,8 @@ namespace {
|
||||
/// 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);
|
||||
x[i] = ecl_kw_iget_bool(kw, i);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1380,7 +1366,7 @@ namespace Opm {
|
||||
this->setActiveBlock(kwloc.sectID);
|
||||
|
||||
if (! gridName.empty()) {
|
||||
// We're cons
|
||||
// Local grid. Further restrict view to relevant LGR section.
|
||||
this->activeBlock_ =
|
||||
ecl_file_view_add_blockview(this->activeBlock_, LGR_KW,
|
||||
kwloc.gridSectID);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@
|
||||
#ifndef OPM_ECLSATURATIONFUNC_HEADER_INCLUDED
|
||||
#define OPM_ECLSATURATIONFUNC_HEADER_INCLUDED
|
||||
|
||||
#include <opm/flowdiagnostics/DerivedQuantities.hpp>
|
||||
#include <opm/utility/ECLPhaseIndex.hpp>
|
||||
|
||||
#include <memory>
|
||||
@ -43,6 +44,36 @@ namespace Opm {
|
||||
class ECLSaturationFunc
|
||||
{
|
||||
public:
|
||||
/// Protocol for describing a particular saturation function
|
||||
/// request.
|
||||
struct RawCurve
|
||||
{
|
||||
/// Which saturation function does this request reference.
|
||||
enum class Function {
|
||||
/// Relative permeability functions
|
||||
RelPerm,
|
||||
};
|
||||
|
||||
/// Which one-dimensional sub-system does this request reference.
|
||||
enum class SubSystem {
|
||||
/// Oil-Gas subsystem
|
||||
OilGas,
|
||||
|
||||
/// Oil-Water subsystem
|
||||
OilWater,
|
||||
};
|
||||
|
||||
/// Particular saturation function of this request.
|
||||
Function curve;
|
||||
|
||||
/// Particular sub-system of this request.
|
||||
SubSystem subsys;
|
||||
|
||||
/// Phase/component for which to form the effective saturation
|
||||
/// function curve.
|
||||
ECLPhaseIndex thisPh;
|
||||
};
|
||||
|
||||
/// Constructor
|
||||
///
|
||||
/// \param[in] G Connected topology of current model's active cells.
|
||||
@ -126,6 +157,70 @@ namespace Opm {
|
||||
const ECLRestartData& rstrt,
|
||||
const ECLPhaseIndex p) const;
|
||||
|
||||
/// Retrieve 2D graph representations of sequence of effective
|
||||
/// saturation functions in a single cell.
|
||||
///
|
||||
/// \param[in] func Sequence of saturation function descriptions.
|
||||
///
|
||||
/// \param[in] activeCell Index of active cell from which to derive
|
||||
/// the effective saturation function. Use member function \code
|
||||
/// ECLGraph::activeCell() \endcode to translate a global cell
|
||||
/// (I,J,K) tuple--relative to a model grid--to a linear active
|
||||
/// cell ID.
|
||||
///
|
||||
/// \param[in] useEPS Whether or not to include effects of
|
||||
/// saturation end-point scaling. No effect if the INIT result
|
||||
/// set from which the object was constructed 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.
|
||||
///
|
||||
/// \return Sequence of 2D graphs for all saturation function
|
||||
/// requests represented by \p func. In particular, the \c i-th
|
||||
/// element of the result corresponds to input request \code
|
||||
/// func[i] \endcode. Abscissas are stored in \code
|
||||
/// graph[i].first \endcode and ordinates are stored in \code
|
||||
/// graph[i].second \endcode. If a particular request is
|
||||
/// semantically invalid, such as when requesting the water
|
||||
/// relative permeability in the oil-gas system, then the
|
||||
/// corresponding graph in the result is empty.
|
||||
///
|
||||
/// Example: Retrieve relative permeability curves for oil in active
|
||||
/// cell 2718 in both the oil-gas and oil-water sub-systems while
|
||||
/// excluding effects of end-point scaling. This effectively
|
||||
/// retrieves the "raw" tabulated saturation functions in the
|
||||
/// INIT result set.
|
||||
///
|
||||
/// \code
|
||||
/// using RC = ECLSaturationFunc::RawCurve;
|
||||
/// auto func = std::vector<RC>{};
|
||||
/// func.reserve(2);
|
||||
///
|
||||
/// // Request krog (oil rel-perm in oil-gas system)
|
||||
/// func.push_back(RC{
|
||||
/// RC::Function::RelPerm,
|
||||
/// RC::SubSystem::OilGas,
|
||||
/// ECLPhaseIndex::Liquid
|
||||
/// });
|
||||
///
|
||||
/// // Request krow (oil rel-perm in oil-water system)
|
||||
/// func.push_back(RC{
|
||||
/// RC::Function::RelPerm,
|
||||
/// RC::SubSystem::OilWater,
|
||||
/// ECLPhaseIndex::Liquid
|
||||
/// });
|
||||
///
|
||||
/// const auto graph =
|
||||
/// sfunc.getSatFuncCurve(func, 2718, false);
|
||||
/// \endcode
|
||||
std::vector<FlowDiagnostics::Graph>
|
||||
getSatFuncCurve(const std::vector<RawCurve>& func,
|
||||
const int activeCell,
|
||||
const bool useEPS = true) const;
|
||||
|
||||
private:
|
||||
/// Implementation backend.
|
||||
class Impl;
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
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/ECLTableInterpolation1D.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Details {
|
||||
template <class Compare>
|
||||
std::vector<double>::size_type
|
||||
intervalInRange(const std::vector<double>& abscissas,
|
||||
const double x,
|
||||
Compare&& compare)
|
||||
{
|
||||
// Verify that 'x' is within range. Order of arguments matters.
|
||||
assert (! compare(x, abscissas.front()));
|
||||
assert (! compare(abscissas.back(), x));
|
||||
|
||||
const auto b = std::begin(abscissas);
|
||||
const auto p =
|
||||
std::lower_bound(b, std::end(abscissas), x,
|
||||
std::forward<Compare>(compare));
|
||||
|
||||
assert (p != std::end(abscissas));
|
||||
|
||||
// p = lower_bound() => p identifies *right-hand* (upper) end-point
|
||||
// of interval (insertion point) => (p - b) is index of right-hand
|
||||
// end-point. Consequently (p - b) - 1 is index of *left-hand*
|
||||
// end-point and the point with which we associate the pertinent
|
||||
// interval. Special case handling for p == b.
|
||||
|
||||
return (p == b) ? 0 : (p - b) - 1;
|
||||
}
|
||||
|
||||
template <class Compare>
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint
|
||||
identifyPoint(const std::vector<double>& xi,
|
||||
const double x,
|
||||
Compare&& compare)
|
||||
{
|
||||
namespace PP = ::Opm::Interp1D::PiecewisePolynomial;
|
||||
using PCat = ::Opm::Interp1D::PointCategory;
|
||||
|
||||
const auto left = xi.front();
|
||||
if (compare(x, left)) {
|
||||
// Left of min(xi) (== xi.front())
|
||||
return PP::LocalInterpPoint {
|
||||
PCat::LeftOfRange, 0, x - left,
|
||||
};
|
||||
}
|
||||
|
||||
const auto right = xi.back();
|
||||
if (compare(right, x)) {
|
||||
// Right of max(xi) (== xi.back())
|
||||
return PP::LocalInterpPoint {
|
||||
PCat::RightOfRange, xi.size() - 1, x - right,
|
||||
};
|
||||
}
|
||||
|
||||
// Common case: x \in [min(xi), max(xi)]
|
||||
const auto interval = intervalInRange(xi, x, compare);
|
||||
|
||||
assert ((interval + 1 < xi.size()) || (xi.size() == 1));
|
||||
|
||||
return PP::LocalInterpPoint {
|
||||
PCat::InRange, interval, x - xi[interval],
|
||||
};
|
||||
}
|
||||
|
||||
template <class Compare = std::less<double>>
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint
|
||||
identify(const std::vector<double>& xi,
|
||||
const double x,
|
||||
Compare&& compare = Compare{})
|
||||
{
|
||||
if (xi.empty()) {
|
||||
throw std::invalid_argument {
|
||||
"Cannot Relate Point to Non-Existent Range of Variable"
|
||||
};
|
||||
}
|
||||
|
||||
return identifyPoint(xi, x, std::forward<Compare>(compare));
|
||||
}
|
||||
} // Anonymous
|
||||
|
||||
// =====================================================================
|
||||
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint::
|
||||
identify(const std::vector<double>& xi,
|
||||
const double x, std::true_type)
|
||||
{
|
||||
return Details::identify(xi, x);
|
||||
}
|
||||
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint
|
||||
Opm::Interp1D::PiecewisePolynomial::LocalInterpPoint::
|
||||
identify(const std::vector<double>& xi,
|
||||
const double x, std::false_type)
|
||||
{
|
||||
return Details::identify(xi, x, std::greater<double>{});
|
||||
}
|
@ -0,0 +1,404 @@
|
||||
/*
|
||||
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_ECLTABLEINTERPOLATION1D_HEADER_INCLUDED
|
||||
#define OPM_ECLTABLEINTERPOLATION1D_HEADER_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
/// \file
|
||||
///
|
||||
/// Common types and policies for one-dimensional (single variate)
|
||||
/// interpolation of tabulated functions. Mainly intended to support
|
||||
/// piecewise linear interpolation.
|
||||
|
||||
namespace Opm { namespace Interp1D {
|
||||
|
||||
/// Categories of interpolation behaviour according to location of
|
||||
/// interpolation point with respect to range of independent variable.
|
||||
enum class PointCategory {
|
||||
/// Point within range of independent variable.
|
||||
InRange,
|
||||
|
||||
/// Point is to the left of range.
|
||||
LeftOfRange,
|
||||
|
||||
/// Point is to the right of range.
|
||||
RightOfRange,
|
||||
};
|
||||
|
||||
/// Functionality for interpolating functions of a single variate using
|
||||
/// piecewise polynomials.
|
||||
namespace PiecewisePolynomial {
|
||||
|
||||
/// Policies that implement particular behaviours for extrapolating
|
||||
/// a tabulated function, assuming ECL's table format, outside the
|
||||
/// tabulated range of its independent variable.
|
||||
namespace ExtrapolationPolicy {
|
||||
/// Extrapolate function using constant values.
|
||||
class Constant
|
||||
{
|
||||
public:
|
||||
/// Extrapolate function to the left of range using first
|
||||
/// tabulated function value.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xmin Location of left-most abscissa. Unused.
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
/// Unused.
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the left of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double left(const std::vector<double>& /* xi */,
|
||||
const double /* x */,
|
||||
const Index col ,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
return f(0, col);
|
||||
}
|
||||
|
||||
/// Extrapolate function to the rigth of range using "last"
|
||||
/// tabulated function value.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xi Location of tabulated function's abscissas
|
||||
/// (unused).
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
/// Unused.
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the rigth of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double right(const std::vector<double>& xi,
|
||||
const double /* x */,
|
||||
const Index col,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
return f(xi.size() - 1, col);
|
||||
}
|
||||
};
|
||||
|
||||
/// Extrapolate function using extrapolated/estimated
|
||||
/// derivatives in range end-points.
|
||||
///
|
||||
/// Derivatives estimated from table points nearest to the end
|
||||
/// of the range.
|
||||
class Linearly
|
||||
{
|
||||
public:
|
||||
/// Extrapolate function to the left of range using first
|
||||
/// tabulated function value and estimated derivative.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xi Location of tabulated function's abscissas.
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the left of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double left(const std::vector<double>& xi,
|
||||
const double x,
|
||||
const Index col,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
// Derivative of f(i,col)
|
||||
const auto f0 = f(0, col);
|
||||
const auto f1 = f(1, col);
|
||||
const auto dfdx = (f1 - f0) / (xi[1] - xi[0]);
|
||||
|
||||
// <= 0 if ascending range.
|
||||
const auto dx = x - xi.front();
|
||||
|
||||
return f0 + (dfdx * dx);
|
||||
}
|
||||
|
||||
/// Extrapolate function to the rigth of range using "last"
|
||||
/// tabulated function value and estimated derivative.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xi Location of tabulated function's abscissas.
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the rigth of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double right(const std::vector<double>& xi,
|
||||
const double x,
|
||||
const Index col,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
const auto nRows = xi.size();
|
||||
|
||||
// Derivative of f(i,col)
|
||||
const auto f0 = f(nRows - 2, col);
|
||||
const auto f1 = f(nRows - 1, col);
|
||||
const auto dfdx =
|
||||
(f1 - f0) / (xi[nRows - 1] - xi[nRows - 2]);
|
||||
|
||||
// >= 0 if ascending range
|
||||
const auto dx = x - xi.back();
|
||||
|
||||
return f1 + (dfdx * dx);
|
||||
}
|
||||
};
|
||||
|
||||
/// Extrapolate function using tabulated constant derivatives in
|
||||
/// range end-points.
|
||||
class LinearlyWithDerivatives
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
///
|
||||
/// \param[in] nResCol Number of function value (result)
|
||||
/// columns in underlying tabulated function. Equal to
|
||||
/// total number of dependent variable columns less the
|
||||
/// number of derivative columns. Typically two as in
|
||||
/// the cases of the PV{D,T}{G,O} tables.
|
||||
explicit LinearlyWithDerivatives(const std::size_t nResCol)
|
||||
: nResCol_(nResCol)
|
||||
{}
|
||||
|
||||
/// Extrapolate function to the left of range using first
|
||||
/// tabulated function value and corresponding derivative.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xi Location of tabulated function's abscissas.
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the left of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double left(const std::vector<double>& xi,
|
||||
const double x,
|
||||
const Index col,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
// Derivative of f(i,col) in f(i, nResCol_ + col)
|
||||
const auto dfdx = f(0, this->nResCol_ + col);
|
||||
const auto dx = x - xi.front(); // <= 0 if ascending range
|
||||
|
||||
return f(0, col) + (dfdx * dx);
|
||||
}
|
||||
|
||||
/// Extrapolate function to the rigth of range using "last"
|
||||
/// tabulated function value and corresponding derivative.
|
||||
///
|
||||
/// \tparam TabulatedFunction Representation of a tabulated
|
||||
/// function. Must support function call operator such
|
||||
/// that the statement \code y = Func(i, col); \endcode
|
||||
/// returns the value of the \c col-th dependent variate
|
||||
/// at the \c i-th abscissa.
|
||||
///
|
||||
/// \tparam Index Integer type representing an index into
|
||||
/// the abscissas of the tabulated function.
|
||||
///
|
||||
/// \param[in] xi Location of tabulated function's abscissas.
|
||||
///
|
||||
/// \param[in] x Interpolation point (global coordinates).
|
||||
///
|
||||
/// \param[in] col Column index identifying which dependent
|
||||
/// variate to extrapolate.
|
||||
///
|
||||
/// \param[in] nRows Number of rows (abscissas) in
|
||||
/// function's underlying table representation.
|
||||
///
|
||||
/// \param[in] f Tabulated function instance.
|
||||
///
|
||||
/// \return Value of \p col-th variate of tabulated function
|
||||
/// extrapolated to the rigth of the range of table's
|
||||
/// independent variate.
|
||||
template <class TabulatedFunction, class Index>
|
||||
double right(const std::vector<double>& xi,
|
||||
const double x,
|
||||
const Index col,
|
||||
TabulatedFunction&& f) const
|
||||
{
|
||||
const auto nRows = xi.size();
|
||||
|
||||
// Derivative of f(i,col) in f(i, nResCol_ + col)
|
||||
const auto dfdx = f(nRows - 1, this->nResCol_ + col);
|
||||
const auto dx = x - xi.back(); // >= 0 if ascending range
|
||||
|
||||
return f(nRows - 1, col) + (dfdx * dx);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Number of function value (result) columns in underlying
|
||||
/// tabulated function.
|
||||
std::size_t nResCol_;
|
||||
};
|
||||
} // ExtrapolationPolicy
|
||||
|
||||
/// Interpolation point localized with respect to sequence of
|
||||
/// non-overlapping intervals with separating abscissas.
|
||||
struct LocalInterpPoint {
|
||||
/// Interpolation behaviour of point with respect to range.
|
||||
PointCategory cat;
|
||||
|
||||
/// Interval index. Meaningful only with respect to particular
|
||||
/// sequence of abscissas. Zero when
|
||||
///
|
||||
/// \code
|
||||
/// cat == PointCategory::LeftOfRange
|
||||
/// \endcode
|
||||
///
|
||||
/// Equal to number of abscissas (i.e., one greater than number
|
||||
/// of intervals) when
|
||||
///
|
||||
/// \code
|
||||
/// cat == PointCategory::RightOfRange
|
||||
/// \endcode.
|
||||
std::vector<double>::size_type interval;
|
||||
|
||||
/// Local coordinate within interval. Defined as
|
||||
///
|
||||
/// \code
|
||||
/// x - abscissa[interval]
|
||||
/// \endcode
|
||||
///
|
||||
/// Non-positive for PointCategory::LeftOfRange. Non-negative
|
||||
/// otherwise.
|
||||
double t;
|
||||
|
||||
/// Identify point category and, usually, particular interval in
|
||||
/// which a specific point is localized.
|
||||
///
|
||||
/// Overload for usual case of ascendingly sorted abscissas.
|
||||
///
|
||||
/// \param[in] xi Sequence of separating abscissas representing
|
||||
/// non-overlapping intervals of an independent variable.
|
||||
///
|
||||
/// \param[in] x Sample point.
|
||||
///
|
||||
/// \param[in] is_ascending Tagged dispatch overload
|
||||
/// disambiguation object. Unused.
|
||||
///
|
||||
/// \return Sample point localized with respect to the abscissas
|
||||
/// \p xi.
|
||||
static LocalInterpPoint
|
||||
identify(const std::vector<double>& xi,
|
||||
const double x,
|
||||
std::true_type is_ascending = std::true_type{});
|
||||
|
||||
/// Identify point category and, usually, particular interval in
|
||||
/// which a specific point is localized.
|
||||
///
|
||||
/// Overload for case of descendingly sorted abscissas (e.g.,
|
||||
/// through \code std::sort(first, last, std::greater<>{})
|
||||
/// \endcode).
|
||||
///
|
||||
/// \param[in] xi Sequence of separating abscissas representing
|
||||
/// non-overlapping intervals of an independent variable.
|
||||
///
|
||||
/// \param[in] x Sample point.
|
||||
///
|
||||
/// \param[in] is_ascending Tagged dispatch overload
|
||||
/// disambiguation object. Unused.
|
||||
///
|
||||
/// \return Sample point localized with respect to the abscissas
|
||||
/// \p xi.
|
||||
static LocalInterpPoint
|
||||
identify(const std::vector<double>& xi,
|
||||
const double x,
|
||||
std::false_type is_ascending);
|
||||
};
|
||||
|
||||
} // PiecewisePolynomial
|
||||
|
||||
}} // Opm::Interp1D
|
||||
|
||||
#endif // OPM_ECLTABLEINTERPOLATION1D_HEADER_INCLUDED
|
@ -42,6 +42,16 @@ namespace Opm { namespace ECLUnits {
|
||||
class USys<ECL_METRIC_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||
{
|
||||
public:
|
||||
virtual double density() const override
|
||||
{
|
||||
return Metric::Density;
|
||||
}
|
||||
|
||||
virtual double depth() const override
|
||||
{
|
||||
return Metric::Length;
|
||||
}
|
||||
|
||||
virtual double pressure() const override
|
||||
{
|
||||
return Metric::Pressure;
|
||||
@ -57,6 +67,16 @@ namespace Opm { namespace ECLUnits {
|
||||
return Metric::ReservoirVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeGas() const override
|
||||
{
|
||||
return Metric::GasSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeLiquid() const override
|
||||
{
|
||||
return Metric::LiquidSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double time() const override
|
||||
{
|
||||
return Metric::Time;
|
||||
@ -66,12 +86,27 @@ namespace Opm { namespace ECLUnits {
|
||||
{
|
||||
return Metric::Transmissibility;
|
||||
}
|
||||
|
||||
virtual double viscosity() const override
|
||||
{
|
||||
return Metric::Viscosity;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class USys<ECL_FIELD_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||
{
|
||||
public:
|
||||
virtual double density() const override
|
||||
{
|
||||
return Field::Density;
|
||||
}
|
||||
|
||||
virtual double depth() const override
|
||||
{
|
||||
return Field::Length;
|
||||
}
|
||||
|
||||
virtual double pressure() const override
|
||||
{
|
||||
return Field::Pressure;
|
||||
@ -87,6 +122,16 @@ namespace Opm { namespace ECLUnits {
|
||||
return Field::ReservoirVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeGas() const override
|
||||
{
|
||||
return Field::GasSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeLiquid() const override
|
||||
{
|
||||
return Field::LiquidSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double time() const override
|
||||
{
|
||||
return Field::Time;
|
||||
@ -96,12 +141,27 @@ namespace Opm { namespace ECLUnits {
|
||||
{
|
||||
return Field::Transmissibility;
|
||||
}
|
||||
|
||||
virtual double viscosity() const override
|
||||
{
|
||||
return Field::Viscosity;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class USys<ECL_LAB_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||
{
|
||||
public:
|
||||
virtual double density() const override
|
||||
{
|
||||
return Lab::Density;
|
||||
}
|
||||
|
||||
virtual double depth() const override
|
||||
{
|
||||
return Lab::Length;
|
||||
}
|
||||
|
||||
virtual double pressure() const override
|
||||
{
|
||||
return Lab::Pressure;
|
||||
@ -117,6 +177,16 @@ namespace Opm { namespace ECLUnits {
|
||||
return Lab::ReservoirVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeGas() const override
|
||||
{
|
||||
return Lab::GasSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeLiquid() const override
|
||||
{
|
||||
return Lab::LiquidSurfaceVolume;
|
||||
}
|
||||
|
||||
virtual double time() const override
|
||||
{
|
||||
return Lab::Time;
|
||||
@ -126,12 +196,30 @@ namespace Opm { namespace ECLUnits {
|
||||
{
|
||||
return Lab::Transmissibility;
|
||||
}
|
||||
|
||||
virtual double viscosity() const override
|
||||
{
|
||||
return Lab::Viscosity;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class USys<ECL_PVT_M_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||
{
|
||||
public:
|
||||
virtual double density() const override
|
||||
{
|
||||
using namespace prefix;
|
||||
using namespace unit;
|
||||
|
||||
return kilogram / cubic(meter);
|
||||
}
|
||||
|
||||
virtual double depth() const override
|
||||
{
|
||||
return unit::meter;
|
||||
}
|
||||
|
||||
virtual double pressure() const override
|
||||
{
|
||||
return unit::atm;
|
||||
@ -153,6 +241,16 @@ namespace Opm { namespace ECLUnits {
|
||||
return cubic(meter);
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeGas() const override
|
||||
{
|
||||
return unit::cubic(unit::meter);
|
||||
}
|
||||
|
||||
virtual double surfaceVolumeLiquid() const override
|
||||
{
|
||||
return unit::cubic(unit::meter);
|
||||
}
|
||||
|
||||
virtual double time() const override
|
||||
{
|
||||
return unit::day;
|
||||
@ -165,6 +263,11 @@ namespace Opm { namespace ECLUnits {
|
||||
|
||||
return centi*Poise * cubic(meter) / (day * atm);
|
||||
}
|
||||
|
||||
virtual double viscosity() const override
|
||||
{
|
||||
return prefix::centi*unit::Poise;
|
||||
}
|
||||
};
|
||||
} // namespace Impl
|
||||
}} // namespace Opm::ECLUnits
|
||||
@ -183,6 +286,18 @@ Opm::ECLUnits::Impl::getUnitConvention(const int usys)
|
||||
+ std::to_string(usys));
|
||||
}
|
||||
|
||||
double Opm::ECLUnits::UnitSystem::dissolvedGasOilRat() const
|
||||
{
|
||||
return this->surfaceVolumeGas()
|
||||
/ this->surfaceVolumeLiquid();
|
||||
}
|
||||
|
||||
double Opm::ECLUnits::UnitSystem::vaporisedOilGasRat() const
|
||||
{
|
||||
return this->surfaceVolumeLiquid()
|
||||
/ this->surfaceVolumeGas();
|
||||
}
|
||||
|
||||
std::unique_ptr<const ::Opm::ECLUnits::UnitSystem>
|
||||
Opm::ECLUnits::createUnitSystem(const int usys)
|
||||
{
|
||||
|
@ -28,11 +28,19 @@ namespace Opm {
|
||||
|
||||
struct UnitSystem
|
||||
{
|
||||
virtual double pressure() const = 0;
|
||||
virtual double reservoirRate() const = 0;
|
||||
virtual double reservoirVolume() const = 0;
|
||||
virtual double time() const = 0;
|
||||
virtual double transmissibility() const = 0;
|
||||
virtual double density() const = 0;
|
||||
virtual double depth() const = 0;
|
||||
virtual double pressure() const = 0;
|
||||
virtual double reservoirRate() const = 0;
|
||||
virtual double reservoirVolume() const = 0;
|
||||
virtual double surfaceVolumeLiquid() const = 0;
|
||||
virtual double surfaceVolumeGas() const = 0;
|
||||
virtual double time() const = 0;
|
||||
virtual double transmissibility() const = 0;
|
||||
virtual double viscosity() const = 0;
|
||||
|
||||
double dissolvedGasOilRat() const; // Rs
|
||||
double vaporisedOilGasRat() const; // Rv
|
||||
};
|
||||
|
||||
std::unique_ptr<const UnitSystem>
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <examples/exampleSetup.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
@ -213,42 +215,6 @@ namespace {
|
||||
return max;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
availableReportSteps(const example::FilePaths& paths)
|
||||
{
|
||||
using FilePtr = ::ERT::
|
||||
ert_unique_ptr<ecl_file_type, ecl_file_close>;
|
||||
|
||||
const auto rsspec_fn = example::
|
||||
deriveFileName(paths.grid, { ".RSSPEC", ".FRSSPEC" });
|
||||
|
||||
// Read-only, keep open between requests
|
||||
const auto open_flags = 0;
|
||||
|
||||
auto rsspec = FilePtr{
|
||||
ecl_file_open(rsspec_fn.generic_string().c_str(), open_flags)
|
||||
};
|
||||
|
||||
auto* globView = ecl_file_get_global_view(rsspec.get());
|
||||
|
||||
const auto* ITIME_kw = "ITIME";
|
||||
const auto n = ecl_file_view_get_num_named_kw(globView, ITIME_kw);
|
||||
|
||||
auto steps = std::vector<int>(n);
|
||||
|
||||
for (auto i = 0*n; i < n; ++i) {
|
||||
const auto* itime =
|
||||
ecl_file_view_iget_named_kw(globView, ITIME_kw, i);
|
||||
|
||||
const auto* itime_data =
|
||||
static_cast<const int*>(ecl_kw_iget_ptr(itime, 0));
|
||||
|
||||
steps[i] = itime_data[0];
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
ErrorTolerance
|
||||
testTolerances(const ::Opm::ParameterGroup& param)
|
||||
{
|
||||
@ -278,8 +244,8 @@ namespace {
|
||||
|
||||
ReferenceToF
|
||||
loadReference(const ::Opm::ParameterGroup& param,
|
||||
const int step,
|
||||
const int nDigits)
|
||||
const int step,
|
||||
const int nDigits)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -431,7 +397,7 @@ try {
|
||||
auto setup = example::Setup(argc, argv);
|
||||
|
||||
const auto tol = testTolerances(setup.param);
|
||||
const auto steps = availableReportSteps(setup.file_paths);
|
||||
const auto steps = setup.result_set.reportStepIDs();
|
||||
|
||||
const auto E = sampleDifferences(std::move(setup), steps);
|
||||
const auto ok =
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <examples/exampleSetup.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
@ -30,6 +32,7 @@
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
@ -340,42 +343,6 @@ namespace {
|
||||
return max;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
availableReportSteps(const example::FilePaths& paths)
|
||||
{
|
||||
using FilePtr = ::ERT::
|
||||
ert_unique_ptr<ecl_file_type, ecl_file_close>;
|
||||
|
||||
const auto rsspec_fn = example::
|
||||
deriveFileName(paths.grid, { ".RSSPEC", ".FRSSPEC" });
|
||||
|
||||
// Read-only, keep open between requests
|
||||
const auto open_flags = 0;
|
||||
|
||||
auto rsspec = FilePtr{
|
||||
ecl_file_open(rsspec_fn.generic_string().c_str(), open_flags)
|
||||
};
|
||||
|
||||
auto* globView = ecl_file_get_global_view(rsspec.get());
|
||||
|
||||
const auto* ITIME_kw = "ITIME";
|
||||
const auto n = ecl_file_view_get_num_named_kw(globView, ITIME_kw);
|
||||
|
||||
auto steps = std::vector<int>(n);
|
||||
|
||||
for (auto i = 0*n; i < n; ++i) {
|
||||
const auto* itime =
|
||||
ecl_file_view_iget_named_kw(globView, ITIME_kw, i);
|
||||
|
||||
const auto* itime_data =
|
||||
static_cast<const int*>(ecl_kw_iget_ptr(itime, 0));
|
||||
|
||||
steps[i] = itime_data[0];
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
ErrorTolerance
|
||||
testTolerances(const ::Opm::ParameterGroup& param)
|
||||
{
|
||||
@ -412,9 +379,9 @@ namespace {
|
||||
|
||||
ReferenceSolution
|
||||
loadReference(const ::Opm::ParameterGroup& param,
|
||||
const std::string& quant,
|
||||
const int step,
|
||||
const int nDigits)
|
||||
const std::string& quant,
|
||||
const int step,
|
||||
const int nDigits)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -491,12 +458,21 @@ namespace {
|
||||
E.relative.push_back(std::move(rel));
|
||||
}
|
||||
|
||||
std::unique_ptr<Opm::ECLRestartData>
|
||||
openRestartSet(const Opm::ECLCaseUtilities::ResultSet& rset,
|
||||
const int step)
|
||||
{
|
||||
return std::unique_ptr<Opm::ECLRestartData> {
|
||||
new Opm::ECLRestartData(rset.restartFile(step))
|
||||
};
|
||||
}
|
||||
|
||||
std::array<AggregateErrors, 2>
|
||||
sampleDifferences(const ::Opm::ECLGraph& graph,
|
||||
const ::Opm::ECLRestartData& rstrt,
|
||||
const ::Opm::ParameterGroup& param,
|
||||
const std::string& quant,
|
||||
const std::vector<int>& steps)
|
||||
sampleDifferences(const ::Opm::ECLGraph& graph,
|
||||
const ::Opm::ECLCaseUtilities::ResultSet& rset,
|
||||
const ::Opm::ParameterGroup& param,
|
||||
const std::string& quant,
|
||||
const std::vector<int>& steps)
|
||||
{
|
||||
const auto ECLquant = boost::algorithm::to_upper_copy(quant);
|
||||
|
||||
@ -508,10 +484,18 @@ namespace {
|
||||
|
||||
const auto nDigits = numDigits(steps);
|
||||
|
||||
auto rstrt = std::unique_ptr<Opm::ECLRestartData>{};
|
||||
|
||||
auto E = std::array<AggregateErrors, 2>{};
|
||||
|
||||
for (const auto& step : steps) {
|
||||
if (! rstrt.selectReportStep(step)) {
|
||||
if (! (rset.isUnifiedRestart() && bool(rstrt))) {
|
||||
// Separate (not unified) restart file or this is the first
|
||||
// time we're selecting a report step.
|
||||
rstrt = openRestartSet(rset, step);
|
||||
}
|
||||
|
||||
if (! rstrt->selectReportStep(step)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -519,7 +503,7 @@ namespace {
|
||||
|
||||
{
|
||||
const auto raw = Calculated {
|
||||
graph.rawLinearisedCellData<double>(rstrt, ECLquant)
|
||||
graph.rawLinearisedCellData<double>(*rstrt, ECLquant)
|
||||
};
|
||||
|
||||
computeErrors(Reference{ ref.raw }, raw, E[0]);
|
||||
@ -527,7 +511,7 @@ namespace {
|
||||
|
||||
{
|
||||
const auto SI = Calculated {
|
||||
graph.linearisedCellData(rstrt, ECLquant, unit)
|
||||
graph.linearisedCellData(*rstrt, ECLquant, unit)
|
||||
};
|
||||
|
||||
computeErrors(Reference{ ref.SI }, SI, E[1]);
|
||||
@ -556,28 +540,27 @@ namespace {
|
||||
}
|
||||
|
||||
::Opm::ECLGraph
|
||||
constructGraph(const example::FilePaths& pth)
|
||||
constructGraph(const Opm::ECLCaseUtilities::ResultSet& rset)
|
||||
{
|
||||
const auto I = ::Opm::ECLInitFileData(pth.init);
|
||||
const auto I = ::Opm::ECLInitFileData(rset.initFile());
|
||||
|
||||
return ::Opm::ECLGraph::load(pth.grid, I);
|
||||
return ::Opm::ECLGraph::load(rset.gridFile(), 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 tol = testTolerances(prm);
|
||||
const auto prm = example::initParam(argc, argv);
|
||||
const auto rset = example::identifyResultSet(prm);
|
||||
const auto tol = testTolerances(prm);
|
||||
|
||||
const auto rstrt = ::Opm::ECLRestartData(pth.restart);
|
||||
const auto steps = availableReportSteps(pth);
|
||||
const auto graph = constructGraph(pth);
|
||||
const auto steps = rset.reportStepIDs();
|
||||
const auto graph = constructGraph(rset);
|
||||
|
||||
auto all_ok = true;
|
||||
for (const auto& quant : testQuantities(prm)) {
|
||||
const auto E =
|
||||
sampleDifferences(graph, rstrt, prm, quant, steps);
|
||||
sampleDifferences(graph, rset, prm, quant, steps);
|
||||
|
||||
const auto ok =
|
||||
everythingFine(E[0], tol) && everythingFine(E[1], tol);
|
||||
|
@ -18,10 +18,11 @@
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
|
||||
#include <examples/exampleSetup.hpp>
|
||||
|
||||
#include <opm/utility/ECLCaseUtilities.hpp>
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
@ -186,7 +187,7 @@ namespace {
|
||||
}
|
||||
|
||||
bool transfieldAcceptable(const ::Opm::ParameterGroup& param,
|
||||
const std::vector<double>& trans)
|
||||
const std::vector<double>& trans)
|
||||
{
|
||||
const auto Tref = loadReference(param);
|
||||
|
||||
@ -208,21 +209,21 @@ namespace {
|
||||
}
|
||||
|
||||
::Opm::ECLGraph
|
||||
constructGraph(const example::FilePaths& pth)
|
||||
constructGraph(const Opm::ECLCaseUtilities::ResultSet& rset)
|
||||
{
|
||||
const auto I = ::Opm::ECLInitFileData(pth.init);
|
||||
const auto I = ::Opm::ECLInitFileData(rset.initFile());
|
||||
|
||||
return ::Opm::ECLGraph::load(pth.grid, I);
|
||||
return ::Opm::ECLGraph::load(rset.gridFile(), 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 = constructGraph(pth);
|
||||
const auto T = G.transmissibility();
|
||||
const auto ok = transfieldAcceptable(prm, T);
|
||||
const auto prm = example::initParam(argc, argv);
|
||||
const auto rset = example::identifyResultSet(prm);
|
||||
const auto G = constructGraph(rset);
|
||||
const auto T = G.transmissibility();
|
||||
const auto ok = transfieldAcceptable(prm, T);
|
||||
|
||||
std::cout << (ok ? "OK" : "FAIL") << '\n';
|
||||
|
||||
|
@ -99,7 +99,6 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
@ -111,9 +110,21 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
check_is_close(s_inp, s);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
@ -136,7 +147,6 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0,
|
||||
@ -148,9 +158,30 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.2, // t.s <= smin => smin
|
||||
0.2, // t.s <= smin => smin
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
@ -173,7 +204,6 @@ BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0.25,
|
||||
@ -185,9 +215,30 @@ BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
0.8, // t.s >= smax => smax
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
@ -210,7 +261,6 @@ BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
0.0,
|
||||
@ -222,9 +272,30 @@ BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.2, // t.s <= smin => smin
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
0.8, // t.s >= smax => smax
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
@ -252,7 +323,6 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.2,
|
||||
0.2,
|
||||
@ -264,9 +334,21 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
check_is_close(s_inp, expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
@ -290,7 +372,6 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.20,
|
||||
0.32,
|
||||
@ -302,9 +383,21 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
check_is_close(s_inp, s);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
@ -328,7 +421,6 @@ BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.20,
|
||||
0.20,
|
||||
@ -340,9 +432,30 @@ BOOST_AUTO_TEST_CASE (ScaledMax)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.2, // t.s <= smin -> smin
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
@ -366,7 +479,6 @@ BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.2,
|
||||
0.2,
|
||||
@ -378,9 +490,30 @@ BOOST_AUTO_TEST_CASE (ScaledBoth)
|
||||
|
||||
const auto eps = SF::TwoPointScaling{ smin, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.5, // t.s <= smin -> smin
|
||||
0.5, // t.s <= smin -> smin
|
||||
0.5, // t.s <= smin -> smin
|
||||
0.6,
|
||||
0.7, // t.s >= smax -> smax
|
||||
0.7, // t.s >= smax -> smax
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
@ -411,7 +544,6 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0.0,
|
||||
0.2,
|
||||
@ -423,9 +555,21 @@ BOOST_AUTO_TEST_CASE (NoScaling)
|
||||
|
||||
const auto eps = SF::ThreePointScaling{ smin, sdisp, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
check_is_close(s_inp, s);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
@ -449,7 +593,6 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
1.0,
|
||||
};
|
||||
|
||||
const auto sp = associate(s);
|
||||
const auto expect = std::vector<double> {
|
||||
0,
|
||||
1.0 / 15,
|
||||
@ -461,9 +604,30 @@ BOOST_AUTO_TEST_CASE (ScaledConnate)
|
||||
|
||||
const auto eps = SF::ThreePointScaling{ smin, sdisp, smax };
|
||||
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
// Input saturation -> Scaled saturation
|
||||
{
|
||||
const auto sp = associate(s);
|
||||
const auto s_eff = eps.eval(tep, sp);
|
||||
|
||||
check_is_close(s_eff, expect);
|
||||
check_is_close(s_eff, expect);
|
||||
}
|
||||
|
||||
// Tabulated saturation -> Input saturation
|
||||
{
|
||||
const auto sp = associate(expect);
|
||||
const auto s_inp = eps.reverse(tep, sp);
|
||||
|
||||
const auto s_inp_expect = std::vector<double> {
|
||||
0.1, // t.s <= smin -> smin
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
};
|
||||
|
||||
check_is_close(s_inp, s_inp_expect);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
@ -81,6 +81,19 @@ namespace {
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
Opm::SatFuncInterpolant::ConvertUnits
|
||||
createDummyUnitConverter(const std::size_t ncol)
|
||||
{
|
||||
using Cvrt = Opm::SatFuncInterpolant::ConvertUnits::Converter;
|
||||
|
||||
auto id = [](const double x) { return x; };
|
||||
|
||||
return Opm::SatFuncInterpolant::ConvertUnits {
|
||||
Cvrt{ id },
|
||||
std::vector<Cvrt>(ncol, Cvrt{ id })
|
||||
};
|
||||
}
|
||||
} // Namespace Anonymous
|
||||
|
||||
// =====================================================================
|
||||
@ -97,11 +110,13 @@ BOOST_AUTO_TEST_CASE (EmptyTable)
|
||||
// s, kr , pc
|
||||
};
|
||||
|
||||
t.numRows = 0;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 0;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(2)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -114,11 +129,13 @@ BOOST_AUTO_TEST_CASE (SingleNode)
|
||||
0.3 , 0.1 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 1;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 1;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(2)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -134,11 +151,13 @@ BOOST_AUTO_TEST_CASE (NoResultColumns)
|
||||
0.8,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 1;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 1;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(0)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -158,11 +177,13 @@ BOOST_AUTO_TEST_CASE (EmptyTableLargeNodeAlloc)
|
||||
1.0e+20 , 1.0e+20, 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 8;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 8;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(2)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -183,11 +204,13 @@ BOOST_AUTO_TEST_CASE (SingleNodeLargeNodeAlloc)
|
||||
1.0e+20 , 1.0e+20, 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 9;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 9;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(2)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -211,11 +234,13 @@ BOOST_AUTO_TEST_CASE (NoResultColumnsLargeNodeAlloc)
|
||||
1.0e+20,
|
||||
};
|
||||
|
||||
t.numRows = 12;
|
||||
t.numCols = 1;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 12;
|
||||
t.numCols = 1;
|
||||
t.numTables = 1;
|
||||
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t)),
|
||||
BOOST_CHECK_THROW(Opm::SatFuncInterpolant(toRawTableFormat(t),
|
||||
createDummyUnitConverter(2)),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@ -238,14 +263,18 @@ BOOST_AUTO_TEST_CASE (AtNodes)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{ 0.8, 0.3, 0.3, 0.2 };
|
||||
const auto kr_expect = std::vector<double>{ 0.5, 0.1, 0.1, 0.0 };
|
||||
@ -284,14 +313,18 @@ BOOST_AUTO_TEST_CASE (AboveAndBelow)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{ 0.80000001, 0.9, 0.199999999, 0.1 };
|
||||
const auto kr_expect = std::vector<double>{ 0.5, 0.5, 0.0, 0.0 };
|
||||
@ -318,14 +351,18 @@ BOOST_AUTO_TEST_CASE (Interpolation)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{
|
||||
0.2000,
|
||||
@ -411,14 +448,18 @@ BOOST_AUTO_TEST_CASE (InterpolationLargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{
|
||||
0.0000,
|
||||
@ -534,14 +575,18 @@ BOOST_AUTO_TEST_CASE (AtNodes)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{ 0.8, 0.3, 0.3, 0.2 };
|
||||
const auto kr_expect = std::vector<double>{ 0.5, 0.1, 0.1, 0.0 };
|
||||
@ -599,14 +644,18 @@ BOOST_AUTO_TEST_CASE (AboveAndBelow)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{ 0.80000001, 0.9, 0.199999999, 0.1 };
|
||||
const auto kr_expect = std::vector<double>{ 0.5, 0.5, 0.0, 0.0 };
|
||||
@ -655,14 +704,18 @@ BOOST_AUTO_TEST_CASE (Interpolation)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{
|
||||
0.2000,
|
||||
@ -805,14 +858,18 @@ BOOST_AUTO_TEST_CASE (InterpolationLargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
const auto s = std::vector<double>{
|
||||
0.0000,
|
||||
@ -911,9 +968,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -923,7 +981,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -960,9 +1021,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -972,7 +1034,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -997,9 +1062,10 @@ BOOST_AUTO_TEST_CASE (SWFN)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1009,7 +1075,10 @@ BOOST_AUTO_TEST_CASE (SWFN)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1047,9 +1116,10 @@ BOOST_AUTO_TEST_CASE (SWFN_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1059,7 +1129,10 @@ BOOST_AUTO_TEST_CASE (SWFN_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1083,9 +1156,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1096,7 +1170,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1134,9 +1211,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1147,7 +1225,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1174,9 +1255,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1187,7 +1269,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1226,9 +1311,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1239,7 +1325,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1266,9 +1355,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1279,7 +1369,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1318,9 +1411,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1331,7 +1425,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1360,9 +1457,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 6;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 6;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1373,7 +1471,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1412,9 +1513,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 1;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 };
|
||||
@ -1425,7 +1527,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1474,9 +1579,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2, 0.2, 0.2, 0.2 };
|
||||
@ -1486,7 +1592,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1574,9 +1683,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2, 0.2, 0.2, 0.2 };
|
||||
@ -1586,7 +1696,10 @@ BOOST_AUTO_TEST_CASE (SWFN_CritIsConn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1629,9 +1742,10 @@ BOOST_AUTO_TEST_CASE (SWFN)
|
||||
0.8 , 0.5 , 0.0,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.2 , 0.2 };
|
||||
@ -1641,7 +1755,10 @@ BOOST_AUTO_TEST_CASE (SWFN)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1733,9 +1850,10 @@ BOOST_AUTO_TEST_CASE (SWFN_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100 , 0.0, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.1 , 0.1 , 0.0 };
|
||||
@ -1745,7 +1863,10 @@ BOOST_AUTO_TEST_CASE (SWFN_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1784,9 +1905,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 3;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2, 0.2, 0.2, 0.2 };
|
||||
@ -1797,7 +1919,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1886,9 +2011,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 15
|
||||
};
|
||||
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 15;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2, 0.1, 0.0, 0.1 };
|
||||
@ -1899,7 +2025,10 @@ BOOST_AUTO_TEST_CASE (SOF3_CritIsConn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -1944,9 +2073,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.2 , 0.2 };
|
||||
@ -1957,7 +2087,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -2050,9 +2183,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.1 , 0.2 };
|
||||
@ -2063,7 +2197,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOGCR_is_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -2108,9 +2245,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn)
|
||||
0.8 , 0.5 , 0.8,
|
||||
};
|
||||
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 4;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.2 , 0.2 };
|
||||
@ -2121,7 +2259,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -2214,9 +2355,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.0 , 0.2 };
|
||||
@ -2227,7 +2369,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SOWCR_is_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -2280,9 +2425,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn)
|
||||
0.9 , 0.8 , 0.9,
|
||||
};
|
||||
|
||||
t.numRows = 6;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 6;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.2 , 0.2 , 0.2 };
|
||||
@ -2293,7 +2439,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
@ -2386,9 +2535,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn_LargeNodeAlloc)
|
||||
1.0e20 , 1.0e+100, 1.0e+100, // 16
|
||||
};
|
||||
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
t.numPrimary = 1;
|
||||
t.numRows = 16;
|
||||
t.numCols = 3;
|
||||
t.numTables = 4;
|
||||
|
||||
// Table end-points
|
||||
const auto sconn_expect = std::vector<double>{ 0.2 , 0.1 , 0.2 , 0.2 };
|
||||
@ -2399,7 +2549,10 @@ BOOST_AUTO_TEST_CASE (SOF3_SCR_Not_Conn_LargeNodeAlloc)
|
||||
// Note: Need to convert input table to column major (Fortran) order
|
||||
// because that is the format in which PropTable1D expects the tabular
|
||||
// data.
|
||||
const auto swfunc = Opm::SatFuncInterpolant(toRawTableFormat(t));
|
||||
const auto swfunc = Opm::SatFuncInterpolant {
|
||||
toRawTableFormat(t),
|
||||
createDummyUnitConverter(t.numCols - 1)
|
||||
};
|
||||
|
||||
using ResultColumn = Opm::SatFuncInterpolant::ResultColumn;
|
||||
|
||||
|
544
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclpvtcommon.cpp
vendored
Normal file
544
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclpvtcommon.cpp
vendored
Normal file
@ -0,0 +1,544 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF ICT, Applied Mathematics.
|
||||
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_ECLPVTCOMMON_UNITCONV
|
||||
|
||||
#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/ECLPvtCommon.hpp>
|
||||
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
struct ConvertToSI
|
||||
{
|
||||
explicit ConvertToSI(const ::Opm::ECLUnits::UnitSystem& usys);
|
||||
|
||||
double dens { 0.0 };
|
||||
double press { 0.0 };
|
||||
double compr { 0.0 };
|
||||
double disgas { 0.0 };
|
||||
double vapoil { 0.0 };
|
||||
|
||||
double recipFvf { 0.0 };
|
||||
double recipFvfDerivPress { 0.0 };
|
||||
double recipFvfDerivVapOil { 0.0 };
|
||||
|
||||
double recipFvfVisc { 0.0 };
|
||||
double recipFvfViscDerivPress { 0.0 };
|
||||
double recipFvfViscDerivVapOil { 0.0 };
|
||||
|
||||
double recipFvfGas { 0.0 };
|
||||
double recipFvfGasDerivPress { 0.0 };
|
||||
double recipFvfGasDerivVapOil { 0.0 };
|
||||
|
||||
double recipFvfGasVisc { 0.0 };
|
||||
double recipFvfGasViscDerivPress { 0.0 };
|
||||
double recipFvfGasViscDerivVapOil { 0.0 };
|
||||
};
|
||||
|
||||
ConvertToSI::ConvertToSI(const ::Opm::ECLUnits::UnitSystem& usys)
|
||||
{
|
||||
using Cvrt = ::Opm::ECLPVT::CreateUnitConverter::ToSI;
|
||||
|
||||
auto apply = [](const ::Opm::ECLPVT::ConvertUnits::Converter& cnv)
|
||||
{
|
||||
return cnv(1.0);
|
||||
};
|
||||
|
||||
// Mass density
|
||||
this->dens = apply(Cvrt::density(usys));
|
||||
|
||||
// Pressure
|
||||
this->press = apply(Cvrt::pressure(usys));
|
||||
|
||||
// Compressibility
|
||||
this->compr = apply(Cvrt::compressibility(usys));
|
||||
|
||||
// Dissolved gas-oil ratio (Rs)
|
||||
this->disgas = apply(Cvrt::disGas(usys));
|
||||
|
||||
// Vaporised oil-gas ratio (Rv)
|
||||
this->vapoil = apply(Cvrt::vapOil(usys));
|
||||
|
||||
// Reciprocal formation volume factor (1/B)
|
||||
this->recipFvf = apply(Cvrt::recipFvf(usys));
|
||||
|
||||
// Derivative of reciprocal formation volume factor (1/B) with respect
|
||||
// to fluid (phase) pressure.
|
||||
this->recipFvfDerivPress =
|
||||
apply(Cvrt::recipFvfDerivPress(usys));
|
||||
|
||||
// Derivative of reciprocal formation volume factor (1/B) with respect
|
||||
// to vaporised oil-gas ratio.
|
||||
this->recipFvfDerivVapOil =
|
||||
apply(Cvrt::recipFvfDerivVapOil(usys));
|
||||
|
||||
// Reciprocal product of formation volume factor and viscosity
|
||||
// (1/(B*mu)).
|
||||
this->recipFvfVisc = apply(Cvrt::recipFvfVisc(usys));
|
||||
|
||||
// Derivative of reciprocal product of formation volume factor and
|
||||
// viscosity (1/(B*mu)) with respect to fluid (phase) pressure.
|
||||
this->recipFvfViscDerivPress =
|
||||
apply(Cvrt::recipFvfViscDerivPress(usys));
|
||||
|
||||
// Derivative of reciprocal product of formation volume factor and
|
||||
// viscosity (1/(B*mu)) with respect to vaporised oil-gas ratio.
|
||||
this->recipFvfViscDerivVapOil =
|
||||
apply(Cvrt::recipFvfViscDerivVapOil(usys));
|
||||
|
||||
// Reciprocal formation volume factor for gas (1/Bg)
|
||||
this->recipFvfGas = apply(Cvrt::recipFvfGas(usys));
|
||||
|
||||
// Derivative of reciprocal formation volume factor for gas (1/Bg) with
|
||||
// respect to fluid (phase) pressure.
|
||||
this->recipFvfGasDerivPress =
|
||||
apply(Cvrt::recipFvfGasDerivPress(usys));
|
||||
|
||||
// Derivative of reciprocal formation volume factor for gas (1/Bg) with
|
||||
// respect to vaporised oil-gas ratio.
|
||||
this->recipFvfGasDerivVapOil =
|
||||
apply(Cvrt::recipFvfGasDerivVapOil(usys));
|
||||
|
||||
// Reciprocal product of formation volume factor for gas and viscosity
|
||||
// (1/(Bg*mu_g)).
|
||||
this->recipFvfGasVisc = apply(Cvrt::recipFvfGasVisc(usys));
|
||||
|
||||
// Derivative of reciprocal product of formation volume factor for gas
|
||||
// and viscosity (1/(Bg*mu_g)) with respect to fluid (phase) pressure.
|
||||
this->recipFvfGasViscDerivPress =
|
||||
apply(Cvrt::recipFvfGasViscDerivPress(usys));
|
||||
|
||||
// Derivative of reciprocal product of formation volume factor for gas
|
||||
// and viscosity (1/(Bg*mu_g)) with respect to vaporised oil-gas ratio.
|
||||
this->recipFvfGasViscDerivVapOil =
|
||||
apply(Cvrt::recipFvfGasViscDerivVapOil(usys));
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
using DVec = ::Opm::ECLPVT::DenseVector<N>;
|
||||
|
||||
// =====================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (Basic_Conversion)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Metric)
|
||||
{
|
||||
const auto usys = ::Opm::ECLUnits::createUnitSystem(1);
|
||||
|
||||
const auto scale = ConvertToSI(*usys);
|
||||
|
||||
// Mass density
|
||||
BOOST_CHECK_CLOSE(scale.dens, 1.0, 1.0e-10);
|
||||
|
||||
// Pressure
|
||||
BOOST_CHECK_CLOSE(scale.press, 1.0e5, 1.0e-10);
|
||||
|
||||
// Compressibility
|
||||
BOOST_CHECK_CLOSE(scale.compr, 1.0e-5, 1.0e-10);
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Rs)
|
||||
BOOST_CHECK_CLOSE(scale.disgas, 1.0, 1.0e-10);
|
||||
|
||||
// Vaporised Oil-Gas Ratio (Rv)
|
||||
BOOST_CHECK_CLOSE(scale.vapoil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor (1 / B)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvf, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivPress, 1.0e-5, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivVapOil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF and Viscosity (1 / (B*mu)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivPress, 1.0e-2, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivVapOil, 1.0e3, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor for Gas (1 / Bg)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGas, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivPress, 1.0e-5, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg ) w.r.t. Vaporised
|
||||
// Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivVapOil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF for Gas and Viscosity (1 / (Bg*mu_g)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivPress, 1.0e-2, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivVapOil, 1.0e3, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Field)
|
||||
{
|
||||
const auto usys = ::Opm::ECLUnits::createUnitSystem(2);
|
||||
|
||||
const auto scale = ConvertToSI(*usys);
|
||||
|
||||
// Mass density
|
||||
BOOST_CHECK_CLOSE(scale.dens, 1.601846337396014e+01, 1.0e-10);
|
||||
|
||||
// Pressure
|
||||
BOOST_CHECK_CLOSE(scale.press, 6.894757293168360e+03, 1.0e-10);
|
||||
|
||||
// Compressibility
|
||||
BOOST_CHECK_CLOSE(scale.compr, 1.450377377302092e-04, 1.0e-10);
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Rs)
|
||||
BOOST_CHECK_CLOSE(scale.disgas, 1.781076066790352e+02, 1.0e-10);
|
||||
|
||||
// Vaporised Oil-Gas Ratio (Rv)
|
||||
BOOST_CHECK_CLOSE(scale.vapoil, 5.614583333333335e-03, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor (1 / B)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvf, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivPress,
|
||||
1.450377377302092e-04, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivVapOil,
|
||||
1.781076066790352e+02, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF and Viscosity (1 / (B*mu)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivPress,
|
||||
1.450377377302093e-01, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivVapOil,
|
||||
1.781076066790352e+05, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor for Gas (1 / Bg)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGas,
|
||||
1.781076066790352e+02, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivPress,
|
||||
2.583232434526917e-02, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg ) w.r.t. Vaporised
|
||||
// Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivVapOil,
|
||||
3.172231955693390e+04, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF for Gas and Viscosity (1 / (Bg*mu_g)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasVisc,
|
||||
1.781076066790352e+05, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivPress,
|
||||
2.583232434526917e+01, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivVapOil,
|
||||
3.172231955693390e+07, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Lab)
|
||||
{
|
||||
const auto usys = ::Opm::ECLUnits::createUnitSystem(3);
|
||||
|
||||
const auto scale = ConvertToSI(*usys);
|
||||
|
||||
// Mass density
|
||||
BOOST_CHECK_CLOSE(scale.dens, 1.0e3, 1.0e-10);
|
||||
|
||||
// Pressure
|
||||
BOOST_CHECK_CLOSE(scale.press, 101.325e3, 1.0e-10);
|
||||
|
||||
// Compressibility
|
||||
BOOST_CHECK_CLOSE(scale.compr, 9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Rs)
|
||||
BOOST_CHECK_CLOSE(scale.disgas, 1.0, 1.0e-10);
|
||||
|
||||
// Vaporised Oil-Gas Ratio (Rv)
|
||||
BOOST_CHECK_CLOSE(scale.vapoil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor (1 / B)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvf, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivPress,
|
||||
9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivVapOil,
|
||||
1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF and Viscosity (1 / (B*mu)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivPress,
|
||||
9.869232667160128e-03, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivVapOil,
|
||||
1.0e3, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor for Gas (1 / Bg)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGas, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivPress,
|
||||
9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg) w.r.t. Vaporised
|
||||
// Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivVapOil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF for Gas and Viscosity (1 / (Bg*mu_g)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivPress,
|
||||
9.869232667160128e-03, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivVapOil, 1.0e3, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (PVT_M)
|
||||
{
|
||||
const auto usys = ::Opm::ECLUnits::createUnitSystem(4);
|
||||
|
||||
const auto scale = ConvertToSI(*usys);
|
||||
|
||||
// Mass density
|
||||
BOOST_CHECK_CLOSE(scale.dens, 1.0, 1.0e-10);
|
||||
|
||||
// Pressure
|
||||
BOOST_CHECK_CLOSE(scale.press, 101.325e3, 1.0e-10);
|
||||
|
||||
// Compressibility
|
||||
BOOST_CHECK_CLOSE(scale.compr, 9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Rs)
|
||||
BOOST_CHECK_CLOSE(scale.disgas, 1.0, 1.0e-10);
|
||||
|
||||
// Vaporised Oil-Gas Ratio (Rv)
|
||||
BOOST_CHECK_CLOSE(scale.vapoil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor (1 / B)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvf, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivPress,
|
||||
9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF (1 / B) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfDerivVapOil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF and Viscosity (1 / (B*mu)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivPress,
|
||||
9.869232667160128e-03, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF and Viscosity (1 / (B*mu))
|
||||
// w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfViscDerivVapOil, 1.0e3, 1.0e-10);
|
||||
|
||||
// Reciprocal Formation Volume Factor for Gas (1 / Bg)
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGas, 1.0, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivPress,
|
||||
9.869232667160129e-06, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal FVF for Gas (1 / Bg ) w.r.t. Vaporised
|
||||
// Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasDerivVapOil, 1.0, 1.0e-10);
|
||||
|
||||
// Reciprocal Product of FVF for Gas and Viscosity (1 / (Bg*mu_g)).
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasVisc, 1.0e3, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Pressure
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivPress,
|
||||
9.869232667160128e-03, 1.0e-10);
|
||||
|
||||
// Derivative of Reciprocal Product of FVF for Gas and Viscosity (1 /
|
||||
// (Bg*mu_g)) w.r.t. Vaporised Oil-Gas Ratio.
|
||||
BOOST_CHECK_CLOSE(scale.recipFvfGasViscDerivVapOil, 1.0e3, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
||||
// =====================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (DenseVector)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Construct)
|
||||
{
|
||||
// DenseVector<1>
|
||||
{
|
||||
const auto x = DVec<1>{ std::array<double, 1>{ { 1.0 } } };
|
||||
|
||||
BOOST_CHECK_CLOSE(x.array()[0], 1.0, 1.0e-10);
|
||||
}
|
||||
|
||||
// DenseVector<2>
|
||||
{
|
||||
const auto x = DVec<2>{ std::array<double, 2>{ { 2.0, -1.0 } } };
|
||||
|
||||
BOOST_CHECK_CLOSE(x.array()[0], 2.0, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(x.array()[1], -1.0, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Addition)
|
||||
{
|
||||
const auto x = DVec<2>{
|
||||
std::array<double,2>{ 0.1, 2.3 }
|
||||
};
|
||||
|
||||
const auto two_x = x + x;
|
||||
|
||||
BOOST_CHECK_CLOSE(two_x.array()[0], 0.2, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(two_x.array()[1], 4.6, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Subtraction)
|
||||
{
|
||||
const auto x = DVec<2>{
|
||||
std::array<double,2>{ 0.1, 2.3 }
|
||||
};
|
||||
|
||||
const auto y = DVec<2>{
|
||||
std::array<double,2>{ 10.9, 8.7 }
|
||||
};
|
||||
|
||||
const auto x_minus_y = x - y;
|
||||
|
||||
BOOST_CHECK_CLOSE(x_minus_y.array()[0], -10.8, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(x_minus_y.array()[1], - 6.4, 1.0e-10);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Mult_By_Scalar)
|
||||
{
|
||||
// x *= a
|
||||
{
|
||||
auto x = DVec<2> {
|
||||
std::array<double,2>{ 0.1, 2.3 }
|
||||
};
|
||||
|
||||
x *= 5.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(x.array()[0], 0.5, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(x.array()[1], 11.5, 1.0e-10);
|
||||
}
|
||||
|
||||
// y <- x * a
|
||||
{
|
||||
const auto x = DVec<2> {
|
||||
std::array<double,2>{ 0.1, 2.3 }
|
||||
};
|
||||
|
||||
{
|
||||
const auto y = x * 5.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(y.array()[0], 0.5, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(y.array()[1], 11.5, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto y = 2.5 * x;
|
||||
|
||||
BOOST_CHECK_CLOSE(y.array()[0], 0.25, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(y.array()[1], 5.75, 1.0e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Divide_By_Scalar)
|
||||
{
|
||||
// x /= a
|
||||
{
|
||||
auto x = DVec<2> {
|
||||
std::array<double,2>{ 0.5, 11.5 }
|
||||
};
|
||||
|
||||
x /= 5.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(x.array()[0], 0.1, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(x.array()[1], 2.3, 1.0e-10);
|
||||
}
|
||||
|
||||
// y <- x / a
|
||||
{
|
||||
const auto x = DVec<2> {
|
||||
std::array<double,2>{ 0.25, 5.75 }
|
||||
};
|
||||
|
||||
const auto y = x / 2.5;
|
||||
|
||||
BOOST_CHECK_CLOSE(y.array()[0], 0.1, 1.0e-10);
|
||||
BOOST_CHECK_CLOSE(y.array()[1], 2.3, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
302
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclregionmapping.cpp
vendored
Normal file
302
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclregionmapping.cpp
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF ICT, Applied Mathematics.
|
||||
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_REGION_MAPPING
|
||||
|
||||
#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/ECLRegionMapping.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <initializer_list>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
std::vector<int> pvtnum(const std::size_t n = 10)
|
||||
{
|
||||
return std::vector<int>(n, 1);
|
||||
}
|
||||
|
||||
std::vector<int> satnum()
|
||||
{
|
||||
return std::vector<int> {
|
||||
1, 1, 1, 2, 2,
|
||||
3, 3, 3, 2, 2,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<int> linear(const std::vector<int>::size_type n)
|
||||
{
|
||||
auto i = std::vector<int>(n);
|
||||
|
||||
std::iota(std::begin(i), std::end(i), 0);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
template <class Coll1, class Coll2>
|
||||
void equal_collection(const Coll1& c1, const Coll2& c2)
|
||||
{
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(c1), std::end(c1),
|
||||
std::begin(c2), std::end(c2));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (Full_Region_Mapping)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Constructor_Failure)
|
||||
{
|
||||
using RM = ::Opm::ECLRegionMapping;
|
||||
|
||||
BOOST_CHECK_THROW(RM{ std::vector<int>{} },
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Single_Region)
|
||||
{
|
||||
const auto rm = ::Opm::ECLRegionMapping{ pvtnum(5) };
|
||||
|
||||
// All cells in single region => active regions == single ID.
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Defaulted index subset => Index vector [0 .. reg.size()-1]
|
||||
{
|
||||
const auto expect_ix = std::vector<int>{ 0, 1, 2, 3, 4, };
|
||||
equal_collection(rm.regionSubset(), expect_ix);
|
||||
}
|
||||
|
||||
// All cells in single region => region's subset of index vector is
|
||||
// [0 .. regionSubset().size()-1]
|
||||
{
|
||||
const auto expect_regix = std::vector<int>{ 0, 1, 2, 3, 4, };
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix);
|
||||
}
|
||||
|
||||
// Invalid region ID (outside configured subset) => logic_error.
|
||||
BOOST_CHECK_THROW(rm.getRegionIndices(1729),
|
||||
std::logic_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Multiple_Regions)
|
||||
{
|
||||
const auto rm = ::Opm::ECLRegionMapping{ satnum() };
|
||||
|
||||
// Active regions returned in sorted order
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1, 2, 3};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Defaulted index subset => Index vector [0 .. reg.size()-1]
|
||||
{
|
||||
const auto expect_ix = std::vector<int>{
|
||||
0, 1, 2, 3, 4,
|
||||
5, 6, 7, 8, 9,
|
||||
};
|
||||
equal_collection(rm.regionSubset(), expect_ix);
|
||||
}
|
||||
|
||||
// Cells in multiple regions => Must verify correct subset mappings.
|
||||
{
|
||||
const auto expect_regix_1 = std::vector<int>{ 0, 1, 2, };
|
||||
const auto expect_regix_2 = std::vector<int>{ 3, 4,
|
||||
8, 9 };
|
||||
const auto expect_regix_3 = std::vector<int>{ 5, 6, 7, };
|
||||
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix_1);
|
||||
equal_collection(rm.getRegionIndices(2), expect_regix_2);
|
||||
equal_collection(rm.getRegionIndices(3), expect_regix_3);
|
||||
}
|
||||
|
||||
// Invalid region ID (outside configured subset) => logic_error.
|
||||
BOOST_CHECK_THROW(rm.getRegionIndices(1701),
|
||||
std::logic_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
||||
// =====================================================================
|
||||
|
||||
BOOST_AUTO_TEST_SUITE (Subset_Region_Mapping)
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Single_Region_Subset)
|
||||
{
|
||||
const auto rm = ::Opm::ECLRegionMapping{
|
||||
pvtnum(5), std::vector<int>{ 1, 3, 4 }
|
||||
};
|
||||
|
||||
// All cells in single region => active regions == single ID.
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Explicit index subset => Index vector equal to this subset.
|
||||
{
|
||||
const auto expect_ix = std::vector<int>{ 1, 3, 4, };
|
||||
equal_collection(rm.regionSubset(), expect_ix);
|
||||
}
|
||||
|
||||
// All cells in single region => region's subset of index vector is
|
||||
// [0 .. regionSubset().size()-1]
|
||||
{
|
||||
const auto expect_regix = std::vector<int>{ 0, 1, 2, };
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Single_Region_MultiSampledSubset)
|
||||
{
|
||||
const auto cellIDs =
|
||||
std::vector<int>{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
const auto rm = ::Opm::ECLRegionMapping{
|
||||
pvtnum(5), cellIDs
|
||||
};
|
||||
|
||||
// All cells in single region => active regions == single ID.
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Explicit index subset => Index vector equal to this subset.
|
||||
{
|
||||
equal_collection(rm.regionSubset(), cellIDs);
|
||||
}
|
||||
|
||||
// All cells in single region => region's subset of index vector is
|
||||
// [0 .. regionSubset().size()-1]
|
||||
{
|
||||
const auto expect_regix = linear(cellIDs.size());
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Multi_Region_Subset)
|
||||
{
|
||||
const auto cellIDs = std::vector<int> {
|
||||
0, 1, /* 2, */ /* 3, */ 4,
|
||||
5, 6, /* 7, */ /* 8, */ 9,
|
||||
};
|
||||
|
||||
const auto rm = ::Opm::ECLRegionMapping{
|
||||
satnum(), cellIDs
|
||||
};
|
||||
|
||||
// Active regions returned in sorted order (cell subset covers all
|
||||
// regions).
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1, 2, 3};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Explicit index subset => Index vector must match this subset.
|
||||
{
|
||||
equal_collection(rm.regionSubset(), cellIDs);
|
||||
}
|
||||
|
||||
// Cells in multiple regions => Must verify correct subset mappings.
|
||||
{
|
||||
const auto expect_regix_1 = std::vector<int>{ 0, 1, };
|
||||
const auto expect_regix_2 = std::vector<int>{ 2,
|
||||
5, };
|
||||
const auto expect_regix_3 = std::vector<int>{ 3, 4, };
|
||||
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix_1);
|
||||
equal_collection(rm.getRegionIndices(2), expect_regix_2);
|
||||
equal_collection(rm.getRegionIndices(3), expect_regix_3);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Multi_Region_MultiSampledSubset)
|
||||
{
|
||||
const auto cellIDs = std::vector<int> {
|
||||
0, 0, 0, 0, 0, 0, // 0 .. 5
|
||||
9, 9, 8, 8, 3, 4, // 6 .. 11
|
||||
5, 5, 2, 2, 7, 7, // 12 .. 17
|
||||
0, 0, 0, 0, 0, 0, // 18 .. 23
|
||||
};
|
||||
|
||||
const auto rm = ::Opm::ECLRegionMapping{
|
||||
satnum(), cellIDs
|
||||
};
|
||||
|
||||
// Active regions returned in sorted order (cell subset covers all
|
||||
// regions).
|
||||
{
|
||||
const auto expect_actreg = std::vector<int>{1, 2, 3};
|
||||
equal_collection(rm.activeRegions(), expect_actreg);
|
||||
}
|
||||
|
||||
// Explicit index subset => Index vector must match this subset.
|
||||
{
|
||||
equal_collection(rm.regionSubset(), cellIDs);
|
||||
}
|
||||
|
||||
// Cells in multiple regions => Must verify correct subset mappings.
|
||||
{
|
||||
// Note: Index subsets appear in sorted order by construction.
|
||||
|
||||
const auto expect_regix_1 = std::vector<int>{
|
||||
0, 1, 2, 3, 4, 5, // 0 .. 5
|
||||
// 6 .. 11
|
||||
14, 15, // 12 .. 17
|
||||
18, 19, 20, 21, 22, 23, // 18 .. 23
|
||||
};
|
||||
|
||||
const auto expect_regix_2 = std::vector<int>{
|
||||
// 0 .. 5
|
||||
6, 7, 8, 9, 10, 11, // 6 .. 11
|
||||
// 12 .. 17
|
||||
// 18 .. 23
|
||||
};
|
||||
|
||||
const auto expect_regix_3 = std::vector<int>{
|
||||
// 0 .. 5
|
||||
// 6 .. 11
|
||||
12, 13, /* 14, 15 */ 16, 17 // 12 .. 17
|
||||
// 18 .. 23
|
||||
};
|
||||
|
||||
equal_collection(rm.getRegionIndices(1), expect_regix_1);
|
||||
equal_collection(rm.getRegionIndices(2), expect_regix_2);
|
||||
equal_collection(rm.getRegionIndices(3), expect_regix_3);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@
|
||||
|
||||
#define NVERBOSE
|
||||
|
||||
#define BOOST_TEST_MODULE TEST_ASSEMBLED_CONNECTIONS
|
||||
#define BOOST_TEST_MODULE TEST_UNIT_HANDLING
|
||||
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@ -56,6 +56,22 @@ BOOST_AUTO_TEST_CASE (Metric)
|
||||
{
|
||||
auto M = ::Opm::ECLUnits::createUnitSystem(1);
|
||||
|
||||
// Density (kilogram/cubic(metres))
|
||||
{
|
||||
const auto scale = M->density();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Depth (metres)
|
||||
{
|
||||
const auto scale = M->depth();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Pressure (bars)
|
||||
{
|
||||
const auto scale = M->pressure();
|
||||
@ -80,6 +96,22 @@ BOOST_AUTO_TEST_CASE (Metric)
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Gas (sm3)
|
||||
{
|
||||
const auto scale = M->surfaceVolumeGas();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Liquid (sm3)
|
||||
{
|
||||
const auto scale = M->surfaceVolumeLiquid();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Time (day)
|
||||
{
|
||||
const auto scale = M->time();
|
||||
@ -95,12 +127,52 @@ BOOST_AUTO_TEST_CASE (Metric)
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Viscosity (cP)
|
||||
{
|
||||
const auto scale = M->viscosity();
|
||||
const auto expect = 1.0e-3;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Sm^3/Sm^3)
|
||||
{
|
||||
const auto scale = M->dissolvedGasOilRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Vaporised Oil-Gas Ratio (Sm^3/Sm^3)
|
||||
{
|
||||
const auto scale = M->vaporisedOilGasRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Field)
|
||||
{
|
||||
auto F = ::Opm::ECLUnits::createUnitSystem(2);
|
||||
|
||||
// Density (pound/cubic(feet))
|
||||
{
|
||||
const auto scale = F->density();
|
||||
const auto expect = 1.601846337396014e+01;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Depth (feet)
|
||||
{
|
||||
const auto scale = F->depth();
|
||||
const auto expect = 0.3048; // 12 * 2.54
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Pressure (psi)
|
||||
{
|
||||
const auto scale = F->pressure();
|
||||
@ -125,6 +197,22 @@ BOOST_AUTO_TEST_CASE (Field)
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Gas (Mscf)
|
||||
{
|
||||
const auto scale = F->surfaceVolumeGas();
|
||||
const auto expect = 2.831684659200000e+01;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Liquid (stb)
|
||||
{
|
||||
const auto scale = F->surfaceVolumeLiquid();
|
||||
const auto expect = 1.589872949280001e-01;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Time (day)
|
||||
{
|
||||
const auto scale = F->time();
|
||||
@ -140,12 +228,52 @@ BOOST_AUTO_TEST_CASE (Field)
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Viscosity (cP)
|
||||
{
|
||||
const auto scale = F->viscosity();
|
||||
const auto expect = 1.0e-3;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Dissolved Gas-Oil Ratio (Mscf/stb)
|
||||
{
|
||||
const auto scale = F->dissolvedGasOilRat();
|
||||
const auto expect = 1.781076066790352e+02;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Vaporised Oil-Gas Ratio (stb/Mscf)
|
||||
{
|
||||
const auto scale = F->vaporisedOilGasRat();
|
||||
const auto expect = 5.614583333333335e-03;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (Lab)
|
||||
{
|
||||
auto L = ::Opm::ECLUnits::createUnitSystem(3);
|
||||
|
||||
// Density (gram/cubic(centi*meter))
|
||||
{
|
||||
const auto scale = L->density();
|
||||
const auto expect = 1.0e3;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Depth (cm)
|
||||
{
|
||||
const auto scale = L->depth();
|
||||
const auto expect = 0.01;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Pressure (atm)
|
||||
{
|
||||
const auto scale = L->pressure();
|
||||
@ -170,6 +298,22 @@ BOOST_AUTO_TEST_CASE (Lab)
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Gas (s(cm)^3)
|
||||
{
|
||||
const auto scale = L->surfaceVolumeGas();
|
||||
const auto expect = 1.0e-06;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Liquid (s(cm)^3)
|
||||
{
|
||||
const auto scale = L->surfaceVolumeLiquid();
|
||||
const auto expect = 1.0e-06;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Time (hour)
|
||||
{
|
||||
const auto scale = L->time();
|
||||
@ -185,12 +329,52 @@ BOOST_AUTO_TEST_CASE (Lab)
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Viscosity (cP)
|
||||
{
|
||||
const auto scale = L->viscosity();
|
||||
const auto expect = 1.0e-3;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Dissolved Gas-Oil Ratio (s(cm)^3/s(cm)^3)
|
||||
{
|
||||
const auto scale = L->dissolvedGasOilRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Vaporised Oil-Gas Ratio (s(cm)^3/s(cm)^3)
|
||||
{
|
||||
const auto scale = L->vaporisedOilGasRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE (PVT_M)
|
||||
{
|
||||
auto P = ::Opm::ECLUnits::createUnitSystem(4);
|
||||
|
||||
// Density (kilogram/cubic(meter))
|
||||
{
|
||||
const auto scale = P->density();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Depth (metres)
|
||||
{
|
||||
const auto scale = P->depth();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Pressure (atm)
|
||||
{
|
||||
const auto scale = P->pressure();
|
||||
@ -215,6 +399,22 @@ BOOST_AUTO_TEST_CASE (PVT_M)
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Gas (sm^3)
|
||||
{
|
||||
const auto scale = P->surfaceVolumeGas();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Surface Volume, Liquid (sm^3)
|
||||
{
|
||||
const auto scale = P->surfaceVolumeLiquid();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Time (day)
|
||||
{
|
||||
const auto scale = P->time();
|
||||
@ -230,6 +430,30 @@ BOOST_AUTO_TEST_CASE (PVT_M)
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Viscosity (cP)
|
||||
{
|
||||
const auto scale = P->viscosity();
|
||||
const auto expect = 1.0e-3;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Dissolved Gas-Oil Ratio (sm^3/sm^3)
|
||||
{
|
||||
const auto scale = P->dissolvedGasOilRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
|
||||
// Vaporised Oil-Gas Ratio (sm^3/sm^3)
|
||||
{
|
||||
const auto scale = P->vaporisedOilGasRat();
|
||||
const auto expect = 1.0;
|
||||
|
||||
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
||||
|
@ -14,46 +14,43 @@
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
# Mandatory call to project
|
||||
project(opm-flowdiagnostics CXX)
|
||||
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
# additional search modules
|
||||
set( OPM_COMMON_ROOT "" CACHE PATH "Root directory containing OPM related cmake modules")
|
||||
option(SIBLING_SEARCH "Search for other modules in sibling directories?" ON)
|
||||
|
||||
if(NOT OPM_COMMON_ROOT)
|
||||
find_package(opm-common QUIET)
|
||||
if(SIBLING_SEARCH AND NOT opm-common_DIR)
|
||||
# guess the sibling dir
|
||||
get_filename_component(_leaf_dir_name ${PROJECT_BINARY_DIR} NAME)
|
||||
get_filename_component(_parent_full_dir ${PROJECT_BINARY_DIR} DIRECTORY)
|
||||
get_filename_component(_parent_dir_name ${_parent_full_dir} NAME)
|
||||
#Try if <module-name>/<build-dir> is used
|
||||
get_filename_component(_modules_dir ${_parent_full_dir} DIRECTORY)
|
||||
if(IS_DIRECTORY ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
set(opm-common_DIR ${_modules_dir}/opm-common/${_leaf_dir_name})
|
||||
else()
|
||||
string(REPLACE ${PROJECT_NAME} opm-common _opm_common_leaf ${_leaf_dir_name})
|
||||
if(NOT _leaf_dir_name STREQUAL _opm_common_leaf
|
||||
AND IS_DIRECTORY ${_parent_full_dir}/${_opm_common_leaf})
|
||||
# We are using build directories named <prefix><module-name><postfix>
|
||||
set(opm-common_DIR ${_parent_full_dir}/${_opm_common_leaf})
|
||||
elseif(IS_DIRECTORY ${_parent_full_dir}/opm-common)
|
||||
# All modules are in a common build dir
|
||||
set(opm-common_DIR "${_parent_full_dir}/opm-common}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(opm-common_DIR AND NOT IS_DIRECTORY ${opm-common_DIR})
|
||||
message(WARNING "Value ${opm-common_DIR} passed to variable"
|
||||
" opm-common_DIR is not a directory")
|
||||
endif()
|
||||
|
||||
if (opm-common_FOUND)
|
||||
include(OpmInit)
|
||||
else()
|
||||
unset(opm-common_FOUND)
|
||||
find_package(opm-common REQUIRED)
|
||||
|
||||
if (NOT OPM_COMMON_ROOT AND SIBLING_SEARCH)
|
||||
set(OPM_COMMON_ROOT ${PROJECT_SOURCE_DIR}/../opm-common)
|
||||
endif()
|
||||
if (OPM_COMMON_ROOT)
|
||||
list( APPEND CMAKE_MODULE_PATH "${OPM_COMMON_ROOT}/cmake/Modules")
|
||||
include (OpmInit OPTIONAL RESULT_VARIABLE OPM_INIT)
|
||||
set( OPM_MACROS_ROOT ${OPM_COMMON_ROOT} )
|
||||
endif()
|
||||
|
||||
if (NOT OPM_INIT)
|
||||
message( "" )
|
||||
message( " /---------------------------------------------------------------------------------\\")
|
||||
message( " | Could not locate the opm build macros. The opm build macros |")
|
||||
message( " | are in a separate repository - instructions to proceed: |")
|
||||
message( " | |")
|
||||
message( " | 1. Clone the repository: git clone git@github.com:OPM/opm-common.git |")
|
||||
message( " | |")
|
||||
message( " | 2. Run cmake in the current project with -DOPM_COMMON_ROOT=<path>/opm-common |")
|
||||
message( " | |")
|
||||
message( " \\---------------------------------------------------------------------------------/")
|
||||
message( "" )
|
||||
message( FATAL_ERROR "Could not find OPM Macros")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
include(OpmInit)
|
||||
|
||||
# not the same location as most of the other projects; this hook overrides
|
||||
macro (dir_hook)
|
||||
|
@ -226,23 +226,19 @@ namespace FlowDiagnostics
|
||||
|
||||
// Helper for injectorProducerPairFlux().
|
||||
double pairFlux(const CellSetValues& tracer,
|
||||
const CellSet& well_cells,
|
||||
const CellSetValues& inflow_flux,
|
||||
const bool require_inflow)
|
||||
{
|
||||
double flux = 0.0;
|
||||
for (const int cell : well_cells) {
|
||||
for (const auto inflow : inflow_flux) {
|
||||
const int cell = inflow.first;
|
||||
const auto tracer_iter = tracer.find(cell);
|
||||
if (tracer_iter != tracer.end()) {
|
||||
// Tracer present in cell.
|
||||
const auto source_iter = inflow_flux.find(cell);
|
||||
if (source_iter != inflow_flux.end()) {
|
||||
// Cell has source term.
|
||||
const double source = source_iter->second;
|
||||
if ((source > 0.0) == require_inflow) {
|
||||
// Source term has correct sign.
|
||||
flux += source * tracer_iter->second;
|
||||
}
|
||||
const double source = inflow.second;
|
||||
if ((source > 0.0) == require_inflow) {
|
||||
// Source term has correct sign.
|
||||
flux += source * tracer_iter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,14 +264,22 @@ namespace FlowDiagnostics
|
||||
std::pair<double, double>
|
||||
injectorProducerPairFlux(const Toolbox::Forward& injector_solution,
|
||||
const Toolbox::Reverse& producer_solution,
|
||||
const CellSet& injector_cells,
|
||||
const CellSet& producer_cells,
|
||||
const CellSetValues& inflow_flux)
|
||||
const CellSetID& injector,
|
||||
const CellSetID& producer,
|
||||
const std::map<CellSetID, CellSetValues>& inflow_flux)
|
||||
{
|
||||
const auto& inj_tracer = injector_solution.fd.concentration(injector_cells.id());
|
||||
const auto& prod_tracer = producer_solution.fd.concentration(producer_cells.id());
|
||||
const double inj_flux = pairFlux(prod_tracer, injector_cells, inflow_flux, true);
|
||||
const double prod_flux = pairFlux(inj_tracer, producer_cells, inflow_flux, false);
|
||||
const auto& inj_tracer = injector_solution.fd.concentration(injector);
|
||||
const auto& prod_tracer = producer_solution.fd.concentration(producer);
|
||||
const auto inj_set_iter = inflow_flux.find(injector);
|
||||
if (inj_set_iter == inflow_flux.end()) {
|
||||
throw std::runtime_error("injectorProducerPairFlux(): Could not find requeste injector set in inflow fluxes.");
|
||||
}
|
||||
const auto prod_set_iter = inflow_flux.find(producer);
|
||||
if (prod_set_iter == inflow_flux.end()) {
|
||||
throw std::runtime_error("injectorProducerPairFlux(): Could not find requested producer set in inflow fluxes.");
|
||||
}
|
||||
const double inj_flux = pairFlux(prod_tracer, inj_set_iter->second, true);
|
||||
const double prod_flux = pairFlux(inj_tracer, prod_set_iter->second, false);
|
||||
return { inj_flux, prod_flux };
|
||||
}
|
||||
|
||||
|
@ -110,9 +110,9 @@ namespace FlowDiagnostics
|
||||
std::pair<double, double>
|
||||
injectorProducerPairFlux(const Toolbox::Forward& injector_solution,
|
||||
const Toolbox::Reverse& producer_solution,
|
||||
const CellSet& injector_cells,
|
||||
const CellSet& producer_cells,
|
||||
const CellSetValues& inflow_flux);
|
||||
const CellSetID& injector,
|
||||
const CellSetID& producer,
|
||||
const std::map<CellSetID, CellSetValues>& inflow_flux);
|
||||
|
||||
|
||||
} // namespace FlowDiagnostics
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
|
||||
void assignPoreVolume(const std::vector<double>& pvol);
|
||||
void assignConnectionFlux(const ConnectionValues& flux);
|
||||
void assignInflowFlux(const CellSetValues& inflow_flux);
|
||||
void assignInflowFlux(const std::map<CellSetID, CellSetValues>& inflow_flux);
|
||||
|
||||
Forward injDiag (const std::vector<CellSet>& start_sets);
|
||||
Reverse prodDiag(const std::vector<CellSet>& start_sets);
|
||||
@ -66,6 +66,8 @@ private:
|
||||
|
||||
std::vector<double> pvol_;
|
||||
ConnectionValues flux_;
|
||||
std::map<CellSetID, CellSetValues> inj_flux_by_id_;
|
||||
std::map<CellSetID, CellSetValues> prod_flux_by_id_;
|
||||
CellSetValues only_inflow_flux_;
|
||||
CellSetValues only_outflow_flux_;
|
||||
|
||||
@ -109,15 +111,20 @@ Toolbox::Impl::assignConnectionFlux(const ConnectionValues& flux)
|
||||
}
|
||||
|
||||
void
|
||||
Toolbox::Impl::assignInflowFlux(const CellSetValues& inflow_flux)
|
||||
Toolbox::Impl::assignInflowFlux(const std::map<CellSetID, CellSetValues>& inflow_flux)
|
||||
{
|
||||
only_inflow_flux_.clear();
|
||||
only_outflow_flux_.clear();
|
||||
for (const auto& data : inflow_flux) {
|
||||
if (data.second > 0.0) {
|
||||
only_inflow_flux_[data.first] = data.second;
|
||||
} else if (data.second < 0.0) {
|
||||
only_outflow_flux_[data.first] = -data.second;
|
||||
for (const auto& inflow_set : inflow_flux) {
|
||||
const CellSetID& id = inflow_set.first;
|
||||
for (const auto& data : inflow_set.second) {
|
||||
if (data.second > 0.0) {
|
||||
only_inflow_flux_[data.first] += data.second;
|
||||
inj_flux_by_id_[id].insert(data);
|
||||
} else if (data.second < 0.0) {
|
||||
only_outflow_flux_[data.first] += -data.second;
|
||||
prod_flux_by_id_[id].insert(std::make_pair(data.first, -data.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,6 +139,9 @@ Toolbox::Impl::injDiag(const std::vector<CellSet>& start_sets)
|
||||
|
||||
// Check that start sets are valid.
|
||||
for (const auto& start : start_sets) {
|
||||
if (inj_flux_by_id_.find(start.id()) == inj_flux_by_id_.end()) {
|
||||
throw std::runtime_error("Start set ID not present in data passed to assignInflowFlux().");
|
||||
}
|
||||
for (const int cell : start) {
|
||||
if (only_inflow_flux_.count(cell) != 1 || only_outflow_flux_.count(cell) != 0) {
|
||||
throw std::runtime_error("Start set inconsistent with assignInflowFlux()-given values");
|
||||
@ -151,7 +161,7 @@ Toolbox::Impl::injDiag(const std::vector<CellSet>& start_sets)
|
||||
sol.assignGlobalToF(solver.solveGlobal());
|
||||
|
||||
for (const auto& start : start_sets) {
|
||||
auto solution = solver.solveLocal(start);
|
||||
auto solution = solver.solveLocal(inj_flux_by_id_[start.id()]);
|
||||
sol.assign(start.id(), ToF{ solution.tof });
|
||||
sol.assign(start.id(), Conc{ solution.concentration });
|
||||
}
|
||||
@ -169,6 +179,9 @@ Toolbox::Impl::prodDiag(const std::vector<CellSet>& start_sets)
|
||||
|
||||
// Check that start sets are valid.
|
||||
for (const auto& start : start_sets) {
|
||||
if (prod_flux_by_id_.find(start.id()) == prod_flux_by_id_.end()) {
|
||||
throw std::runtime_error("Start set ID not present in data passed to assignInflowFlux().");
|
||||
}
|
||||
for (const int cell : start) {
|
||||
if (only_inflow_flux_.count(cell) != 0 || only_outflow_flux_.count(cell) != 1) {
|
||||
throw std::runtime_error("Start set inconsistent with assignInflowFlux()-given values");
|
||||
@ -188,7 +201,7 @@ Toolbox::Impl::prodDiag(const std::vector<CellSet>& start_sets)
|
||||
sol.assignGlobalToF(solver.solveGlobal());
|
||||
|
||||
for (const auto& start : start_sets) {
|
||||
auto solution = solver.solveLocal(start);
|
||||
auto solution = solver.solveLocal(prod_flux_by_id_[start.id()]);
|
||||
sol.assign(start.id(), ToF{ solution.tof });
|
||||
sol.assign(start.id(), Conc{ solution.concentration });
|
||||
}
|
||||
@ -271,7 +284,7 @@ Toolbox::assignConnectionFlux(const ConnectionValues& flux)
|
||||
}
|
||||
|
||||
void
|
||||
Toolbox::assignInflowFlux(const CellSetValues& inflow_flux)
|
||||
Toolbox::assignInflowFlux(const std::map<CellSetID, CellSetValues>& inflow_flux)
|
||||
{
|
||||
pImpl_->assignInflowFlux(inflow_flux);
|
||||
}
|
||||
|
@ -62,7 +62,9 @@ namespace FlowDiagnostics
|
||||
/// Inflow fluxes (injection) should be positive, outflow
|
||||
/// fluxes (production) should be negative, both should be
|
||||
/// given in the inflow_flux argument passed to this method.
|
||||
void assignInflowFlux(const CellSetValues& inflow_flux);
|
||||
/// Values from a single well should typically be associated with
|
||||
/// a single CellSetID and be a single CellSetValues object.
|
||||
void assignInflowFlux(const std::map<CellSetID, CellSetValues>& inflow_flux);
|
||||
|
||||
struct Forward
|
||||
{
|
||||
|
@ -100,6 +100,7 @@ namespace FlowDiagnostics
|
||||
, influx_(std::move(inout.influx))
|
||||
, outflux_(std::move(inout.outflux))
|
||||
, source_term_(expandSparse(pore_volumes.size(), source_inflow))
|
||||
, local_source_term_(pore_volumes.size(), 0.0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -112,10 +113,12 @@ namespace FlowDiagnostics
|
||||
// Reset solver variables and set source terms.
|
||||
prepareForSolve();
|
||||
setupStartArrayFromSource();
|
||||
local_source_term_ = source_term_;
|
||||
|
||||
// Compute topological ordering and solve.
|
||||
computeOrdering();
|
||||
solve();
|
||||
std::fill(local_source_term_.begin(), local_source_term_.end(), 0.0);
|
||||
|
||||
// Return computed time-of-flight.
|
||||
return tof_;
|
||||
@ -125,7 +128,7 @@ namespace FlowDiagnostics
|
||||
|
||||
|
||||
|
||||
TracerTofSolver::LocalSolution TracerTofSolver::solveLocal(const CellSet& startset)
|
||||
TracerTofSolver::LocalSolution TracerTofSolver::solveLocal(const CellSetValues& startset)
|
||||
{
|
||||
// Reset solver variables and set source terms.
|
||||
prepareForSolve();
|
||||
@ -133,7 +136,9 @@ namespace FlowDiagnostics
|
||||
|
||||
// Compute local topological ordering and solve.
|
||||
computeLocalOrdering(startset);
|
||||
setupLocalSource(startset);
|
||||
solve();
|
||||
cleanupLocalSource(startset);
|
||||
|
||||
// Return computed time-of-flight.
|
||||
CellSetValues local_tof;
|
||||
@ -143,6 +148,10 @@ namespace FlowDiagnostics
|
||||
const int cell = sequence_[element];
|
||||
local_tof[cell] = tof_[cell];
|
||||
local_tracer[cell] = tracer_[cell];
|
||||
// Verify that tracer values are greater than zero
|
||||
if (tracer_[cell] <= 0.0) {
|
||||
throw std::logic_error("Tracer is zero in non-isolated cell.");
|
||||
}
|
||||
}
|
||||
return LocalSolution{ std::move(local_tof), std::move(local_tracer) };
|
||||
}
|
||||
@ -170,9 +179,10 @@ namespace FlowDiagnostics
|
||||
|
||||
|
||||
|
||||
void TracerTofSolver::setupStartArray(const CellSet& startset)
|
||||
void TracerTofSolver::setupStartArray(const CellSetValues& startset)
|
||||
{
|
||||
for (const int cell : startset) {
|
||||
for (const auto& startpoint : startset) {
|
||||
const int cell = startpoint.first;
|
||||
is_start_[cell] = 1;
|
||||
}
|
||||
}
|
||||
@ -227,10 +237,14 @@ namespace FlowDiagnostics
|
||||
|
||||
|
||||
|
||||
void TracerTofSolver::computeLocalOrdering(const CellSet& startset)
|
||||
void TracerTofSolver::computeLocalOrdering(const CellSetValues& startset)
|
||||
{
|
||||
// Extract start cells.
|
||||
std::vector<int> startcells(startset.begin(), startset.end());
|
||||
std::vector<int> startcells;
|
||||
startcells.reserve(startset.size());
|
||||
for (const auto& startpoint : startset) {
|
||||
startcells.push_back(startpoint.first);
|
||||
}
|
||||
|
||||
// Compute reverse topological ordering.
|
||||
const size_t num_cells = pv_.size();
|
||||
@ -251,7 +265,7 @@ namespace FlowDiagnostics
|
||||
}
|
||||
|
||||
// Extract data from solution.
|
||||
sequence_.resize(num_cells); // For local solutions this is the upper limit of the size. TODO: use exact size.
|
||||
sequence_.resize(num_cells); // For local solutions this is the upper limit of the size. Will give proper size afterwards.
|
||||
const int num_comp = tarjan_get_numcomponents(result.get());
|
||||
component_starts_.resize(num_comp + 1);
|
||||
component_starts_[0] = 0;
|
||||
@ -260,6 +274,32 @@ namespace FlowDiagnostics
|
||||
std::copy(tc.vertex, tc.vertex + tc.size, sequence_.begin() + component_starts_[comp]);
|
||||
component_starts_[comp + 1] = component_starts_[comp] + tc.size;
|
||||
}
|
||||
sequence_.resize(component_starts_.back());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TracerTofSolver::setupLocalSource(const CellSetValues& startset)
|
||||
{
|
||||
for (const auto& startpoint : startset) {
|
||||
if (startpoint.second < 0.0) {
|
||||
throw std::logic_error("Start set for local solve has negative source value.");
|
||||
}
|
||||
local_source_term_[startpoint.first] = startpoint.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TracerTofSolver::cleanupLocalSource(const CellSetValues& startset)
|
||||
{
|
||||
for (const auto& startpoint : startset) {
|
||||
local_source_term_[startpoint.first] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -292,25 +332,12 @@ namespace FlowDiagnostics
|
||||
void TracerTofSolver::solveSingleCell(const int cell)
|
||||
{
|
||||
// Compute influx (divisor of tof expression).
|
||||
double source = source_term_[cell]; // Initial tof for well cell equal to fill time.
|
||||
double source = source_term_[cell];
|
||||
if (source == 0.0 && is_start_[cell]) {
|
||||
source = std::numeric_limits<double>::infinity(); // Gives 0 tof in start cell.
|
||||
}
|
||||
const double total_influx = influx_[cell] + source;
|
||||
|
||||
// Cap time-of-flight if time to fill cell is greater than
|
||||
// max_tof_. Note that cells may still have larger than
|
||||
// max_tof_ after solveSingleCell() when including upwind
|
||||
// contributions, and those in turn can affect cells
|
||||
// downstream (so capping in this method will not produce the
|
||||
// same result). All tofs will finally be capped in solve() as
|
||||
// a post-process. The reason for the somewhat convoluted
|
||||
// behaviour is to match existing MRST results.
|
||||
if (total_influx < pv_[cell] / max_tof_) {
|
||||
tof_[cell] = max_tof_;
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute upwind contribution.
|
||||
double upwind_tof_contrib = 0.0;
|
||||
double upwind_tracer_contrib = 0.0;
|
||||
@ -325,21 +352,41 @@ namespace FlowDiagnostics
|
||||
// should get a contribution from the local source term
|
||||
// (which is then considered to be containing the
|
||||
// currently considered tracer).
|
||||
//
|
||||
// Start cells should therefore never have a zero source
|
||||
// term. This may need to change in the future to support
|
||||
// local tracing from arbitrary locations.
|
||||
upwind_tracer_contrib += source;
|
||||
upwind_tracer_contrib += local_source_term_[cell];
|
||||
}
|
||||
|
||||
// Compute time-of-flight and tracer.
|
||||
tracer_[cell] = upwind_tracer_contrib / total_influx;
|
||||
// The following should be true if Tarjan was done correctly.
|
||||
// Note that global Tarjan will also visit isolated cells,
|
||||
// so it is possible to have exactly zero.
|
||||
assert(total_influx >= 0.0);
|
||||
assert(upwind_tracer_contrib >= 0.0);
|
||||
|
||||
// Compute tracer.
|
||||
if (total_influx == 0.0) {
|
||||
assert(upwind_tracer_contrib == 0.0);
|
||||
// Isolated cell.
|
||||
tracer_[cell] = 0.0;
|
||||
} else {
|
||||
tracer_[cell] = upwind_tracer_contrib / total_influx;
|
||||
}
|
||||
|
||||
// Compute time-of-flight.
|
||||
if (tracer_[cell] > 0.0) {
|
||||
tof_[cell] = (pv_[cell]*tracer_[cell] + upwind_tof_contrib)
|
||||
/ (total_influx * tracer_[cell]);
|
||||
} else {
|
||||
tof_[cell] = max_tof_;
|
||||
}
|
||||
else {
|
||||
|
||||
// Cap time-of-flight if time to fill cell is greater than
|
||||
// max_tof_. Note that cells may still have larger than
|
||||
// max_tof_ after solveSingleCell() when including upwind
|
||||
// contributions, and those in turn can affect cells
|
||||
// downstream (so capping in this method will not produce the
|
||||
// same result). All tofs will finally be capped in solve() as
|
||||
// a post-process. The reason for the somewhat convoluted
|
||||
// behaviour is to match existing MRST results.
|
||||
if (total_influx < pv_[cell] / max_tof_) {
|
||||
tof_[cell] = max_tof_;
|
||||
}
|
||||
}
|
||||
@ -354,17 +401,23 @@ namespace FlowDiagnostics
|
||||
++num_multicell_;
|
||||
max_size_multicell_ = std::max(max_size_multicell_, num_cells);
|
||||
|
||||
// Using a Gauss-Seidel approach.
|
||||
double max_delta = 1e100;
|
||||
// Using a simple Gauss-Seidel approach.
|
||||
double max_tof_delta = 1e100;
|
||||
double max_tracer_delta = 1e100;
|
||||
int num_iter = 0;
|
||||
while (max_delta > gauss_seidel_tol_) {
|
||||
max_delta = 0.0;
|
||||
while (max_tof_delta > gauss_seidel_tof_tol_ || max_tracer_delta > gauss_seidel_tracer_tol_) {
|
||||
max_tof_delta = 0.0;
|
||||
max_tracer_delta = 0.0;
|
||||
++num_iter;
|
||||
for (int ci = 0; ci < num_cells; ++ci) {
|
||||
const int cell = cells[ci];
|
||||
const double tof_before = tof_[cell];
|
||||
const double tracer_before = tracer_[cell];
|
||||
solveSingleCell(cell);
|
||||
max_delta = std::max(max_delta, std::fabs(tof_[cell] - tof_before));
|
||||
max_tof_delta = std::max(max_tof_delta, std::fabs(tof_[cell] - tof_before));
|
||||
const bool zero_change = ((tracer_before == 0.0) != (tracer_[cell] == 0.0));
|
||||
const double tracer_change = zero_change ? 1.0 : std::fabs(tracer_[cell] - tracer_before);
|
||||
max_tracer_delta = std::max(max_tracer_delta, tracer_change);
|
||||
}
|
||||
}
|
||||
max_iter_multicell_ = std::max(max_iter_multicell_, num_iter);
|
||||
|
@ -48,9 +48,11 @@ namespace FlowDiagnostics
|
||||
{
|
||||
public:
|
||||
/// Initialize solver with a given flow graph (a weighted,
|
||||
/// directed asyclic graph) containing the out-fluxes from
|
||||
/// directed acyclic graph) containing the out-fluxes from
|
||||
/// each cell, the reverse graph (with in-fluxes from each
|
||||
/// cell), pore volumes and inflow sources (positive).
|
||||
/// cell), pore volumes and all (positive) inflow sources. If
|
||||
/// there are multiple inflow sources for a single cell, they
|
||||
/// should be added before passing to this function.
|
||||
TracerTofSolver(const AssembledConnections& graph,
|
||||
const AssembledConnections& reverse_graph,
|
||||
const std::vector<double>& pore_volumes,
|
||||
@ -69,10 +71,11 @@ namespace FlowDiagnostics
|
||||
|
||||
/// Compute a local solution tracer and time-of-flight solution.
|
||||
///
|
||||
/// Local means that only cells downwind from he startset are considered.
|
||||
/// The solution is therefore potentially sparse.
|
||||
/// TODO: not implemented!
|
||||
LocalSolution solveLocal(const CellSet& startset);
|
||||
/// Local means that only cells downwind from he startset are
|
||||
/// considered. The solution is therefore potentially sparse.
|
||||
/// The startset must contain the (nonnegative) source term for
|
||||
/// each start cell.
|
||||
LocalSolution solveLocal(const CellSetValues& startset);
|
||||
|
||||
private:
|
||||
|
||||
@ -84,6 +87,7 @@ namespace FlowDiagnostics
|
||||
const std::vector<double> influx_;
|
||||
const std::vector<double> outflux_;
|
||||
std::vector<double> source_term_;
|
||||
std::vector<double> local_source_term_;
|
||||
std::vector<char> is_start_; // char to avoid the nasty vector<bool> specialization
|
||||
std::vector<int> sequence_;
|
||||
std::vector<int> component_starts_;
|
||||
@ -92,8 +96,9 @@ namespace FlowDiagnostics
|
||||
int num_multicell_ = 0;
|
||||
int max_size_multicell_ = 0;
|
||||
int max_iter_multicell_ = 0;
|
||||
const double gauss_seidel_tol_ = 1e-3;
|
||||
const double max_tof_ = 200.0 * 365.0 * 24.0 * 60.0 * 60.0; // 200 years.
|
||||
const double gauss_seidel_tof_tol_ = max_tof_ / 1e12;
|
||||
const double gauss_seidel_tracer_tol_ = 1e-9;
|
||||
|
||||
// -------------- Private helper class --------------
|
||||
|
||||
@ -109,13 +114,17 @@ namespace FlowDiagnostics
|
||||
|
||||
void prepareForSolve();
|
||||
|
||||
void setupStartArray(const CellSet& startset);
|
||||
void setupStartArray(const CellSetValues& startset);
|
||||
|
||||
void setupStartArrayFromSource();
|
||||
|
||||
void computeOrdering();
|
||||
|
||||
void computeLocalOrdering(const CellSet& startset);
|
||||
void computeLocalOrdering(const CellSetValues& startset);
|
||||
|
||||
void setupLocalSource(const CellSetValues& startset);
|
||||
|
||||
void cleanupLocalSource(const CellSetValues& startset);
|
||||
|
||||
void solve();
|
||||
|
||||
|
@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE (OneDimCase)
|
||||
const auto& flux = cas.flux();
|
||||
|
||||
// Create well in/out flows.
|
||||
CellSetValues wellflow = { {0, 0.3}, {4, -0.3} };
|
||||
std::map<CellSetID, CellSetValues> wellflow = { { CellSetID("I-1"), {{0, 0.3}} }, { CellSetID("P-1"), {{4, -0.3}} } };
|
||||
|
||||
Toolbox diagTool(graph);
|
||||
diagTool.assignPoreVolume(pv);
|
||||
@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE (OneDimCase)
|
||||
const double vol12 = injectorProducerPairVolume(fwd, rev, pv, CellSetID("I-1"), CellSetID("P-1"));
|
||||
BOOST_CHECK_CLOSE(vol12, expectedVol12, 1e-10);
|
||||
|
||||
const auto pairflux = injectorProducerPairFlux(fwd, rev, inje[0], prod[0], wellflow);
|
||||
const auto pairflux = injectorProducerPairFlux(fwd, rev, CellSetID("I-1"), CellSetID("P-1"), wellflow);
|
||||
BOOST_CHECK_CLOSE(pairflux.first, 0.3, 1e-10);
|
||||
BOOST_CHECK_CLOSE(pairflux.second, -0.3, 1e-10);
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE (OneDimCase)
|
||||
}
|
||||
|
||||
// Create well in/out flows.
|
||||
CellSetValues wellflow = { {0, 0.3}, {4, -0.3} };
|
||||
std::map<CellSetID, CellSetValues> wellflow = { { CellSetID("I-1"), {{0, 0.3}} }, { CellSetID("P-1"), {{4, -0.3}} } };
|
||||
|
||||
Toolbox diagTool(graph);
|
||||
diagTool.assignPoreVolume(cas.poreVolume());
|
||||
@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE (OneDimCase)
|
||||
const int first_cell = 0;
|
||||
const int last_cell = cas.connectivity().numCells() - 1;
|
||||
auto start = std::vector<CellSet>{ CellSet(CellSetID("I-1"), {first_cell}),
|
||||
CellSet(CellSetID("I-2"), {last_cell}) };
|
||||
CellSet(CellSetID("P-1"), {last_cell}) };
|
||||
BOOST_CHECK_THROW(diagTool.computeInjectionDiagnostics(start), std::runtime_error);
|
||||
BOOST_CHECK_THROW(diagTool.computeProductionDiagnostics(start), std::runtime_error);
|
||||
}
|
||||
@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE (OneDimCase)
|
||||
const int first_cell = 0;
|
||||
const int last_cell = cas.connectivity().numCells() - 1;
|
||||
auto start_fwd = std::vector<CellSet>{ CellSet(CellSetID("I-1"), {first_cell}) };
|
||||
auto start_rev = std::vector<CellSet>{ CellSet(CellSetID("I-2"), {last_cell}) };
|
||||
auto start_rev = std::vector<CellSet>{ CellSet(CellSetID("P-1"), {last_cell}) };
|
||||
const auto fwd = diagTool.computeInjectionDiagnostics(start_fwd);
|
||||
const auto rev = diagTool.computeProductionDiagnostics(start_rev);
|
||||
|
||||
@ -426,7 +426,9 @@ BOOST_AUTO_TEST_CASE (LocalSolutions)
|
||||
flux(C{6}, P{0}) = -0.6;
|
||||
|
||||
// Create well in/out flows.
|
||||
CellSetValues wellflow = { {0, 0.3}, {3, 0.3}, {2, -0.6} };
|
||||
std::map<CellSetID, CellSetValues> wellflow = { { CellSetID("I-1"), {{0, 0.3}} },
|
||||
{ CellSetID("I-2"), {{3, 0.3}} },
|
||||
{ CellSetID("P-1"), {{2, -0.6}} } };
|
||||
|
||||
Toolbox diagTool(graph);
|
||||
diagTool.assignPoreVolume(cas.poreVolume());
|
||||
@ -624,7 +626,9 @@ BOOST_AUTO_TEST_CASE (LocalSolutionsWithMidflowSource)
|
||||
flux(C{1}, P{0}) = 0.6;
|
||||
|
||||
// Create well in/out flows.
|
||||
CellSetValues wellflow = { {0, 0.3}, {1, 0.3}, {2, -0.6} };
|
||||
std::map<CellSetID, CellSetValues> wellflow = { { CellSetID("I-1"), {{0, 0.3}} },
|
||||
{ CellSetID("I-2"), {{1, 0.3}} },
|
||||
{ CellSetID("P-1"), {{2, -0.6}} } };
|
||||
|
||||
Toolbox diagTool(graph);
|
||||
diagTool.assignPoreVolume(cas.poreVolume());
|
||||
@ -744,4 +748,206 @@ BOOST_AUTO_TEST_CASE (LocalSolutionsWithMidflowSource)
|
||||
|
||||
|
||||
|
||||
|
||||
// Arrows indicate a flux of 0.3, O is a source of 0.3
|
||||
// and X is a sink of 0.3 (each cell has a pore volume of 0.3).
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | O O -> XX |
|
||||
// | "I-1" "I-2" -> "P-1" |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Cell indices:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0 | 1 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected global injection TOF:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0.5 | 1.0 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected global production TOF:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 1.0 | 0.5 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected local tracer I-1:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0.5 | 0.5 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected local tracer I-2:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0.5 | 0.5 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected local tof I-1:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0.5 | 1.0 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
// Expected local tof I-2:
|
||||
// -----------------------------
|
||||
// | | |
|
||||
// | | |
|
||||
// | 0.5 | 1.0 |
|
||||
// | | |
|
||||
// -----------------------------
|
||||
BOOST_AUTO_TEST_CASE (LocalSolutionsPerfSameCell)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("============== Test: LocalSolutionsPerfSameCell ==============");
|
||||
using namespace Opm::FlowDiagnostics;
|
||||
|
||||
const auto cas = Setup(2, 1);
|
||||
const auto& graph = cas.connectivity();
|
||||
|
||||
// Create fluxes.
|
||||
ConnectionValues flux(ConnectionValues::NumConnections{ graph.numConnections() },
|
||||
ConnectionValues::NumPhases { 1 });
|
||||
const size_t nconn = cas.connectivity().numConnections();
|
||||
for (size_t conn = 0; conn < nconn; ++conn) {
|
||||
BOOST_TEST_MESSAGE("Connection " << conn << " connects cells "
|
||||
<< graph.connection(conn).first << " and "
|
||||
<< graph.connection(conn).second);
|
||||
}
|
||||
using C = ConnectionValues::ConnID;
|
||||
using P = ConnectionValues::PhaseID;
|
||||
flux(C{0}, P{0}) = 0.6;
|
||||
|
||||
// Create well in/out flows.
|
||||
std::map<CellSetID, CellSetValues> wellflow = { { CellSetID("I-1"), {{0, 0.3}} },
|
||||
{ CellSetID("I-2"), {{0, 0.3}} },
|
||||
{ CellSetID("P-1"), {{1, -0.6}} } };
|
||||
|
||||
Toolbox diagTool(graph);
|
||||
diagTool.assignPoreVolume(cas.poreVolume());
|
||||
diagTool.assignConnectionFlux(flux);
|
||||
diagTool.assignInflowFlux(wellflow);
|
||||
|
||||
auto injstart = std::vector<CellSet>{ CellSet(CellSetID("I-1"), {0}),
|
||||
CellSet(CellSetID("I-2"), {0}) };
|
||||
auto prdstart = std::vector<CellSet>{ CellSet(CellSetID("P-1"), {1}) };
|
||||
|
||||
const auto fwd = diagTool.computeInjectionDiagnostics(injstart);
|
||||
const auto rev = diagTool.computeProductionDiagnostics(prdstart);
|
||||
// Global ToF field (accumulated from all injectors)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== Global injector ToF");
|
||||
const auto tof = fwd.fd.timeOfFlight();
|
||||
BOOST_REQUIRE_EQUAL(tof.size(), cas.connectivity().numCells());
|
||||
std::vector<double> expected = { 0.5, 1.0 };
|
||||
check_is_close(tof, expected);
|
||||
}
|
||||
|
||||
// Global ToF field (accumulated from all producers)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== Global producer ToF");
|
||||
const auto tof = rev.fd.timeOfFlight();
|
||||
BOOST_REQUIRE_EQUAL(tof.size(), cas.connectivity().numCells());
|
||||
std::vector<double> expected = { 1.0, 0.5 };
|
||||
check_is_close(tof, expected);
|
||||
}
|
||||
|
||||
// Verify set of start points.
|
||||
{
|
||||
using VCS = std::vector<Opm::FlowDiagnostics::CellSet>;
|
||||
using VCSI = std::vector<Opm::FlowDiagnostics::CellSetID>;
|
||||
using P = std::pair<VCS, VCSI>;
|
||||
std::vector<P> pairs { P{ injstart, fwd.fd.startPoints() }, P{ prdstart, rev.fd.startPoints() } };
|
||||
for (const auto& p : pairs) {
|
||||
const auto& s1 = p.first;
|
||||
const auto& s2 = p.second;
|
||||
BOOST_CHECK_EQUAL(s1.size(), s2.size());
|
||||
for (const auto& pt : s2) {
|
||||
// ID of 'pt' *MUST* be in set of identified start points.
|
||||
auto pos = std::find_if(s1.begin(), s1.end(),
|
||||
[&pt](const CellSet& s)
|
||||
{
|
||||
return s.id().to_string() == pt.to_string();
|
||||
});
|
||||
BOOST_CHECK(pos != s1.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Local I-1 tracer concentration.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== I-1 tracer");
|
||||
const auto conc = fwd.fd.concentration(CellSetID("I-1"));
|
||||
std::vector<std::pair<int, double>> expected = { {0, 0.5}, {1, 0.5} };
|
||||
BOOST_REQUIRE_EQUAL(conc.size(), expected.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto& v : conc) {
|
||||
BOOST_TEST_MESSAGE("Conc[" << v.first << "] = " << v.second);
|
||||
BOOST_CHECK_EQUAL(v.first, expected[i].first);
|
||||
BOOST_CHECK_CLOSE(v.second, expected[i].second, 1.0e-10);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Local I-1 tof.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== I-1 tof");
|
||||
const auto tof = fwd.fd.timeOfFlight(CellSetID("I-1"));
|
||||
std::vector<std::pair<int, double>> expected = { {0, 0.5}, {1, 1.0} };
|
||||
BOOST_REQUIRE_EQUAL(tof.size(), expected.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto& v : tof) {
|
||||
BOOST_TEST_MESSAGE("ToF[" << v.first << "] = " << v.second);
|
||||
BOOST_CHECK_EQUAL(v.first, expected[i].first);
|
||||
BOOST_CHECK_CLOSE(v.second, expected[i].second, 1.0e-10);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Local I-2 tracer concentration.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== I-2 tracer");
|
||||
const auto conc = fwd.fd.concentration(CellSetID("I-2"));
|
||||
std::vector<std::pair<int, double>> expected = { {0, 0.5}, {1, 0.5} };
|
||||
BOOST_REQUIRE_EQUAL(conc.size(), expected.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto& v : conc) {
|
||||
BOOST_TEST_MESSAGE("Conc[" << v.first << "] = " << v.second);
|
||||
BOOST_CHECK_EQUAL(v.first, expected[i].first);
|
||||
BOOST_CHECK_CLOSE(v.second, expected[i].second, 1.0e-10);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Local I-2 tof.
|
||||
{
|
||||
BOOST_TEST_MESSAGE("== I-2 tof");
|
||||
const auto tof = fwd.fd.timeOfFlight(CellSetID("I-2"));
|
||||
std::vector<std::pair<int, double>> expected = { {0, 0.5}, {1, 1.0} };
|
||||
BOOST_REQUIRE_EQUAL(tof.size(), expected.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto& v : tof) {
|
||||
BOOST_TEST_MESSAGE("ToF[" << v.first << "] = " << v.second);
|
||||
BOOST_CHECK_EQUAL(v.first, expected[i].first);
|
||||
BOOST_CHECK_CLOSE(v.second, expected[i].second, 1.0e-10);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user