mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
#1206 Updated the opm-flowdiag libraries
This commit is contained in:
@@ -13,8 +13,10 @@ set(ERT_GITHUB_SHA "e2a5a9cc20705537d07822958d925e092a323367")
|
|||||||
set(OPM_COMMON_GITHUB_SHA "1216bc052542f24ec6fcfbe1947d52e6300ff754")
|
set(OPM_COMMON_GITHUB_SHA "1216bc052542f24ec6fcfbe1947d52e6300ff754")
|
||||||
set(OPM_PARSER_GITHUB_SHA "a3496df501a4369fd827fbf0ff893d03deff425f")
|
set(OPM_PARSER_GITHUB_SHA "a3496df501a4369fd827fbf0ff893d03deff425f")
|
||||||
|
|
||||||
set(OPM_FLOWDIAGNOSTICS_SHA "80190c28ae0fd4a82cfe88c827559029982db83b")
|
set(OPM_FLOWDIAGNOSTICS_SHA "02503abf0043bb08062e358e460a0c8231b6225c")
|
||||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "01ecb8fc22cb70d883edaad398bffc49878859c3")
|
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "09057e5767e492fa44c5e9ae78e9e1fcc9694b6c")
|
||||||
|
|
||||||
|
# sha for Units.hpp 9a679071dd0066236154852c39a9e0b3c3ac4873
|
||||||
|
|
||||||
set(STRPRODUCTVER ${RESINSIGHT_MAJOR_VERSION}.${RESINSIGHT_MINOR_VERSION}.${RESINSIGHT_INCREMENT_VERSION})
|
set(STRPRODUCTVER ${RESINSIGHT_MAJOR_VERSION}.${RESINSIGHT_MINOR_VERSION}.${RESINSIGHT_INCREMENT_VERSION})
|
||||||
|
|
||||||
|
|||||||
58
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/AcceptanceTests.cmake
vendored
Normal file
58
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/AcceptanceTests.cmake
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Set absolute tolerance to be used for testing
|
||||||
|
Set (abs_tol 5.0e-8)
|
||||||
|
Set (rel_tol 1.0e-13)
|
||||||
|
|
||||||
|
# Input:
|
||||||
|
# - casename: with or without extension
|
||||||
|
#
|
||||||
|
Macro (add_acceptance_test casename)
|
||||||
|
|
||||||
|
String (REGEX REPLACE "\\.[^.]*$" "" basename "${casename}")
|
||||||
|
|
||||||
|
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}")
|
||||||
|
|
||||||
|
EndMacro (add_acceptance_test)
|
||||||
|
|
||||||
|
# Input
|
||||||
|
# - casename: with or without extension
|
||||||
|
#
|
||||||
|
Macro (add_trans_acceptance_test casename)
|
||||||
|
|
||||||
|
String (REGEX REPLACE "\\.[^.]*$" "" basename "${casename}")
|
||||||
|
|
||||||
|
Add_Test (NAME Trans_accept_${casename}
|
||||||
|
COMMAND runTransTest
|
||||||
|
"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}")
|
||||||
|
|
||||||
|
EndMacro (add_trans_acceptance_test)
|
||||||
|
|
||||||
|
# Input
|
||||||
|
# - casename: with or without extension
|
||||||
|
# - strings identifying which physical quantities to compare
|
||||||
|
Macro (add_celldata_acceptance_test casename)
|
||||||
|
|
||||||
|
String (REGEX REPLACE "\\.[^.]*$" "" basename "${casename}")
|
||||||
|
|
||||||
|
Add_Test (NAME CellData_accept_${casename}
|
||||||
|
COMMAND runLinearisedCellDataTest
|
||||||
|
"case=${OPM_DATA_ROOT}/flow_diagnostic_test/eclipse-simulation/${basename}"
|
||||||
|
"ref-dir=${OPM_DATA_ROOT}/flow_diagnostic_test/fd-ref-data/${basename}"
|
||||||
|
"quant=${ARGN}" "atol=${abs_tol}" "rtol=${rel_tol}")
|
||||||
|
|
||||||
|
EndMacro (add_celldata_acceptance_test)
|
||||||
|
|
||||||
|
If (NOT TARGET test-suite)
|
||||||
|
Add_Custom_Target (test-suite)
|
||||||
|
EndIf ()
|
||||||
|
|
||||||
|
# Acceptance tests
|
||||||
|
|
||||||
|
Add_Acceptance_Test (SIMPLE_2PH_W_FAULT_LGR)
|
||||||
|
Add_Trans_Acceptance_Test (SIMPLE_2PH_W_FAULT_LGR)
|
||||||
|
Add_CellData_Acceptance_Test (SIMPLE_2PH_W_FAULT_LGR "pressure")
|
||||||
@@ -59,6 +59,10 @@ endif()
|
|||||||
macro (dir_hook)
|
macro (dir_hook)
|
||||||
endmacro (dir_hook)
|
endmacro (dir_hook)
|
||||||
|
|
||||||
|
# Look for the opm-data repository; if found the variable
|
||||||
|
# HAVE_OPM_DATA will be set to true.
|
||||||
|
include (Findopm-data)
|
||||||
|
|
||||||
# list of prerequisites for this particular project; this is in a
|
# list of prerequisites for this particular project; this is in a
|
||||||
# separate file (in cmake/Modules sub-directory) because it is shared
|
# separate file (in cmake/Modules sub-directory) because it is shared
|
||||||
# with the find module
|
# with the find module
|
||||||
@@ -91,3 +95,7 @@ endmacro (install_hook)
|
|||||||
|
|
||||||
# all setup common to the OPM library modules is done here
|
# all setup common to the OPM library modules is done here
|
||||||
include (OpmLibMain)
|
include (OpmLibMain)
|
||||||
|
|
||||||
|
if (HAVE_OPM_DATA)
|
||||||
|
include (${CMAKE_CURRENT_SOURCE_DIR}/AcceptanceTests.cmake)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -21,22 +21,30 @@
|
|||||||
# the library needs it.
|
# the library needs it.
|
||||||
|
|
||||||
list (APPEND MAIN_SOURCE_FILES
|
list (APPEND MAIN_SOURCE_FILES
|
||||||
|
opm/utility/ECLFluxCalc.cpp
|
||||||
opm/utility/ECLGraph.cpp
|
opm/utility/ECLGraph.cpp
|
||||||
opm/utility/ECLResultData.cpp
|
opm/utility/ECLResultData.cpp
|
||||||
|
opm/utility/ECLUnitHandling.cpp
|
||||||
opm/utility/ECLWellSolution.cpp
|
opm/utility/ECLWellSolution.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list (APPEND TEST_SOURCE_FILES
|
list (APPEND TEST_SOURCE_FILES
|
||||||
|
tests/test_eclunithandling.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list (APPEND EXAMPLE_SOURCE_FILES
|
list (APPEND EXAMPLE_SOURCE_FILES
|
||||||
examples/computeLocalSolutions.cpp
|
examples/computeLocalSolutions.cpp
|
||||||
examples/computeToFandTracers.cpp
|
examples/computeToFandTracers.cpp
|
||||||
examples/computeTracers.cpp
|
examples/computeTracers.cpp
|
||||||
|
tests/runAcceptanceTest.cpp
|
||||||
|
tests/runLinearisedCellDataTest.cpp
|
||||||
|
tests/runTransTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
list (APPEND PUBLIC_HEADER_FILES
|
list (APPEND PUBLIC_HEADER_FILES
|
||||||
|
opm/utility/ECLFluxCalc.hpp
|
||||||
opm/utility/ECLGraph.hpp
|
opm/utility/ECLGraph.hpp
|
||||||
opm/utility/ECLResultData.hpp
|
opm/utility/ECLResultData.hpp
|
||||||
|
opm/utility/ECLUnitHandling.hpp
|
||||||
opm/utility/ECLWellSolution.hpp
|
opm/utility/ECLWellSolution.hpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <opm/flowdiagnostics/ConnectionValues.hpp>
|
#include <opm/flowdiagnostics/ConnectionValues.hpp>
|
||||||
#include <opm/flowdiagnostics/Toolbox.hpp>
|
#include <opm/flowdiagnostics/Toolbox.hpp>
|
||||||
|
|
||||||
|
#include <opm/utility/ECLFluxCalc.hpp>
|
||||||
#include <opm/utility/ECLGraph.hpp>
|
#include <opm/utility/ECLGraph.hpp>
|
||||||
#include <opm/utility/ECLWellSolution.hpp>
|
#include <opm/utility/ECLWellSolution.hpp>
|
||||||
|
|
||||||
@@ -80,38 +81,31 @@ namespace example {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Opm::FlowDiagnostics::ConnectionValues
|
inline Opm::FlowDiagnostics::ConnectionValues
|
||||||
extractFluxField(const Opm::ECLGraph& G)
|
extractFluxField(const Opm::ECLGraph& G, const bool compute_fluxes)
|
||||||
{
|
{
|
||||||
using ConnVals = Opm::FlowDiagnostics::ConnectionValues;
|
using ConnVals = Opm::FlowDiagnostics::ConnectionValues;
|
||||||
|
auto flux = ConnVals(ConnVals::NumConnections{G.numConnections()},
|
||||||
using NConn = ConnVals::NumConnections;
|
ConnVals::NumPhases{3});
|
||||||
using NPhas = ConnVals::NumPhases;
|
|
||||||
|
|
||||||
const auto nconn = NConn{G.numConnections()};
|
|
||||||
const auto nphas = NPhas{3};
|
|
||||||
|
|
||||||
auto flux = ConnVals(nconn, nphas);
|
|
||||||
|
|
||||||
auto phas = ConnVals::PhaseID{0};
|
auto phas = ConnVals::PhaseID{0};
|
||||||
|
|
||||||
for (const auto& p : { Opm::ECLGraph::PhaseIndex::Aqua ,
|
Opm::ECLFluxCalc calc(G);
|
||||||
|
|
||||||
|
const auto phases = { Opm::ECLGraph::PhaseIndex::Aqua ,
|
||||||
Opm::ECLGraph::PhaseIndex::Liquid ,
|
Opm::ECLGraph::PhaseIndex::Liquid ,
|
||||||
Opm::ECLGraph::PhaseIndex::Vapour })
|
Opm::ECLGraph::PhaseIndex::Vapour };
|
||||||
|
|
||||||
|
for (const auto& p : phases)
|
||||||
{
|
{
|
||||||
const auto pflux = G.flux(p);
|
const auto pflux = compute_fluxes ? calc.flux(p) : G.flux(p);
|
||||||
|
|
||||||
if (! pflux.empty()) {
|
if (! pflux.empty()) {
|
||||||
assert (pflux.size() == nconn.total);
|
assert (pflux.size() == flux.numConnections());
|
||||||
|
|
||||||
auto conn = ConnVals::ConnID{0};
|
auto conn = ConnVals::ConnID{0};
|
||||||
|
|
||||||
for (const auto& v : pflux) {
|
for (const auto& v : pflux) {
|
||||||
flux(conn, phas) = v;
|
flux(conn, phas) = v;
|
||||||
|
|
||||||
conn.id += 1;
|
conn.id += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
phas.id += 1;
|
phas.id += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,72 +132,60 @@ namespace example {
|
|||||||
return inflow;
|
return inflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Hack {
|
|
||||||
inline Opm::FlowDiagnostics::ConnectionValues
|
|
||||||
convert_flux_to_SI(Opm::FlowDiagnostics::ConnectionValues&& fl)
|
|
||||||
|
struct FilePaths
|
||||||
{
|
{
|
||||||
using Co = Opm::FlowDiagnostics::ConnectionValues::ConnID;
|
FilePaths(const Opm::parameter::ParameterGroup& param)
|
||||||
using Ph = Opm::FlowDiagnostics::ConnectionValues::PhaseID;
|
{
|
||||||
|
const string casename = param.getDefault<string>("case", "DEFAULT_CASE_NAME");
|
||||||
const auto nconn = fl.numConnections();
|
grid = param.has("grid") ? param.get<string>("grid")
|
||||||
const auto nphas = fl.numPhases();
|
: deriveFileName(casename, { ".EGRID", ".FEGRID", ".GRID", ".FGRID" });
|
||||||
|
init = param.has("init") ? param.get<string>("init")
|
||||||
for (auto phas = Ph{0}; phas.id < nphas; ++phas.id) {
|
: deriveFileName(casename, { ".INIT", ".FINIT" });
|
||||||
for (auto conn = Co{0}; conn.id < nconn; ++conn.id) {
|
restart = param.has("restart") ? param.get<string>("restart")
|
||||||
fl(conn, phas) /= 86400;
|
: deriveFileName(casename, { ".UNRST", ".FUNRST" });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fl;
|
using path = boost::filesystem::path;
|
||||||
}
|
using string = std::string;
|
||||||
}
|
|
||||||
|
|
||||||
inline Opm::ECLGraph
|
path grid;
|
||||||
initGraph(int argc, char* argv[])
|
path init;
|
||||||
|
path restart;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline Opm::parameter::ParameterGroup
|
||||||
|
initParam(int argc, char** argv)
|
||||||
{
|
{
|
||||||
// Obtain parameters from command line (possibly specifying a parameter file).
|
// Obtain parameters from command line (possibly specifying a parameter file).
|
||||||
const bool verify_commandline_syntax = true;
|
const bool verify_commandline_syntax = true;
|
||||||
const bool parameter_output = false;
|
const bool parameter_output = false;
|
||||||
Opm::parameter::ParameterGroup param(argc, argv, verify_commandline_syntax, parameter_output);
|
Opm::parameter::ParameterGroup param(argc, argv, verify_commandline_syntax, parameter_output);
|
||||||
|
return param;
|
||||||
// Obtain filenames for grid, init and restart files, as well as step number.
|
|
||||||
using boost::filesystem::path;
|
|
||||||
using std::string;
|
|
||||||
const string casename = param.getDefault<string>("case", "DEFAULT_CASE_NAME");
|
|
||||||
const path grid = param.has("grid") ? param.get<string>("grid")
|
|
||||||
: deriveFileName(casename, { ".EGRID", ".FEGRID", ".GRID", ".FGRID" });
|
|
||||||
const path init = param.has("init") ? param.get<string>("init")
|
|
||||||
: deriveFileName(casename, { ".INIT", ".FINIT" });
|
|
||||||
const path restart = param.has("restart") ? param.get<string>("restart")
|
|
||||||
: deriveFileName(casename, { ".UNRST", ".FUNRST" });
|
|
||||||
const int step = param.getDefault("step", 0);
|
|
||||||
|
|
||||||
// Read graph and fluxes, initialise the toolbox.
|
|
||||||
auto graph = Opm::ECLGraph::load(grid, init);
|
|
||||||
graph.assignFluxDataSource(restart);
|
|
||||||
|
|
||||||
if (! graph.selectReportStep(step)) {
|
|
||||||
std::ostringstream os;
|
|
||||||
|
|
||||||
os << "Report Step " << step
|
|
||||||
<< " is Not Available in Result Set '"
|
|
||||||
<< grid.stem() << '\'';
|
|
||||||
|
|
||||||
throw std::domain_error(os.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline Opm::ECLGraph
|
||||||
|
initGraph(const FilePaths& file_paths)
|
||||||
|
{
|
||||||
|
// Read graph and assign restart file.
|
||||||
|
auto graph = Opm::ECLGraph::load(file_paths.grid, file_paths.init);
|
||||||
|
graph.assignFluxDataSource(file_paths.restart);
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<Opm::ECLWellSolution::WellData>
|
|
||||||
initWellFluxes(const Opm::ECLGraph& G)
|
|
||||||
{
|
|
||||||
auto wsol = Opm::ECLWellSolution{};
|
|
||||||
return wsol.solution(G.rawResultData(), G.numGrids());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Opm::FlowDiagnostics::Toolbox
|
inline Opm::FlowDiagnostics::Toolbox
|
||||||
initToolbox(const Opm::ECLGraph& G, const std::vector<Opm::ECLWellSolution::WellData>& well_fluxes)
|
initToolbox(const Opm::ECLGraph& G)
|
||||||
{
|
{
|
||||||
const auto connGraph = Opm::FlowDiagnostics::
|
const auto connGraph = Opm::FlowDiagnostics::
|
||||||
ConnectivityGraph{ static_cast<int>(G.numCells()),
|
ConnectivityGraph{ static_cast<int>(G.numCells()),
|
||||||
@@ -211,11 +193,7 @@ namespace example {
|
|||||||
|
|
||||||
// Create the Toolbox.
|
// Create the Toolbox.
|
||||||
auto tool = Opm::FlowDiagnostics::Toolbox{ connGraph };
|
auto tool = Opm::FlowDiagnostics::Toolbox{ connGraph };
|
||||||
|
|
||||||
tool.assignPoreVolume(G.poreVolume());
|
tool.assignPoreVolume(G.poreVolume());
|
||||||
tool.assignConnectionFlux(Hack::convert_flux_to_SI(extractFluxField(G)));
|
|
||||||
|
|
||||||
tool.assignInflowFlux(extractWellFlows(G, well_fluxes));
|
|
||||||
|
|
||||||
return tool;
|
return tool;
|
||||||
}
|
}
|
||||||
@@ -226,15 +204,42 @@ namespace example {
|
|||||||
struct Setup
|
struct Setup
|
||||||
{
|
{
|
||||||
Setup(int argc, char** argv)
|
Setup(int argc, char** argv)
|
||||||
: graph(initGraph(argc, argv))
|
: param(initParam(argc, argv))
|
||||||
, well_fluxes(initWellFluxes(graph))
|
, file_paths(param)
|
||||||
, toolbox(initToolbox(graph, well_fluxes))
|
, graph(initGraph(file_paths))
|
||||||
|
, well_fluxes()
|
||||||
|
, toolbox(initToolbox(graph))
|
||||||
|
, compute_fluxes_(param.getDefault("compute_fluxes", false))
|
||||||
{
|
{
|
||||||
|
const int step = param.getDefault("step", 0);
|
||||||
|
if (!selectReportStep(step)) {
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "Report Step " << step
|
||||||
|
<< " is Not Available in Result Set '"
|
||||||
|
<< file_paths.grid.stem() << '\'';
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool selectReportStep(const int step)
|
||||||
|
{
|
||||||
|
if (graph.selectReportStep(step)) {
|
||||||
|
auto wsol = Opm::ECLWellSolution{};
|
||||||
|
well_fluxes = wsol.solution(graph.rawResultData(), graph.numGrids());;
|
||||||
|
toolbox.assignConnectionFlux(extractFluxField(graph, compute_fluxes_));
|
||||||
|
toolbox.assignInflowFlux(extractWellFlows(graph, well_fluxes));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Opm::parameter::ParameterGroup param;
|
||||||
|
FilePaths file_paths;
|
||||||
Opm::ECLGraph graph;
|
Opm::ECLGraph graph;
|
||||||
std::vector<Opm::ECLWellSolution::WellData> well_fluxes;
|
std::vector<Opm::ECLWellSolution::WellData> well_fluxes;
|
||||||
Opm::FlowDiagnostics::Toolbox toolbox;
|
Opm::FlowDiagnostics::Toolbox toolbox;
|
||||||
|
bool compute_fluxes_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
64
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLFluxCalc.cpp
vendored
Normal file
64
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLFluxCalc.cpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
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/ECLFluxCalc.hpp>
|
||||||
|
|
||||||
|
namespace Opm
|
||||||
|
{
|
||||||
|
|
||||||
|
ECLFluxCalc::ECLFluxCalc(const ECLGraph& graph)
|
||||||
|
: graph_(graph)
|
||||||
|
, neighbours_(graph.neighbours())
|
||||||
|
, transmissibility_(graph.transmissibility())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<double> ECLFluxCalc::flux(const PhaseIndex /* phase */) const
|
||||||
|
{
|
||||||
|
// Obtain pressure vector.
|
||||||
|
std::vector<double> pressure = graph_.linearisedCellData("PRESSURE", &ECLUnits::UnitSystem::pressure);
|
||||||
|
|
||||||
|
// Compute fluxes per connection.
|
||||||
|
const int num_conn = transmissibility_.size();
|
||||||
|
std::vector<double> fluxvec(num_conn);
|
||||||
|
for (int conn = 0; conn < num_conn; ++conn) {
|
||||||
|
fluxvec[conn] = singleFlux(conn, pressure);
|
||||||
|
}
|
||||||
|
return fluxvec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double ECLFluxCalc::singleFlux(const int connection,
|
||||||
|
const std::vector<double>& pressure) const
|
||||||
|
{
|
||||||
|
const int c1 = neighbours_[2*connection];
|
||||||
|
const int c2 = neighbours_[2*connection + 1];
|
||||||
|
const double t = transmissibility_[connection];
|
||||||
|
return t * (pressure[c1] - pressure[c2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Opm
|
||||||
61
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLFluxCalc.hpp
vendored
Normal file
61
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLFluxCalc.hpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
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_ECLFLUXCALC_HEADER_INCLUDED
|
||||||
|
#define OPM_ECLFLUXCALC_HEADER_INCLUDED
|
||||||
|
|
||||||
|
#include <opm/utility/ECLGraph.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Opm
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Class for computing connection fluxes in the absence of flux output.
|
||||||
|
class ECLFluxCalc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Construct from ECLGraph.
|
||||||
|
///
|
||||||
|
/// \param[in] graph Connectivity data, as well as providing a means to read data from the restart file.
|
||||||
|
explicit ECLFluxCalc(const ECLGraph& graph);
|
||||||
|
|
||||||
|
using PhaseIndex = ECLGraph::PhaseIndex;
|
||||||
|
|
||||||
|
/// Retrive phase flux on all connections defined by \code
|
||||||
|
/// graph.neighbours() \endcode.
|
||||||
|
///
|
||||||
|
/// \param[in] phase Canonical phase for which to retrive flux.
|
||||||
|
///
|
||||||
|
/// \return Flux values corresponding to selected phase.
|
||||||
|
/// Empty if required data is missing.
|
||||||
|
/// Numerical values in SI units (rm^3/s).
|
||||||
|
std::vector<double> flux(const PhaseIndex phase) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double singleFlux(const int connection,
|
||||||
|
const std::vector<double>& pressure) const;
|
||||||
|
|
||||||
|
const ECLGraph& graph_;
|
||||||
|
std::vector<int> neighbours_;
|
||||||
|
std::vector<double> transmissibility_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Opm
|
||||||
|
|
||||||
|
#endif // OPM_ECLFLUXCALC_HEADER_INCLUDED
|
||||||
@@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
#include <opm/utility/ECLGraph.hpp>
|
#include <opm/utility/ECLGraph.hpp>
|
||||||
#include <opm/utility/ECLResultData.hpp>
|
#include <opm/utility/ECLResultData.hpp>
|
||||||
|
#include <opm/utility/ECLUnitHandling.hpp>
|
||||||
|
|
||||||
|
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -41,6 +44,7 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include <ert/ecl/ecl_grid.h>
|
#include <ert/ecl/ecl_grid.h>
|
||||||
|
#include <ert/ecl/ecl_kw_magic.h>
|
||||||
#include <ert/ecl/ecl_nnc_export.h>
|
#include <ert/ecl/ecl_nnc_export.h>
|
||||||
#include <ert/util/ert_unique_ptr.hpp>
|
#include <ert/util/ert_unique_ptr.hpp>
|
||||||
|
|
||||||
@@ -93,6 +97,18 @@ namespace {
|
|||||||
std::array<std::size_t,3>
|
std::array<std::size_t,3>
|
||||||
cartesianDimensions(const ecl_grid_type* G);
|
cartesianDimensions(const ecl_grid_type* G);
|
||||||
|
|
||||||
|
/// Access unit conventions pertaining to single grid in result set.
|
||||||
|
///
|
||||||
|
/// \param[in] rset Result set.
|
||||||
|
///
|
||||||
|
/// \param[in] grid_ID Numerical ID of grid. Non-negative. Zero
|
||||||
|
/// for the main grid and positive for LGRs.
|
||||||
|
///
|
||||||
|
/// \return Unit system convention for \p grid_ID in result set.
|
||||||
|
auto getUnitSystem(const ::Opm::ECLResultData& rset,
|
||||||
|
const int grid_ID)
|
||||||
|
-> decltype(::Opm::ECLUnits::createUnitSystem(0));
|
||||||
|
|
||||||
/// Retrieve global pore-volume vector from INIT source.
|
/// Retrieve global pore-volume vector from INIT source.
|
||||||
///
|
///
|
||||||
/// Specialised tool needed to determine the active cells.
|
/// Specialised tool needed to determine the active cells.
|
||||||
@@ -101,7 +117,11 @@ namespace {
|
|||||||
///
|
///
|
||||||
/// \param[in] init ERT representation of INIT source.
|
/// \param[in] init ERT representation of INIT source.
|
||||||
///
|
///
|
||||||
/// \return Vector of pore-volumes for all global cells of \p G.
|
/// \param[in] grid_ID Numerical ID of grid. Non-negative. Zero
|
||||||
|
/// for the main grid and positive in the case of an LGR.
|
||||||
|
///
|
||||||
|
/// \return Vector of pore-volumes for all global cells of \p G in
|
||||||
|
/// SI unit conventions (rm^3).
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
getPVolVector(const ecl_grid_type* G,
|
getPVolVector(const ecl_grid_type* G,
|
||||||
const ::Opm::ECLResultData& init,
|
const ::Opm::ECLResultData& init,
|
||||||
@@ -137,6 +157,11 @@ namespace {
|
|||||||
const ::Opm::ECLResultData& init,
|
const ::Opm::ECLResultData& init,
|
||||||
const int gridID);
|
const int gridID);
|
||||||
|
|
||||||
|
/// Retrieve non-negative numeric ID of grid instance.
|
||||||
|
///
|
||||||
|
/// \return Constructor's \c gridID parameter.
|
||||||
|
int gridID() const;
|
||||||
|
|
||||||
/// Retrieve number of active cells in graph.
|
/// Retrieve number of active cells in graph.
|
||||||
std::size_t numCells() const;
|
std::size_t numCells() const;
|
||||||
|
|
||||||
@@ -154,9 +179,18 @@ namespace {
|
|||||||
///
|
///
|
||||||
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
||||||
/// restricted to those active cells for which the pore-volume is
|
/// restricted to those active cells for which the pore-volume is
|
||||||
/// strictly positive.
|
/// strictly positive. SI unit conventions (rm^3).
|
||||||
const std::vector<double>& activePoreVolume() const;
|
const std::vector<double>& activePoreVolume() const;
|
||||||
|
|
||||||
|
/// Retrieve static (background) transmissibility values on all
|
||||||
|
/// connections defined by \code neighbours() \endcode.
|
||||||
|
///
|
||||||
|
/// Specifically, \code transmissibility()[i] \endcode is the
|
||||||
|
/// transmissibility of the connection between cells \code
|
||||||
|
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i +
|
||||||
|
/// 1] \endcode.
|
||||||
|
const std::vector<double>& transmissibility() const;
|
||||||
|
|
||||||
/// Retrieve ID of active cell from global ID.
|
/// Retrieve ID of active cell from global ID.
|
||||||
int activeCell(const std::size_t globalCell) const;
|
int activeCell(const std::size_t globalCell) const;
|
||||||
|
|
||||||
@@ -188,6 +222,11 @@ namespace {
|
|||||||
cellData(const ::Opm::ECLResultData& src,
|
cellData(const ::Opm::ECLResultData& src,
|
||||||
const std::string& vector) const;
|
const std::string& vector) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
activeCellData(const ::Opm::ECLResultData& src,
|
||||||
|
const std::string& vector) const;
|
||||||
|
|
||||||
/// Retrieve values of result set vector for all Cartesian
|
/// Retrieve values of result set vector for all Cartesian
|
||||||
/// connections in grid.
|
/// connections in grid.
|
||||||
///
|
///
|
||||||
@@ -200,7 +239,8 @@ namespace {
|
|||||||
/// all of the grid's Cartesian connections.
|
/// all of the grid's Cartesian connections.
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
connectionData(const ::Opm::ECLResultData& src,
|
connectionData(const ::Opm::ECLResultData& src,
|
||||||
const std::string& vector) const;
|
const std::string& vector,
|
||||||
|
const double unit) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Facility for deriving Cartesian neighbourship in a grid
|
/// Facility for deriving Cartesian neighbourship in a grid
|
||||||
@@ -217,14 +257,18 @@ namespace {
|
|||||||
/// \param[in] G ERT Grid representation.
|
/// \param[in] G ERT Grid representation.
|
||||||
///
|
///
|
||||||
/// \param[in] pvol Vector of pore-volumes on all global
|
/// \param[in] pvol Vector of pore-volumes on all global
|
||||||
/// cells of \p G. Typically obtained
|
/// cells of \p G. Typically obtained through function
|
||||||
/// through function getPVolVector().
|
/// getPVolVector(). Numerical values assumed to be in
|
||||||
|
/// SI units (rm^3).
|
||||||
CartesianCells(const ecl_grid_type* G,
|
CartesianCells(const ecl_grid_type* G,
|
||||||
const std::vector<double>& pvol);
|
const std::vector<double>& pvol);
|
||||||
|
|
||||||
/// Retrive global cell indices of all active cells in grid.
|
/// Retrive global cell indices of all active cells in grid.
|
||||||
std::vector<std::size_t> activeGlobal() const;
|
std::vector<std::size_t> activeGlobal() const;
|
||||||
|
|
||||||
|
/// Retrieve pore-volume values for all active cells in grid.
|
||||||
|
///
|
||||||
|
/// SI unit conventions (rm^3).
|
||||||
const std::vector<double>& activePoreVolume() const;
|
const std::vector<double>& activePoreVolume() const;
|
||||||
|
|
||||||
/// Map input vector to all global cells.
|
/// Map input vector to all global cells.
|
||||||
@@ -240,6 +284,30 @@ namespace {
|
|||||||
std::vector<T>
|
std::vector<T>
|
||||||
scatterToGlobal(const std::vector<T>& x) const;
|
scatterToGlobal(const std::vector<T>& x) const;
|
||||||
|
|
||||||
|
/// Restrict input vector to active grid cells.
|
||||||
|
///
|
||||||
|
/// Selects subsets corresponding to active cells (i.e.,
|
||||||
|
/// those cells for which \code pore_volume > 0 \endcode) if
|
||||||
|
/// input size is
|
||||||
|
///
|
||||||
|
/// - All global cells
|
||||||
|
/// - Explicitly active cells (ACTNUM != 0)
|
||||||
|
///
|
||||||
|
/// All other cases are returned unfiltered--i.e., as direct
|
||||||
|
/// copies of the input.
|
||||||
|
///
|
||||||
|
/// \param[in] x Input vector, defined on the explicitly
|
||||||
|
/// active cells, all global cells or some
|
||||||
|
/// other subset (e.g., all non-neighbouring
|
||||||
|
/// connections).
|
||||||
|
///
|
||||||
|
/// \return Input vector restricted to active cells if
|
||||||
|
/// subset known. Direct copy if \p x is defined on set
|
||||||
|
/// other than explicitly active or all global cells.
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
gatherToActive(const std::vector<T>& x) const;
|
||||||
|
|
||||||
/// Retrieve total number of cells in grid, including
|
/// Retrieve total number of cells in grid, including
|
||||||
/// inactive ones.
|
/// inactive ones.
|
||||||
///
|
///
|
||||||
@@ -315,13 +383,8 @@ namespace {
|
|||||||
/// Whether or not a particular active cell is subdivided.
|
/// Whether or not a particular active cell is subdivided.
|
||||||
std::vector<bool> is_divided_;
|
std::vector<bool> is_divided_;
|
||||||
|
|
||||||
/// Identify those grid cells that are further subdivided by
|
/// Retrieve number of active cells in grid.
|
||||||
/// an LGR.
|
std::size_t numActiveCells() const;
|
||||||
///
|
|
||||||
/// Writes to \c is_divided_.
|
|
||||||
///
|
|
||||||
/// \param
|
|
||||||
void identifySubdividedCells(const ecl_grid_type* G);
|
|
||||||
|
|
||||||
/// Compute linear index of global cell from explicit
|
/// Compute linear index of global cell from explicit
|
||||||
/// (I,J,K) tuple.
|
/// (I,J,K) tuple.
|
||||||
@@ -374,6 +437,11 @@ namespace {
|
|||||||
/// Source cells for each Cartesian connection.
|
/// Source cells for each Cartesian connection.
|
||||||
OutCell outCell_;
|
OutCell outCell_;
|
||||||
|
|
||||||
|
/// Transmissibility field for purpose of on-demand flux
|
||||||
|
/// calculation if fluxes are not already available in dynamic
|
||||||
|
/// result set.
|
||||||
|
std::vector<double> trans_;
|
||||||
|
|
||||||
/// Predicate for whether or not a particular result vector is
|
/// Predicate for whether or not a particular result vector is
|
||||||
/// defined on the grid's cells.
|
/// defined on the grid's cells.
|
||||||
///
|
///
|
||||||
@@ -415,6 +483,7 @@ namespace {
|
|||||||
void connectionData(const ::Opm::ECLResultData& src,
|
void connectionData(const ::Opm::ECLResultData& src,
|
||||||
const CartesianCells::Direction d,
|
const CartesianCells::Direction d,
|
||||||
const std::string& vector,
|
const std::string& vector,
|
||||||
|
const double unit,
|
||||||
std::vector<double>& x) const;
|
std::vector<double>& x) const;
|
||||||
|
|
||||||
/// Form complete name of directional result set vector from
|
/// Form complete name of directional result set vector from
|
||||||
@@ -430,7 +499,7 @@ namespace {
|
|||||||
const CartesianCells::Direction d) const;
|
const CartesianCells::Direction d) const;
|
||||||
|
|
||||||
/// Derive neighbourship relations on active cells in particular
|
/// Derive neighbourship relations on active cells in particular
|
||||||
/// Cartesian directions.
|
/// Cartesian directions and capture transmissibilty field.
|
||||||
///
|
///
|
||||||
/// Writes to \c neigh_ and \c outCell_.
|
/// Writes to \c neigh_ and \c outCell_.
|
||||||
///
|
///
|
||||||
@@ -487,6 +556,13 @@ ECL::getPVolVector(const ecl_grid_type* G,
|
|||||||
|
|
||||||
assert ((pvol.size() == nglob) &&
|
assert ((pvol.size() == nglob) &&
|
||||||
"Pore-volume must be provided for all global cells");
|
"Pore-volume must be provided for all global cells");
|
||||||
|
|
||||||
|
const auto pvol_unit =
|
||||||
|
getUnitSystem(init, gridID)->reservoirVolume();
|
||||||
|
|
||||||
|
for (auto& pv : pvol) {
|
||||||
|
pv = ::Opm::unit::convert::from(pv, pvol_unit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pvol;
|
return pvol;
|
||||||
@@ -524,6 +600,18 @@ ECL::cartesianDimensions(const ecl_grid_type* G)
|
|||||||
make_szt(ecl_grid_get_nz(G)) } };
|
make_szt(ecl_grid_get_nz(G)) } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ECL::getUnitSystem(const ::Opm::ECLResultData& rset,
|
||||||
|
const int grid_ID)
|
||||||
|
-> decltype(::Opm::ECLUnits::createUnitSystem(0))
|
||||||
|
{
|
||||||
|
assert (rset.haveKeywordData(INTEHEAD_KW, grid_ID)
|
||||||
|
&& "Result Set Does Not Provide Grid Header");
|
||||||
|
|
||||||
|
const auto ih = rset.keywordData<int>(INTEHEAD_KW, grid_ID);
|
||||||
|
|
||||||
|
return ::Opm::ECLUnits::createUnitSystem(ih[INTEHEAD_UNIT_INDEX]);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ecl_nnc_type>
|
std::vector<ecl_nnc_type>
|
||||||
ECL::loadNNC(const ecl_grid_type* G,
|
ECL::loadNNC(const ecl_grid_type* G,
|
||||||
const ::Opm::ECLResultData& init)
|
const ::Opm::ECLResultData& init)
|
||||||
@@ -613,7 +701,7 @@ std::vector<std::size_t>
|
|||||||
ECL::CartesianGridData::CartesianCells::activeGlobal() const
|
ECL::CartesianGridData::CartesianCells::activeGlobal() const
|
||||||
{
|
{
|
||||||
auto active = std::vector<std::size_t>{};
|
auto active = std::vector<std::size_t>{};
|
||||||
active.reserve(this->rsMap_.subset.size());
|
active.reserve(this->numActiveCells());
|
||||||
|
|
||||||
for (const auto& id : this->rsMap_.subset) {
|
for (const auto& id : this->rsMap_.subset) {
|
||||||
active.push_back(id.glob);
|
active.push_back(id.glob);
|
||||||
@@ -656,6 +744,48 @@ CartesianCells::scatterToGlobal(const std::vector<T>& x) const
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace { namespace ECL {
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
CartesianGridData::CartesianCells::
|
||||||
|
gatherToActive(const std::vector<T>& x) const
|
||||||
|
{
|
||||||
|
const auto num_explicit_active =
|
||||||
|
static_cast<decltype(x.size())>(this->rsMap_.num_active);
|
||||||
|
|
||||||
|
if (x.size() == num_explicit_active) {
|
||||||
|
// Input defined on explictly active cells (ACTNUM != 0).
|
||||||
|
// Extract subset of these.
|
||||||
|
|
||||||
|
auto ax = std::vector<T>{};
|
||||||
|
ax.reserve(this->numActiveCells());
|
||||||
|
|
||||||
|
for (const auto& i : this->rsMap_.subset) {
|
||||||
|
ax.push_back(x[i.act]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ax;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.size() == this->numGlobalCells()) {
|
||||||
|
// Input defined on all global cells. Extract active subset.
|
||||||
|
|
||||||
|
auto ax = std::vector<T>{};
|
||||||
|
ax.reserve(this->numActiveCells());
|
||||||
|
|
||||||
|
for (const auto& i : this->rsMap_.subset) {
|
||||||
|
ax.push_back(x[i.glob]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input defined on neither explicitly active nor global cells.
|
||||||
|
// Possibly on all grid's NNCs. Let caller deal with this.
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}} // namespace Anonymous::ECL
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
ECL::CartesianGridData::CartesianCells::numGlobalCells() const
|
ECL::CartesianGridData::CartesianCells::numGlobalCells() const
|
||||||
{
|
{
|
||||||
@@ -718,6 +848,12 @@ ECL::CartesianGridData::CartesianCells::isSubdivided(const int cellID) const
|
|||||||
return this->is_divided_[ix];
|
return this->is_divided_[ix];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
ECL::CartesianGridData::CartesianCells::numActiveCells() const
|
||||||
|
{
|
||||||
|
return this->rsMap_.subset.size();
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
ECL::CartesianGridData::
|
ECL::CartesianGridData::
|
||||||
CartesianCells::globIdx(const IndexTuple& ijk) const
|
CartesianCells::globIdx(const IndexTuple& ijk) const
|
||||||
@@ -772,6 +908,7 @@ CartesianGridData(const ecl_grid_type* G,
|
|||||||
|
|
||||||
// Too large, but this is a quick estimate.
|
// Too large, but this is a quick estimate.
|
||||||
this->neigh_.reserve(3 * (2 * this->numCells()));
|
this->neigh_.reserve(3 * (2 * this->numCells()));
|
||||||
|
this->trans_.reserve(3 * (1 * this->numCells()));
|
||||||
|
|
||||||
for (const auto d : { CartesianCells::Direction::I ,
|
for (const auto d : { CartesianCells::Direction::I ,
|
||||||
CartesianCells::Direction::J ,
|
CartesianCells::Direction::J ,
|
||||||
@@ -781,6 +918,11 @@ CartesianGridData(const ecl_grid_type* G,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ECL::CartesianGridData::gridID() const
|
||||||
|
{
|
||||||
|
return this->gridID_;
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
ECL::CartesianGridData::numCells() const
|
ECL::CartesianGridData::numCells() const
|
||||||
{
|
{
|
||||||
@@ -805,6 +947,12 @@ ECL::CartesianGridData::activePoreVolume() const
|
|||||||
return this->cells_.activePoreVolume();
|
return this->cells_.activePoreVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<double>&
|
||||||
|
ECL::CartesianGridData::transmissibility() const
|
||||||
|
{
|
||||||
|
return this->trans_;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ECL::CartesianGridData::activeCell(const std::size_t globalCell) const
|
ECL::CartesianGridData::activeCell(const std::size_t globalCell) const
|
||||||
{
|
{
|
||||||
@@ -839,6 +987,22 @@ cellData(const ::Opm::ECLResultData& src,
|
|||||||
return this->cells_.scatterToGlobal(x);
|
return this->cells_.scatterToGlobal(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace { namespace ECL {
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
CartesianGridData::activeCellData(const ::Opm::ECLResultData& src,
|
||||||
|
const std::string& vector) const
|
||||||
|
{
|
||||||
|
if (! this->haveCellData(src, vector)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto x = src.keywordData<T>(vector, this->gridID_);
|
||||||
|
|
||||||
|
return this->cells_.gatherToActive(std::move(x));
|
||||||
|
}
|
||||||
|
}} // namespace Anonymous::ECL
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ECL::CartesianGridData::
|
ECL::CartesianGridData::
|
||||||
haveCellData(const ::Opm::ECLResultData& src,
|
haveCellData(const ::Opm::ECLResultData& src,
|
||||||
@@ -871,7 +1035,8 @@ haveConnData(const ::Opm::ECLResultData& src,
|
|||||||
std::vector<double>
|
std::vector<double>
|
||||||
ECL::CartesianGridData::
|
ECL::CartesianGridData::
|
||||||
connectionData(const ::Opm::ECLResultData& src,
|
connectionData(const ::Opm::ECLResultData& src,
|
||||||
const std::string& vector) const
|
const std::string& vector,
|
||||||
|
const double unit) const
|
||||||
{
|
{
|
||||||
if (! this->haveConnData(src, vector)) {
|
if (! this->haveConnData(src, vector)) {
|
||||||
return {};
|
return {};
|
||||||
@@ -883,7 +1048,7 @@ connectionData(const ::Opm::ECLResultData& src,
|
|||||||
CartesianCells::Direction::J ,
|
CartesianCells::Direction::J ,
|
||||||
CartesianCells::Direction::K })
|
CartesianCells::Direction::K })
|
||||||
{
|
{
|
||||||
this->connectionData(src, d, vector, x);
|
this->connectionData(src, d, this->vectorName(vector, d), unit, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
@@ -894,10 +1059,10 @@ ECL::CartesianGridData::
|
|||||||
connectionData(const ::Opm::ECLResultData& src,
|
connectionData(const ::Opm::ECLResultData& src,
|
||||||
const CartesianCells::Direction d,
|
const CartesianCells::Direction d,
|
||||||
const std::string& vector,
|
const std::string& vector,
|
||||||
|
const double unit,
|
||||||
std::vector<double>& x) const
|
std::vector<double>& x) const
|
||||||
{
|
{
|
||||||
const auto vname = this->vectorName(vector, d);
|
const auto v = this->cellData(src, vector);
|
||||||
const auto v = this->cellData(src, vname);
|
|
||||||
|
|
||||||
const auto& cells = this->outCell_.find(d);
|
const auto& cells = this->outCell_.find(d);
|
||||||
|
|
||||||
@@ -905,7 +1070,7 @@ connectionData(const ::Opm::ECLResultData& src,
|
|||||||
"Direction must be I, J, or K");
|
"Direction must be I, J, or K");
|
||||||
|
|
||||||
for (const auto& cell : cells->second) {
|
for (const auto& cell : cells->second) {
|
||||||
x.push_back(v[cell]);
|
x.push_back(::Opm::unit::convert::from(v[cell], unit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,6 +1116,14 @@ deriveNeighbours(const std::vector<std::size_t>& gcells,
|
|||||||
? this->cellData(init, tran)
|
? this->cellData(init, tran)
|
||||||
: std::vector<double>(this->cells_.numGlobalCells(), 1.0);
|
: std::vector<double>(this->cells_.numGlobalCells(), 1.0);
|
||||||
|
|
||||||
|
const auto trans_unit =
|
||||||
|
ECL::getUnitSystem(init, this->gridID_)->transmissibility();
|
||||||
|
|
||||||
|
auto SI_trans = [trans_unit](const double trans)
|
||||||
|
{
|
||||||
|
return ::Opm::unit::convert::from(trans, trans_unit);
|
||||||
|
};
|
||||||
|
|
||||||
auto& ocell = this->outCell_[d];
|
auto& ocell = this->outCell_[d];
|
||||||
ocell.reserve(gcells.size());
|
ocell.reserve(gcells.size());
|
||||||
|
|
||||||
@@ -976,6 +1149,7 @@ deriveNeighbours(const std::vector<std::size_t>& gcells,
|
|||||||
this->neigh_.push_back(other);
|
this->neigh_.push_back(other);
|
||||||
|
|
||||||
ocell.push_back(globID);
|
ocell.push_back(globID);
|
||||||
|
this->trans_.push_back(SI_trans(T[globID]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1032,28 +1206,57 @@ public:
|
|||||||
/// Retrieve number of active cells in graph.
|
/// Retrieve number of active cells in graph.
|
||||||
std::size_t numCells() const;
|
std::size_t numCells() const;
|
||||||
|
|
||||||
/// Retrive number of connections in graph.
|
/// Retrieve number of connections in graph.
|
||||||
std::size_t numConnections() const;
|
std::size_t numConnections() const;
|
||||||
|
|
||||||
/// Retrive neighbourship relations between active cells.
|
/// Retrieve the simulation scenario's active phases.
|
||||||
|
///
|
||||||
|
/// Mostly useful to determine the set of \c PhaseIndex values for which
|
||||||
|
/// flux() may return non-zero values.
|
||||||
|
const std::vector<PhaseIndex>& activePhases() const;
|
||||||
|
|
||||||
|
/// Retrieve neighbourship relations between active cells.
|
||||||
///
|
///
|
||||||
/// The \c i-th connection is between active cells \code
|
/// The \c i-th connection is between active cells \code
|
||||||
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i + 1]
|
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i + 1]
|
||||||
/// \endcode.
|
/// \endcode.
|
||||||
std::vector<int> neighbours() const;
|
std::vector<int> neighbours() const;
|
||||||
|
|
||||||
/// Retrive static pore-volume values on active cells only.
|
/// Retrieve static pore-volume values on active cells only.
|
||||||
///
|
///
|
||||||
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
||||||
/// restricted to those active cells for which the pore-volume is
|
/// restricted to those active cells for which the pore-volume is
|
||||||
/// strictly positive.
|
/// strictly positive.
|
||||||
std::vector<double> activePoreVolume() const;
|
std::vector<double> activePoreVolume() const;
|
||||||
|
|
||||||
const ::Opm::ECLResultData& rawResultData() const;
|
/// Retrieve static (background) transmissibility values on all
|
||||||
|
/// connections defined by \code neighbours() \endcode.
|
||||||
|
///
|
||||||
|
/// Specifically, \code transmissibility()[i] \endcode is the
|
||||||
|
/// transmissibility of the connection between cells \code
|
||||||
|
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i + 1]
|
||||||
|
/// \endcode.
|
||||||
|
std::vector<double> transmissibility() const;
|
||||||
|
|
||||||
|
/// Restrict dynamic result set data to single report step.
|
||||||
|
///
|
||||||
|
/// This method must be called before calling either flux() or
|
||||||
|
/// rawResultData().
|
||||||
|
///
|
||||||
|
/// \param[in] rptstep Selected temporal vector. Report-step ID.
|
||||||
|
///
|
||||||
|
/// \return Whether or not dynamic data for the requested report step
|
||||||
|
/// exists in the underlying result set identified in method
|
||||||
|
/// assignDataSource().
|
||||||
bool selectReportStep(const int rptstep) const;
|
bool selectReportStep(const int rptstep) const;
|
||||||
|
|
||||||
/// Retrive phase flux on all connections defined by \code neighbours()
|
/// Access underlying result set.
|
||||||
|
///
|
||||||
|
/// The result set dynamic data corresponds to the most recent call to
|
||||||
|
/// method selectReportStep().
|
||||||
|
const ::Opm::ECLResultData& rawResultData() const;
|
||||||
|
|
||||||
|
/// Retrieve phase flux on all connections defined by \code neighbours()
|
||||||
/// \endcode.
|
/// \endcode.
|
||||||
///
|
///
|
||||||
/// \param[in] phase Canonical phase for which to retrive flux.
|
/// \param[in] phase Canonical phase for which to retrive flux.
|
||||||
@@ -1068,6 +1271,14 @@ public:
|
|||||||
std::vector<double>
|
std::vector<double>
|
||||||
flux(const PhaseIndex phase) const;
|
flux(const PhaseIndex phase) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
rawLinearisedCellData(const std::string& vector) const;
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
linearisedCellData(const std::string& vector,
|
||||||
|
UnitConvention unit) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Collection of non-Cartesian neighbourship relations attributed to a
|
/// Collection of non-Cartesian neighbourship relations attributed to a
|
||||||
/// particular ECL keyword set (i.e., one of NNC{1,2}, NNC{G,L}, NNCLL).
|
/// particular ECL keyword set (i.e., one of NNC{1,2}, NNC{G,L}, NNCLL).
|
||||||
@@ -1186,9 +1397,14 @@ private:
|
|||||||
/// \param[in] offset Start index into global linear number for all
|
/// \param[in] offset Start index into global linear number for all
|
||||||
/// active grids.
|
/// active grids.
|
||||||
///
|
///
|
||||||
|
/// \param[in] trans_unit Unit of measurement of transmissibility
|
||||||
|
/// field stored in result set. Used to convert values to the
|
||||||
|
/// strict SI conventions (i.e., rm^3).
|
||||||
|
///
|
||||||
/// \param[in] nnc Non-neighbouring connection from result set.
|
/// \param[in] nnc Non-neighbouring connection from result set.
|
||||||
void add(const std::vector<ECL::CartesianGridData>& grids,
|
void add(const std::vector<ECL::CartesianGridData>& grids,
|
||||||
const std::vector<std::size_t>& offset,
|
const std::vector<std::size_t>& offset,
|
||||||
|
const double trans_unit,
|
||||||
const ecl_nnc_type& nnc);
|
const ecl_nnc_type& nnc);
|
||||||
|
|
||||||
std::vector<Category> allCategories() const;
|
std::vector<Category> allCategories() const;
|
||||||
@@ -1199,6 +1415,10 @@ private:
|
|||||||
/// Access all active non-neighbouring connections.
|
/// Access all active non-neighbouring connections.
|
||||||
const std::vector<int>& getNeighbours() const;
|
const std::vector<int>& getNeighbours() const;
|
||||||
|
|
||||||
|
/// Access transmissibility field of all active non-neighbouring
|
||||||
|
/// connections. Numerical values in strict SI units (rm^3).
|
||||||
|
const std::vector<double>& transmissibility() const;
|
||||||
|
|
||||||
/// Retrieve all non-neighbouring connections of a particular
|
/// Retrieve all non-neighbouring connections of a particular
|
||||||
/// category (i.e., pertaining to a particular set of keywords).
|
/// category (i.e., pertaining to a particular set of keywords).
|
||||||
///
|
///
|
||||||
@@ -1214,6 +1434,13 @@ private:
|
|||||||
/// in linear numbering of all model's active cells.
|
/// in linear numbering of all model's active cells.
|
||||||
std::vector<int> neigh_;
|
std::vector<int> neigh_;
|
||||||
|
|
||||||
|
/// Transmissibility of non-Cartesian (non-neighbouring) connections.
|
||||||
|
///
|
||||||
|
/// Note that \code trans_[i] \endcode is the transmissibility of
|
||||||
|
/// the connection between cells \code neigh_[2*i + 0] \endcode and
|
||||||
|
/// \code neigh_[2*i + 1] \endcode.
|
||||||
|
std::vector<double> trans_;
|
||||||
|
|
||||||
/// Collection of
|
/// Collection of
|
||||||
KeywordIndexMap keywords_;
|
KeywordIndexMap keywords_;
|
||||||
|
|
||||||
@@ -1221,7 +1448,9 @@ private:
|
|||||||
///
|
///
|
||||||
/// Simplifies implementation of ctor.
|
/// Simplifies implementation of ctor.
|
||||||
///
|
///
|
||||||
/// \param[in] cat Class
|
/// \param[in] cat Requested category of flux relation.
|
||||||
|
///
|
||||||
|
/// \return Flux relation of type \p cat.
|
||||||
FluxRelation makeRelation(const Category cat) const;
|
FluxRelation makeRelation(const Category cat) const;
|
||||||
|
|
||||||
/// Identify connection category from connection's grids.
|
/// Identify connection category from connection's grids.
|
||||||
@@ -1284,6 +1513,11 @@ private:
|
|||||||
/// range \code [0 .. numCells()) \endcode).
|
/// range \code [0 .. numCells()) \endcode).
|
||||||
std::vector<std::size_t> activeOffset_;
|
std::vector<std::size_t> activeOffset_;
|
||||||
|
|
||||||
|
/// Set of active phases in result set. Derived from .INIT on the
|
||||||
|
/// assumption that the set of active phases does not change throughout
|
||||||
|
/// the simulation run.
|
||||||
|
std::vector<PhaseIndex> activePhases_;
|
||||||
|
|
||||||
/// Current result set.
|
/// Current result set.
|
||||||
std::unique_ptr<ECLResultData> src_;
|
std::unique_ptr<ECLResultData> src_;
|
||||||
|
|
||||||
@@ -1299,6 +1533,11 @@ private:
|
|||||||
void defineNNCs(const ecl_grid_type* G,
|
void defineNNCs(const ecl_grid_type* G,
|
||||||
const ::Opm::ECLResultData& init);
|
const ::Opm::ECLResultData& init);
|
||||||
|
|
||||||
|
/// Extract scenario's set of active phases.
|
||||||
|
///
|
||||||
|
/// Writes to activePhases_.
|
||||||
|
void defineActivePhases(const ::Opm::ECLResultData& init);
|
||||||
|
|
||||||
/// Compute ECL vector basename for particular phase flux.
|
/// Compute ECL vector basename for particular phase flux.
|
||||||
///
|
///
|
||||||
/// \param[in] phase Canonical phase for which to derive ECL vector
|
/// \param[in] phase Canonical phase for which to derive ECL vector
|
||||||
@@ -1312,15 +1551,29 @@ private:
|
|||||||
/// Extract flux values corresponding to particular result set vector
|
/// Extract flux values corresponding to particular result set vector
|
||||||
/// for all identified non-neighbouring connections.
|
/// for all identified non-neighbouring connections.
|
||||||
///
|
///
|
||||||
|
/// \tparam[in] GetFluxUnit Type of function object for computing the
|
||||||
|
/// grid-dependent flux unit.
|
||||||
|
///
|
||||||
/// \param[in] vector Result set vector prefix. Typically computed by
|
/// \param[in] vector Result set vector prefix. Typically computed by
|
||||||
/// method flowVector().
|
/// method flowVector().
|
||||||
///
|
///
|
||||||
|
/// \param[in] fluxUnit Function object for computing grid-dependent
|
||||||
|
/// flux units. Must support the syntax
|
||||||
|
/// \code
|
||||||
|
/// unit = fluxUnit(gridID)
|
||||||
|
/// \endcode
|
||||||
|
/// with 'gridID' being a non-negative \c int that identifies a
|
||||||
|
/// particular model grid (zero for the main grid and positive for
|
||||||
|
/// LGRs) and 'unit' a positive floating-point value.
|
||||||
|
///
|
||||||
/// \param[in,out] flux Numerical values of result set vector. On
|
/// \param[in,out] flux Numerical values of result set vector. On
|
||||||
/// input, contains all values corresponding to all fully Cartesian
|
/// input, contains all values corresponding to all fully Cartesian
|
||||||
/// connections across all active grids. On output additionally
|
/// connections across all active grids. On output additionally
|
||||||
/// contains those values that correspond to the non-neighbouring
|
/// contains those values that correspond to the non-neighbouring
|
||||||
/// connections (appended onto \p flux).
|
/// connections (appended onto \p flux).
|
||||||
|
template <class GetFluxUnit>
|
||||||
void fluxNNC(const std::string& vector,
|
void fluxNNC(const std::string& vector,
|
||||||
|
GetFluxUnit&& fluxUnit,
|
||||||
std::vector<double>& flux) const;
|
std::vector<double>& flux) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1371,6 +1624,7 @@ void
|
|||||||
Opm::ECLGraph::Impl::
|
Opm::ECLGraph::Impl::
|
||||||
NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
||||||
const std::vector<std::size_t>& offset,
|
const std::vector<std::size_t>& offset,
|
||||||
|
const double trans_unit,
|
||||||
const ecl_nnc_type& nnc)
|
const ecl_nnc_type& nnc)
|
||||||
{
|
{
|
||||||
if (! this->isViable(grid, nnc)) {
|
if (! this->isViable(grid, nnc)) {
|
||||||
@@ -1395,6 +1649,10 @@ NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
|||||||
this->neigh_.push_back(o + c);
|
this->neigh_.push_back(o + c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture transmissibility field to support on-demand flux calculations
|
||||||
|
// if flux fields are not output to the on-disk result set.
|
||||||
|
this->trans_.push_back(unit::convert::from(nnc.trans, trans_unit));
|
||||||
|
|
||||||
const auto cat = this->classifyConnection(nnc.grid_nr1, nnc.grid_nr2);
|
const auto cat = this->classifyConnection(nnc.grid_nr1, nnc.grid_nr2);
|
||||||
|
|
||||||
auto entry = NonNeighKeywordIndexSet::Map {
|
auto entry = NonNeighKeywordIndexSet::Map {
|
||||||
@@ -1411,9 +1669,10 @@ NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
|||||||
std::size_t
|
std::size_t
|
||||||
Opm::ECLGraph::Impl::NNC::numConnections() const
|
Opm::ECLGraph::Impl::NNC::numConnections() const
|
||||||
{
|
{
|
||||||
assert (this->neigh_.size() % 2 == 0);
|
assert ((this->neigh_.size() % 2) == 0);
|
||||||
|
assert ((this->neigh_.size() / 2) == this->trans_.size());
|
||||||
|
|
||||||
return this->neigh_.size() / 2;
|
return this->trans_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<int>&
|
const std::vector<int>&
|
||||||
@@ -1422,6 +1681,12 @@ Opm::ECLGraph::Impl::NNC::getNeighbours() const
|
|||||||
return this->neigh_;
|
return this->neigh_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<double>&
|
||||||
|
Opm::ECLGraph::Impl::NNC::transmissibility() const
|
||||||
|
{
|
||||||
|
return this->trans_;
|
||||||
|
}
|
||||||
|
|
||||||
const Opm::ECLGraph::Impl::NNC::FluxRelation&
|
const Opm::ECLGraph::Impl::NNC::FluxRelation&
|
||||||
Opm::ECLGraph::Impl::NNC::getRelations(const Category& cat) const
|
Opm::ECLGraph::Impl::NNC::getRelations(const Category& cat) const
|
||||||
{
|
{
|
||||||
@@ -1521,6 +1786,7 @@ Opm::ECLGraph::Impl::Impl(const Path& grid, const Path& init)
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->defineNNCs(G.get(), I);
|
this->defineNNCs(G.get(), I);
|
||||||
|
this->defineActivePhases(I);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1579,6 +1845,12 @@ Opm::ECLGraph::Impl::numConnections() const
|
|||||||
return nconn + this->nnc_.numConnections();
|
return nconn + this->nnc_.numConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<Opm::ECLGraph::PhaseIndex>&
|
||||||
|
Opm::ECLGraph::Impl::activePhases() const
|
||||||
|
{
|
||||||
|
return this->activePhases_;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int>
|
std::vector<int>
|
||||||
Opm::ECLGraph::Impl::neighbours() const
|
Opm::ECLGraph::Impl::neighbours() const
|
||||||
{
|
{
|
||||||
@@ -1625,6 +1897,34 @@ Opm::ECLGraph::Impl::activePoreVolume() const
|
|||||||
return pvol;
|
return pvol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
Opm::ECLGraph::Impl::transmissibility() const
|
||||||
|
{
|
||||||
|
auto trans = std::vector<double>{};
|
||||||
|
|
||||||
|
// Recall: this->numConnections() includes NNCs.
|
||||||
|
const auto totconn = this->numConnections();
|
||||||
|
trans.reserve(totconn);
|
||||||
|
|
||||||
|
for (const auto& G : this->grid_) {
|
||||||
|
const auto& Ti = G.transmissibility();
|
||||||
|
|
||||||
|
trans.insert(trans.end(), Ti.begin(), Ti.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->nnc_.numConnections() > 0) {
|
||||||
|
const auto& tranNNC = this->nnc_.transmissibility();
|
||||||
|
|
||||||
|
trans.insert(trans.end(), tranNNC.begin(), tranNNC.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trans.size() < totconn) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
const ::Opm::ECLResultData&
|
const ::Opm::ECLResultData&
|
||||||
Opm::ECLGraph::Impl::rawResultData() const
|
Opm::ECLGraph::Impl::rawResultData() const
|
||||||
{
|
{
|
||||||
@@ -1640,6 +1940,11 @@ std::vector<double>
|
|||||||
Opm::ECLGraph::Impl::
|
Opm::ECLGraph::Impl::
|
||||||
flux(const PhaseIndex phase) const
|
flux(const PhaseIndex phase) const
|
||||||
{
|
{
|
||||||
|
auto fluxUnit = [this](const int gridID)
|
||||||
|
{
|
||||||
|
return ::ECL::getUnitSystem(*this->src_, gridID)->reservoirRate();
|
||||||
|
};
|
||||||
|
|
||||||
const auto vector = this->flowVector(phase);
|
const auto vector = this->flowVector(phase);
|
||||||
|
|
||||||
auto v = std::vector<double>{};
|
auto v = std::vector<double>{};
|
||||||
@@ -1650,7 +1955,8 @@ flux(const PhaseIndex phase) const
|
|||||||
v.reserve(totconn);
|
v.reserve(totconn);
|
||||||
|
|
||||||
for (const auto& G : this->grid_) {
|
for (const auto& G : this->grid_) {
|
||||||
const auto& q = G.connectionData(*this->src_, vector);
|
const auto& q =
|
||||||
|
G.connectionData(*this->src_, vector, fluxUnit(G.gridID()));
|
||||||
|
|
||||||
if (q.empty()) {
|
if (q.empty()) {
|
||||||
// Flux vector invalid unless all grids provide this result
|
// Flux vector invalid unless all grids provide this result
|
||||||
@@ -1665,7 +1971,7 @@ flux(const PhaseIndex phase) const
|
|||||||
// Model includes non-neighbouring connections such as faults and/or
|
// Model includes non-neighbouring connections such as faults and/or
|
||||||
// local grid refinement. Extract pertinent flux values for these
|
// local grid refinement. Extract pertinent flux values for these
|
||||||
// connections.
|
// connections.
|
||||||
this->fluxNNC(vector, v);
|
this->fluxNNC(vector, std::move(fluxUnit), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.size() < totconn) {
|
if (v.size() < totconn) {
|
||||||
@@ -1677,17 +1983,81 @@ flux(const PhaseIndex phase) const
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Opm {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
ECLGraph::Impl::rawLinearisedCellData(const std::string& vector) const
|
||||||
|
{
|
||||||
|
auto x = std::vector<T>{}; x.reserve(this->numCells());
|
||||||
|
|
||||||
|
for (const auto& G : this->grid_) {
|
||||||
|
const auto xi = G.activeCellData<T>(*this->src_, vector);
|
||||||
|
|
||||||
|
x.insert(x.end(), std::begin(xi), std::end(xi));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.size() != this->numCells()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
} // namespace Opm
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
Opm::ECLGraph::Impl::linearisedCellData(const std::string& vector,
|
||||||
|
UnitConvention unit) const
|
||||||
|
{
|
||||||
|
auto x = std::vector<double>{}; x.reserve(this->numCells());
|
||||||
|
|
||||||
|
for (const auto& G : this->grid_) {
|
||||||
|
const auto xi = G.activeCellData<double>(*this->src_, vector);
|
||||||
|
|
||||||
|
if (xi.empty()) { continue; }
|
||||||
|
|
||||||
|
// Note: Compensate for incrementing Grid ID above.
|
||||||
|
const auto usys =
|
||||||
|
ECL::getUnitSystem(*this->src_, G.gridID());
|
||||||
|
|
||||||
|
// Note: 'usys' (generally, std::unique_ptr<>) does not support
|
||||||
|
// regular PMF syntax (i.e., operator->*()).
|
||||||
|
const auto vector_unit = ((*usys).*unit)();
|
||||||
|
|
||||||
|
std::transform(std::begin(xi), std::end(xi),
|
||||||
|
std::back_inserter(x),
|
||||||
|
[vector_unit](const double value)
|
||||||
|
{
|
||||||
|
return ::Opm::unit::convert::from(value, vector_unit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.size() != this->numCells()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Opm::ECLGraph::Impl::defineNNCs(const ecl_grid_type* G,
|
Opm::ECLGraph::Impl::defineNNCs(const ecl_grid_type* G,
|
||||||
const ::Opm::ECLResultData& init)
|
const ::Opm::ECLResultData& init)
|
||||||
{
|
{
|
||||||
|
// Assume all transmissibilites in the result set follow the same unit
|
||||||
|
// conventions.
|
||||||
|
|
||||||
|
const auto trans_unit =
|
||||||
|
ECL::getUnitSystem(init, 0)->transmissibility();
|
||||||
|
|
||||||
for (const auto& nnc : ECL::loadNNC(G, init)) {
|
for (const auto& nnc : ECL::loadNNC(G, init)) {
|
||||||
this->nnc_.add(this->grid_, this->activeOffset_, nnc);
|
this->nnc_.add(this->grid_, this->activeOffset_, trans_unit, nnc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class GetFluxUnit>
|
||||||
void
|
void
|
||||||
Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
||||||
|
GetFluxUnit&& fluxUnit,
|
||||||
std::vector<double>& flux) const
|
std::vector<double>& flux) const
|
||||||
{
|
{
|
||||||
auto v = std::vector<double>(this->nnc_.numConnections(), 0.0);
|
auto v = std::vector<double>(this->nnc_.numConnections(), 0.0);
|
||||||
@@ -1697,13 +2067,11 @@ Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
|||||||
const auto& rel = this->nnc_.getRelations(cat);
|
const auto& rel = this->nnc_.getRelations(cat);
|
||||||
const auto fluxID = rel.makeKeyword(vector);
|
const auto fluxID = rel.makeKeyword(vector);
|
||||||
|
|
||||||
auto gridID = 0;
|
|
||||||
for (const auto& G : this->grid_) {
|
for (const auto& G : this->grid_) {
|
||||||
const auto& iset = rel.indexSet().getGridCollection(gridID);
|
const auto gridID = G.gridID();
|
||||||
|
|
||||||
// Must increment grid ID irrespective of early break of
|
const auto& iset =
|
||||||
// iteration.
|
rel.indexSet().getGridCollection(gridID);
|
||||||
gridID += 1;
|
|
||||||
|
|
||||||
if (iset.empty()) {
|
if (iset.empty()) {
|
||||||
// No NNCs for this category in this grid. Skip.
|
// No NNCs for this category in this grid. Skip.
|
||||||
@@ -1719,13 +2087,16 @@ Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto flux_unit = fluxUnit(gridID);
|
||||||
|
|
||||||
// Data fully available for (category,gridID). Assign
|
// Data fully available for (category,gridID). Assign
|
||||||
// approriate subset of NNC flux vector.
|
// approriate subset of NNC flux vector.
|
||||||
for (const auto& ix : iset) {
|
for (const auto& ix : iset) {
|
||||||
assert (ix.neighIdx < v.size());
|
assert (ix.neighIdx < v.size());
|
||||||
assert (ix.kwIdx < q.size());
|
assert (ix.kwIdx < q.size());
|
||||||
|
|
||||||
v[ix.neighIdx] = q[ix.kwIdx];
|
v[ix.neighIdx] =
|
||||||
|
unit::convert::from(q[ix.kwIdx], flux_unit);
|
||||||
|
|
||||||
assigned[ix.neighIdx] = true;
|
assigned[ix.neighIdx] = true;
|
||||||
}
|
}
|
||||||
@@ -1745,6 +2116,32 @@ Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Opm::ECLGraph::Impl::
|
||||||
|
defineActivePhases(const ::Opm::ECLResultData& init)
|
||||||
|
{
|
||||||
|
const auto ih =
|
||||||
|
init.keywordData<int>(INTEHEAD_KW, ECL_GRID_MAINGRID_LGR_NR);
|
||||||
|
|
||||||
|
const auto phaseMask =
|
||||||
|
static_cast<unsigned int>(ih[INTEHEAD_PHASE_INDEX]);
|
||||||
|
|
||||||
|
using Check = std::pair<PhaseIndex, unsigned int>;
|
||||||
|
|
||||||
|
const auto allPhases = std::vector<Check> {
|
||||||
|
{ PhaseIndex::Aqua , (1u << 1) },
|
||||||
|
{ PhaseIndex::Liquid, (1u << 0) },
|
||||||
|
{ PhaseIndex::Vapour, (1u << 2) },
|
||||||
|
};
|
||||||
|
|
||||||
|
this->activePhases_.clear();
|
||||||
|
for (const auto& phase : allPhases) {
|
||||||
|
if ((phase.second & phaseMask) != 0) {
|
||||||
|
this->activePhases_.push_back(phase.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
Opm::ECLGraph::Impl::
|
Opm::ECLGraph::Impl::
|
||||||
flowVector(const PhaseIndex phase) const
|
flowVector(const PhaseIndex phase) const
|
||||||
@@ -1834,6 +2231,12 @@ Opm::ECLGraph::numConnections() const
|
|||||||
return this->pImpl_->numConnections();
|
return this->pImpl_->numConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<Opm::ECLGraph::PhaseIndex>&
|
||||||
|
Opm::ECLGraph::activePhases() const
|
||||||
|
{
|
||||||
|
return this->pImpl_->activePhases();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> Opm::ECLGraph::neighbours() const
|
std::vector<int> Opm::ECLGraph::neighbours() const
|
||||||
{
|
{
|
||||||
return this->pImpl_->neighbours();
|
return this->pImpl_->neighbours();
|
||||||
@@ -1844,6 +2247,11 @@ std::vector<double> Opm::ECLGraph::poreVolume() const
|
|||||||
return this->pImpl_->activePoreVolume();
|
return this->pImpl_->activePoreVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<double> Opm::ECLGraph::transmissibility() const
|
||||||
|
{
|
||||||
|
return this->pImpl_->transmissibility();
|
||||||
|
}
|
||||||
|
|
||||||
bool Opm::ECLGraph::selectReportStep(const int rptstep) const
|
bool Opm::ECLGraph::selectReportStep(const int rptstep) const
|
||||||
{
|
{
|
||||||
return this->pImpl_->selectReportStep(rptstep);
|
return this->pImpl_->selectReportStep(rptstep);
|
||||||
@@ -1861,3 +2269,28 @@ flux(const PhaseIndex phase) const
|
|||||||
{
|
{
|
||||||
return this->pImpl_->flux(phase);
|
return this->pImpl_->flux(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Opm {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
ECLGraph::rawLinearisedCellData(const std::string& vector) const
|
||||||
|
{
|
||||||
|
return this->pImpl_->rawLinearisedCellData<T>(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit instantiations for the element types we care about.
|
||||||
|
template std::vector<int>
|
||||||
|
ECLGraph::rawLinearisedCellData<int>(const std::string& vector) const;
|
||||||
|
|
||||||
|
template std::vector<double>
|
||||||
|
ECLGraph::rawLinearisedCellData<double>(const std::string& vector) const;
|
||||||
|
|
||||||
|
} // namespace Opm
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
Opm::ECLGraph::linearisedCellData(const std::string& vector,
|
||||||
|
UnitConvention unit) const
|
||||||
|
{
|
||||||
|
return this->pImpl_->linearisedCellData(vector, unit);
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#define OPM_ECLGRAPH_HEADER_INCLUDED
|
#define OPM_ECLGRAPH_HEADER_INCLUDED
|
||||||
|
|
||||||
#include <opm/utility/ECLResultData.hpp>
|
#include <opm/utility/ECLResultData.hpp>
|
||||||
|
#include <opm/utility/ECLUnitHandling.hpp>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -46,6 +47,9 @@ namespace Opm {
|
|||||||
public:
|
public:
|
||||||
using Path = boost::filesystem::path;
|
using Path = boost::filesystem::path;
|
||||||
|
|
||||||
|
/// Enum for indicating requested phase from the flux() method.
|
||||||
|
enum class PhaseIndex { Aqua = 0, Liquid = 1, Vapour = 2 };
|
||||||
|
|
||||||
/// Disabled default constructor.
|
/// Disabled default constructor.
|
||||||
ECLGraph() = delete;
|
ECLGraph() = delete;
|
||||||
|
|
||||||
@@ -124,6 +128,12 @@ namespace Opm {
|
|||||||
/// Retrieve number of connections in graph.
|
/// Retrieve number of connections in graph.
|
||||||
std::size_t numConnections() const;
|
std::size_t numConnections() const;
|
||||||
|
|
||||||
|
/// Retrieve the simulation scenario's active phases.
|
||||||
|
///
|
||||||
|
/// Mostly useful to determine the set of \c PhaseIndex values for
|
||||||
|
/// which flux() will return non-zero values if data available.
|
||||||
|
const std::vector<PhaseIndex>& activePhases() const;
|
||||||
|
|
||||||
/// Retrieve neighbourship relations between active cells.
|
/// Retrieve neighbourship relations between active cells.
|
||||||
///
|
///
|
||||||
/// The \c i-th connection is between active cells \code
|
/// The \c i-th connection is between active cells \code
|
||||||
@@ -135,9 +145,18 @@ namespace Opm {
|
|||||||
///
|
///
|
||||||
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
||||||
/// restricted to those active cells for which the pore-volume is
|
/// restricted to those active cells for which the pore-volume is
|
||||||
/// strictly positive.
|
/// strictly positive. Numerical values in SI units (rm^3).
|
||||||
std::vector<double> poreVolume() const;
|
std::vector<double> poreVolume() const;
|
||||||
|
|
||||||
|
/// Retrieve static (background) transmissibility values on all
|
||||||
|
/// connections defined by \code neighbours() \endcode.
|
||||||
|
///
|
||||||
|
/// Specifically, \code transmissibility()[i] \endcode is the
|
||||||
|
/// transmissibility of the connection between cells \code
|
||||||
|
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i + 1]
|
||||||
|
/// \endcode.
|
||||||
|
std::vector<double> transmissibility() const;
|
||||||
|
|
||||||
/// Restrict dynamic result set data to single report step.
|
/// Restrict dynamic result set data to single report step.
|
||||||
///
|
///
|
||||||
/// This method must be called before calling either flux() or
|
/// This method must be called before calling either flux() or
|
||||||
@@ -156,21 +175,57 @@ namespace Opm {
|
|||||||
/// to method selectReportStep().
|
/// to method selectReportStep().
|
||||||
const ::Opm::ECLResultData& rawResultData() const;
|
const ::Opm::ECLResultData& rawResultData() const;
|
||||||
|
|
||||||
/// Enum for indicating requested phase from the flux() method.
|
/// Retrieve phase flux on all connections defined by \code
|
||||||
enum class PhaseIndex { Aqua = 0, Liquid = 1, Vapour = 2 };
|
|
||||||
|
|
||||||
/// Retrive phase flux on all connections defined by \code
|
|
||||||
/// neighbours() \endcode.
|
/// neighbours() \endcode.
|
||||||
///
|
///
|
||||||
/// \param[in] phase Canonical phase for which to retrive flux.
|
/// \param[in] phase Canonical phase for which to retrieve flux.
|
||||||
///
|
///
|
||||||
/// \return Flux values corresponding to selected phase. Empty if
|
/// \return Flux values corresponding to selected phase. Empty if
|
||||||
/// unavailable in the result set (e.g., when querying the gas
|
/// unavailable in the result set (e.g., when querying the gas
|
||||||
/// flux in an oil/water system or if no flux values at all were
|
/// flux in an oil/water system or if no flux values at all were
|
||||||
/// output to the restart file).
|
/// output to the restart file). Numerical values in SI units
|
||||||
|
/// (rm^3/s).
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
flux(const PhaseIndex phase) const;
|
flux(const PhaseIndex phase) const;
|
||||||
|
|
||||||
|
/// Retrieve result set vector from current view (e.g., particular
|
||||||
|
/// report step) linearised on active cells.
|
||||||
|
///
|
||||||
|
/// \tparam T Element type of result set vector.
|
||||||
|
///
|
||||||
|
/// \param[in] vector Name of result set vector.
|
||||||
|
///
|
||||||
|
/// \return Result set vector linearised on active cells.
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T>
|
||||||
|
rawLinearisedCellData(const std::string& vector) const;
|
||||||
|
|
||||||
|
/// Convenience type alias for \c UnitSystem PMFs (pointer to member
|
||||||
|
/// function).
|
||||||
|
typedef double (ECLUnits::UnitSystem::*UnitConvention)() const;
|
||||||
|
|
||||||
|
/// Retrieve floating-point result set vector from current view
|
||||||
|
/// (e.g., particular report step) linearised on active cells and
|
||||||
|
/// converted to strict SI unit conventions.
|
||||||
|
///
|
||||||
|
/// Typical call:
|
||||||
|
/// \code
|
||||||
|
/// const auto press =
|
||||||
|
/// lCD("PRESSURE", &ECLUnits::UnitSystem::pressure);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// \param[in] vector Name of result set vector.
|
||||||
|
///
|
||||||
|
/// \param[in] unit Call-back hook in \c UnitSystem implementation
|
||||||
|
/// that enables converting the raw result data to strict SI unit
|
||||||
|
/// conventions. Hook is called for each grid in the result set.
|
||||||
|
///
|
||||||
|
/// \return Result set vector linearised on active cells, converted
|
||||||
|
/// to strict SI unit conventions.
|
||||||
|
std::vector<double>
|
||||||
|
linearisedCellData(const std::string& vector,
|
||||||
|
UnitConvention unit) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Implementation class.
|
/// Implementation class.
|
||||||
class Impl;
|
class Impl;
|
||||||
|
|||||||
209
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLUnitHandling.cpp
vendored
Normal file
209
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/opm/utility/ECLUnitHandling.cpp
vendored
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
#include <opm/utility/ECLUnitHandling.hpp>
|
||||||
|
|
||||||
|
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <ert/ecl/ecl_util.h>
|
||||||
|
|
||||||
|
namespace Opm { namespace ECLUnits {
|
||||||
|
namespace Impl {
|
||||||
|
ert_ecl_unit_enum getUnitConvention(const int usys);
|
||||||
|
|
||||||
|
template <ert_ecl_unit_enum convention>
|
||||||
|
class USys;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class USys<ECL_METRIC_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double pressure() const override
|
||||||
|
{
|
||||||
|
return Metric::Pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirRate() const override
|
||||||
|
{
|
||||||
|
return Metric::ReservoirVolume / Metric::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirVolume() const override
|
||||||
|
{
|
||||||
|
return Metric::ReservoirVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double time() const override
|
||||||
|
{
|
||||||
|
return Metric::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double transmissibility() const override
|
||||||
|
{
|
||||||
|
return Metric::Transmissibility;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class USys<ECL_FIELD_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double pressure() const override
|
||||||
|
{
|
||||||
|
return Field::Pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirRate() const override
|
||||||
|
{
|
||||||
|
return Field::ReservoirVolume / Field::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirVolume() const override
|
||||||
|
{
|
||||||
|
return Field::ReservoirVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double time() const override
|
||||||
|
{
|
||||||
|
return Field::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double transmissibility() const override
|
||||||
|
{
|
||||||
|
return Field::Transmissibility;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class USys<ECL_LAB_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double pressure() const override
|
||||||
|
{
|
||||||
|
return Lab::Pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirRate() const override
|
||||||
|
{
|
||||||
|
return Lab::ReservoirVolume / Lab::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirVolume() const override
|
||||||
|
{
|
||||||
|
return Lab::ReservoirVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double time() const override
|
||||||
|
{
|
||||||
|
return Lab::Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double transmissibility() const override
|
||||||
|
{
|
||||||
|
return Lab::Transmissibility;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class USys<ECL_PVT_M_UNITS> : public ::Opm::ECLUnits::UnitSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double pressure() const override
|
||||||
|
{
|
||||||
|
return unit::atm;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirRate() const override
|
||||||
|
{
|
||||||
|
using namespace prefix;
|
||||||
|
using namespace unit;
|
||||||
|
|
||||||
|
return cubic(meter) / day;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double reservoirVolume() const override
|
||||||
|
{
|
||||||
|
using namespace prefix;
|
||||||
|
using namespace unit;
|
||||||
|
|
||||||
|
return cubic(meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double time() const override
|
||||||
|
{
|
||||||
|
return unit::day;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double transmissibility() const override
|
||||||
|
{
|
||||||
|
using namespace prefix;
|
||||||
|
using namespace unit;
|
||||||
|
|
||||||
|
return centi*Poise * cubic(meter) / (day * atm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Impl
|
||||||
|
}} // namespace Opm::ECLUnits
|
||||||
|
|
||||||
|
ert_ecl_unit_enum
|
||||||
|
Opm::ECLUnits::Impl::getUnitConvention(const int usys)
|
||||||
|
{
|
||||||
|
switch (usys) {
|
||||||
|
case 1: return ECL_METRIC_UNITS;
|
||||||
|
case 2: return ECL_FIELD_UNITS;
|
||||||
|
case 3: return ECL_LAB_UNITS;
|
||||||
|
case 4: return ECL_PVT_M_UNITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unsupported Unit Convention: "
|
||||||
|
+ std::to_string(usys));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<const ::Opm::ECLUnits::UnitSystem>
|
||||||
|
Opm::ECLUnits::createUnitSystem(const int usys)
|
||||||
|
{
|
||||||
|
using UPtr =
|
||||||
|
std::unique_ptr<const ::Opm::ECLUnits::UnitSystem>;
|
||||||
|
|
||||||
|
switch (Impl::getUnitConvention(usys)) {
|
||||||
|
case ECL_METRIC_UNITS:
|
||||||
|
return UPtr{ new Impl::USys<ECL_METRIC_UNITS>{} };
|
||||||
|
|
||||||
|
case ECL_FIELD_UNITS:
|
||||||
|
return UPtr{ new Impl::USys<ECL_FIELD_UNITS>{} };
|
||||||
|
|
||||||
|
case ECL_LAB_UNITS:
|
||||||
|
return UPtr{ new Impl::USys<ECL_LAB_UNITS>{} };
|
||||||
|
|
||||||
|
case ECL_PVT_M_UNITS:
|
||||||
|
return UPtr{ new Impl::USys<ECL_PVT_M_UNITS>{} };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unsupported Unit Convention: "
|
||||||
|
+ std::to_string(usys));
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPM_ECLUNITHANDLING_HEADER_INCLUDED
|
||||||
|
#define OPM_ECLUNITHANDLING_HEADER_INCLUDED
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Opm {
|
||||||
|
|
||||||
|
namespace ECLUnits {
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<const UnitSystem>
|
||||||
|
createUnitSystem(const int usys);
|
||||||
|
|
||||||
|
} // namespace ECLUnits
|
||||||
|
} // namespace Opm
|
||||||
|
|
||||||
|
#endif // OPM_ECLUNITHANDLING_HEADER_INCLUDED
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <opm/utility/ECLWellSolution.hpp>
|
#include <opm/utility/ECLWellSolution.hpp>
|
||||||
#include <opm/utility/ECLResultData.hpp>
|
#include <opm/utility/ECLResultData.hpp>
|
||||||
|
#include <opm/utility/ECLUnitHandling.hpp>
|
||||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||||
#include <ert/ecl/ecl_kw_magic.h>
|
#include <ert/ecl/ecl_kw_magic.h>
|
||||||
#include <ert/ecl_well/well_const.h>
|
#include <ert/ecl_well/well_const.h>
|
||||||
@@ -108,15 +109,7 @@ namespace Opm
|
|||||||
// Reservoir rate units from code used in INTEHEAD.
|
// Reservoir rate units from code used in INTEHEAD.
|
||||||
double resRateUnit(const int unit_code)
|
double resRateUnit(const int unit_code)
|
||||||
{
|
{
|
||||||
using prefix::centi;
|
return ECLUnits::createUnitSystem(unit_code)->reservoirRate();
|
||||||
using namespace unit;
|
|
||||||
|
|
||||||
switch (unit_code) {
|
|
||||||
case INTEHEAD::Metric: return cubic(meter)/day; // m^3/day
|
|
||||||
case INTEHEAD::Field: return stb/day; // stb/day
|
|
||||||
case INTEHEAD::Lab: return cubic(centi*meter)/hour; // (cm)^3/h
|
|
||||||
default: throw std::runtime_error("Unknown unit code from INTEHEAD: " + std::to_string(unit_code));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
550
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/runAcceptanceTest.cpp
vendored
Normal file
550
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/runAcceptanceTest.cpp
vendored
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
/*
|
||||||
|
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 <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
#include <ert/ecl/ecl_file.h>
|
||||||
|
#include <ert/ecl/ecl_file_kw.h>
|
||||||
|
#include <ert/ecl/ecl_file_view.h>
|
||||||
|
#include <ert/ecl/ecl_kw.h>
|
||||||
|
#include <ert/ecl/ecl_kw_magic.h>
|
||||||
|
#include <ert/util/ert_unique_ptr.hpp>
|
||||||
|
|
||||||
|
namespace StringUtils {
|
||||||
|
namespace {
|
||||||
|
std::string trim(const std::string& s)
|
||||||
|
{
|
||||||
|
const auto anchor_ws =
|
||||||
|
boost::regex(R"~~(^\s+([^\s]+)\s+$)~~");
|
||||||
|
|
||||||
|
auto m = boost::smatch{};
|
||||||
|
|
||||||
|
if (boost::regex_match(s, m, anchor_ws)) {
|
||||||
|
return m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string& s)
|
||||||
|
{
|
||||||
|
if (s.empty()) {
|
||||||
|
// Single element vector whose only element is the empty
|
||||||
|
// string.
|
||||||
|
return { "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto sep = boost::regex(R"~~([\s,;.|]+)~~");
|
||||||
|
|
||||||
|
using TI = boost::sregex_token_iterator;
|
||||||
|
|
||||||
|
// vector<string>(begin, end)
|
||||||
|
//
|
||||||
|
// Range is every substring (i.e., token) in input string 's'
|
||||||
|
// that does NOT match 'sep'.
|
||||||
|
return{ TI(s.begin(), s.end(), sep, -1), TI{} };
|
||||||
|
}
|
||||||
|
} // namespace Anonymous
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct StringTo;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<int>
|
||||||
|
{
|
||||||
|
static int value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<double>
|
||||||
|
{
|
||||||
|
static double value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<std::string>
|
||||||
|
{
|
||||||
|
static std::string value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
int StringTo<int>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return std::stoi(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
double StringTo<double>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return std::stod(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringTo<std::string>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return trim(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VectorValue {
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s, std::true_type)
|
||||||
|
{
|
||||||
|
return split(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s, std::false_type)
|
||||||
|
{
|
||||||
|
const auto tokens = split(s);
|
||||||
|
|
||||||
|
auto ret = std::vector<T>{};
|
||||||
|
ret.reserve(tokens.size());
|
||||||
|
for (const auto& token : tokens) {
|
||||||
|
ret.push_back(StringTo<T>::value(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s)
|
||||||
|
{
|
||||||
|
return get<T>(s, typename std::is_same<T, std::string>::type());
|
||||||
|
}
|
||||||
|
} // namespace VectorValue
|
||||||
|
} // namespace StringUtils
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct PoreVolume
|
||||||
|
{
|
||||||
|
std::vector<double> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VectorDifference
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Vector = std::vector<double>;
|
||||||
|
using size_type = Vector::size_type;
|
||||||
|
|
||||||
|
VectorDifference(const Vector& x, const Vector& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y_.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return this->size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] - y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector& x_;
|
||||||
|
const Vector& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Vector1, class Vector2>
|
||||||
|
class VectorRatio
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = typename std::decay<
|
||||||
|
decltype(std::declval<Vector1>()[0])
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
VectorRatio(const Vector1& x, const Vector2& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return x_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] / y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector1& x_;
|
||||||
|
const Vector2& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorMeasurement
|
||||||
|
{
|
||||||
|
double volume;
|
||||||
|
double inf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorTolerance
|
||||||
|
{
|
||||||
|
double absolute;
|
||||||
|
double relative;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AggregateErrors
|
||||||
|
{
|
||||||
|
std::vector<ErrorMeasurement> absolute;
|
||||||
|
std::vector<ErrorMeasurement> relative;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReferenceToF
|
||||||
|
{
|
||||||
|
std::vector<double> forward;
|
||||||
|
std::vector<double> reverse;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class FieldVariable>
|
||||||
|
double volumeMetric(const PoreVolume& pv,
|
||||||
|
const FieldVariable& x)
|
||||||
|
{
|
||||||
|
if (x.size() != pv.data.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< pv.data.size() << ", but got ("
|
||||||
|
<< pv.data.size() << ", " << x.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto num = 0.0;
|
||||||
|
auto den = 0.0;
|
||||||
|
|
||||||
|
for (decltype(pv.data.size())
|
||||||
|
i = 0, n = pv.data.size(); i < n; ++i)
|
||||||
|
{
|
||||||
|
num += std::abs(x[i]) * pv.data[i];
|
||||||
|
den += pv.data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return num / den;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FieldVariable>
|
||||||
|
double pointMetric(const FieldVariable& x)
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<typename std::decay<decltype(std::abs(x[0]))>::type, double>::value,
|
||||||
|
"Field Variable Value Type Must be 'double'");
|
||||||
|
|
||||||
|
if (x.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto max = 0*x[0] - 1;
|
||||||
|
|
||||||
|
for (decltype(x.size())
|
||||||
|
i = 0, n = x.size(); i < n; ++i)
|
||||||
|
{
|
||||||
|
const auto t = std::abs(x[i]);
|
||||||
|
|
||||||
|
if (t > max) {
|
||||||
|
max = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::parameter::ParameterGroup& param)
|
||||||
|
{
|
||||||
|
const auto atol = param.getDefault("atol", 1.0e-8);
|
||||||
|
const auto rtol = param.getDefault("rtol", 5.0e-12);
|
||||||
|
|
||||||
|
return ErrorTolerance{ atol, rtol };
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceToF
|
||||||
|
loadReference(const ::Opm::parameter::ParameterGroup& param,
|
||||||
|
const int step,
|
||||||
|
const int nDigits)
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
using VRef = std::reference_wrapper<std::vector<double>>;
|
||||||
|
|
||||||
|
auto fname = fs::path(param.get<std::string>("ref-dir"));
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "tof-" << std::setw(nDigits) << std::setfill('0')
|
||||||
|
<< step << ".txt";
|
||||||
|
|
||||||
|
fname /= os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::ifstream input(fname);
|
||||||
|
|
||||||
|
if (! input) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Unable to Open Reference Data File "
|
||||||
|
<< fname.filename();
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tof = ReferenceToF{};
|
||||||
|
|
||||||
|
auto ref = std::array<VRef,2>{{ std::ref(tof.forward) ,
|
||||||
|
std::ref(tof.reverse) }};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto i = static_cast<decltype(ref[0].get().size())>(0);
|
||||||
|
auto t = 0.0;
|
||||||
|
|
||||||
|
while (input >> t) {
|
||||||
|
ref[i].get().push_back(t);
|
||||||
|
|
||||||
|
i = (i + 1) % 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tof.forward.size() != tof.reverse.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Unable to Read Consistent ToF Reference Data From "
|
||||||
|
<< fname.filename();
|
||||||
|
|
||||||
|
throw std::out_of_range(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return tof;
|
||||||
|
}
|
||||||
|
|
||||||
|
void computeErrors(const PoreVolume& pv,
|
||||||
|
const std::vector<double>& ref,
|
||||||
|
const ::Opm::FlowDiagnostics::Solution& fd,
|
||||||
|
AggregateErrors& E)
|
||||||
|
{
|
||||||
|
const auto tof = fd.timeOfFlight();
|
||||||
|
const auto diff = VectorDifference(tof, ref); // tof - ref
|
||||||
|
|
||||||
|
using Vector1 = std::decay<decltype(diff)>::type;
|
||||||
|
using Vector2 = std::decay<decltype(ref)>::type;
|
||||||
|
using Ratio = VectorRatio<Vector1, Vector2>;
|
||||||
|
|
||||||
|
const auto rat = Ratio(diff, ref); // (tof - ref) / ref
|
||||||
|
|
||||||
|
auto abs = ErrorMeasurement{};
|
||||||
|
{
|
||||||
|
abs.volume = volumeMetric(pv, diff);
|
||||||
|
abs.inf = pointMetric ( diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rel = ErrorMeasurement{};
|
||||||
|
{
|
||||||
|
rel.volume = volumeMetric(pv, rat);
|
||||||
|
rel.inf = pointMetric ( rat);
|
||||||
|
}
|
||||||
|
|
||||||
|
E.absolute.push_back(std::move(abs));
|
||||||
|
E.relative.push_back(std::move(rel));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<AggregateErrors, 2>
|
||||||
|
sampleDifferences(example::Setup&& setup,
|
||||||
|
const std::vector<int>& steps)
|
||||||
|
{
|
||||||
|
const auto start =
|
||||||
|
std::vector<Opm::FlowDiagnostics::CellSet>{};
|
||||||
|
|
||||||
|
const auto nDigits = numDigits(steps);
|
||||||
|
|
||||||
|
const auto pv = PoreVolume{ setup.graph.poreVolume() };
|
||||||
|
|
||||||
|
auto E = std::array<AggregateErrors, 2>{};
|
||||||
|
|
||||||
|
for (const auto& step : steps) {
|
||||||
|
if (step == 0) {
|
||||||
|
// Ignore initial condition
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! setup.selectReportStep(step)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ref = loadReference(setup.param, step, nDigits);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto fwd = setup.toolbox
|
||||||
|
.computeInjectionDiagnostics(start);
|
||||||
|
|
||||||
|
computeErrors(pv, ref.forward, fwd.fd, E[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto rev = setup.toolbox
|
||||||
|
.computeProductionDiagnostics(start);
|
||||||
|
|
||||||
|
computeErrors(pv, ref.reverse, rev.fd, E[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool errorAcceptable(const std::vector<ErrorMeasurement>& E,
|
||||||
|
const double tol)
|
||||||
|
{
|
||||||
|
return std::accumulate(std::begin(E), std::end(E), true,
|
||||||
|
[tol](const bool ok, const ErrorMeasurement& e)
|
||||||
|
{
|
||||||
|
// Fine if at least one of .volume or .inf <= tol.
|
||||||
|
return ok && ! ((e.volume > tol) && (e.inf > tol));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool everythingFine(const AggregateErrors& E,
|
||||||
|
const ErrorTolerance& tol)
|
||||||
|
{
|
||||||
|
return errorAcceptable(E.absolute, tol.absolute)
|
||||||
|
&& errorAcceptable(E.relative, tol.relative);
|
||||||
|
}
|
||||||
|
} // namespace Anonymous
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
try {
|
||||||
|
auto setup = example::Setup(argc, argv);
|
||||||
|
|
||||||
|
const auto tol = testTolerances(setup.param);
|
||||||
|
const auto steps = availableReportSteps(setup.file_paths);
|
||||||
|
|
||||||
|
const auto E = sampleDifferences(std::move(setup), steps);
|
||||||
|
const auto ok =
|
||||||
|
everythingFine(E[0], tol) && everythingFine(E[1], tol);
|
||||||
|
|
||||||
|
std::cout << (ok ? "OK" : "FAIL") << '\n';
|
||||||
|
|
||||||
|
if (! ok) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "Caught Exception: " << e.what() << '\n';
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,587 @@
|
|||||||
|
/*
|
||||||
|
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 <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <numeric>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
#include <ert/ecl/ecl_file.h>
|
||||||
|
#include <ert/ecl/ecl_file_kw.h>
|
||||||
|
#include <ert/ecl/ecl_file_view.h>
|
||||||
|
#include <ert/ecl/ecl_kw.h>
|
||||||
|
#include <ert/ecl/ecl_kw_magic.h>
|
||||||
|
#include <ert/util/ert_unique_ptr.hpp>
|
||||||
|
|
||||||
|
namespace StringUtils {
|
||||||
|
namespace {
|
||||||
|
std::string trim(const std::string& s)
|
||||||
|
{
|
||||||
|
const auto anchor_ws =
|
||||||
|
boost::regex(R"~~(^\s+([^\s]+)\s+$)~~");
|
||||||
|
|
||||||
|
auto m = boost::smatch{};
|
||||||
|
|
||||||
|
if (boost::regex_match(s, m, anchor_ws)) {
|
||||||
|
return m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string& s)
|
||||||
|
{
|
||||||
|
if (s.empty()) {
|
||||||
|
// Single element vector whose only element is the empty
|
||||||
|
// string.
|
||||||
|
return { "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto sep = boost::regex(R"~~([\s,;.|]+)~~");
|
||||||
|
|
||||||
|
using TI = boost::sregex_token_iterator;
|
||||||
|
|
||||||
|
// vector<string>(begin, end)
|
||||||
|
//
|
||||||
|
// Range is every substring (i.e., token) in input string 's'
|
||||||
|
// that does NOT match 'sep'.
|
||||||
|
return{ TI(s.begin(), s.end(), sep, -1), TI{} };
|
||||||
|
}
|
||||||
|
} // namespace Anonymous
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct StringTo;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<int>
|
||||||
|
{
|
||||||
|
static int value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<double>
|
||||||
|
{
|
||||||
|
static double value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct StringTo<std::string>
|
||||||
|
{
|
||||||
|
static std::string value(const std::string& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
int StringTo<int>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return std::stoi(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
double StringTo<double>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return std::stod(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StringTo<std::string>::value(const std::string& s)
|
||||||
|
{
|
||||||
|
return trim(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VectorValue {
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s, std::true_type)
|
||||||
|
{
|
||||||
|
return split(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s, std::false_type)
|
||||||
|
{
|
||||||
|
const auto tokens = split(s);
|
||||||
|
|
||||||
|
auto ret = std::vector<T>{};
|
||||||
|
ret.reserve(tokens.size());
|
||||||
|
for (const auto& token : tokens) {
|
||||||
|
ret.push_back(StringTo<T>::value(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> get(const std::string& s)
|
||||||
|
{
|
||||||
|
return get<T>(s, typename std::is_same<T, std::string>::type());
|
||||||
|
}
|
||||||
|
} // namespace VectorValue
|
||||||
|
} // namespace StringUtils
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct Reference
|
||||||
|
{
|
||||||
|
std::vector<double> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Calculated
|
||||||
|
{
|
||||||
|
std::vector<double> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VectorUnits
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using USys = ::Opm::ECLUnits::UnitSystem;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using UnitConvention = ::Opm::ECLGraph::UnitConvention;
|
||||||
|
|
||||||
|
VectorUnits()
|
||||||
|
: units_({ { "pressure", &USys::pressure } })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitConvention getUnit(const std::string& vector) const
|
||||||
|
{
|
||||||
|
auto p = units_.find(vector);
|
||||||
|
|
||||||
|
if (p == units_.end()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Unsupported Vector Quantity '" << vector << '\'';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return p->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, UnitConvention> units_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VectorDifference
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Vector = std::vector<double>;
|
||||||
|
using size_type = Vector::size_type;
|
||||||
|
|
||||||
|
VectorDifference(const Vector& x, const Vector& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y_.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return this->size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] - y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector& x_;
|
||||||
|
const Vector& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Vector1, class Vector2>
|
||||||
|
class VectorRatio
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = typename std::decay<
|
||||||
|
decltype(std::declval<Vector1>()[0])
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
VectorRatio(const Vector1& x, const Vector2& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return x_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] / y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector1& x_;
|
||||||
|
const Vector2& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorMeasurement
|
||||||
|
{
|
||||||
|
double volume;
|
||||||
|
double inf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorTolerance
|
||||||
|
{
|
||||||
|
double absolute;
|
||||||
|
double relative;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AggregateErrors
|
||||||
|
{
|
||||||
|
std::vector<ErrorMeasurement> absolute;
|
||||||
|
std::vector<ErrorMeasurement> relative;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReferenceSolution
|
||||||
|
{
|
||||||
|
std::vector<double> raw;
|
||||||
|
std::vector<double> SI;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class FieldVariable>
|
||||||
|
double volumeMetric(const FieldVariable& x)
|
||||||
|
{
|
||||||
|
auto result = 0.0;
|
||||||
|
|
||||||
|
for (decltype(x.size())
|
||||||
|
i = 0, n = x.size(); i < n; ++i)
|
||||||
|
{
|
||||||
|
const auto m = std::abs(x[i]);
|
||||||
|
result += m * m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::sqrt(result / x.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class FieldVariable>
|
||||||
|
double pointMetric(const FieldVariable& x)
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<typename std::decay<decltype(std::abs(x[0]))>::type, double>::value,
|
||||||
|
"Field Variable Value Type Must be 'double'");
|
||||||
|
|
||||||
|
if (x.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto max = 0*x[0] - 1;
|
||||||
|
|
||||||
|
for (decltype(x.size())
|
||||||
|
i = 0, n = x.size(); i < n; ++i)
|
||||||
|
{
|
||||||
|
const auto t = std::abs(x[i]);
|
||||||
|
|
||||||
|
if (t > max) {
|
||||||
|
max = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::parameter::ParameterGroup& param)
|
||||||
|
{
|
||||||
|
const auto atol = param.getDefault("atol", 1.0e-8);
|
||||||
|
const auto rtol = param.getDefault("rtol", 5.0e-12);
|
||||||
|
|
||||||
|
return ErrorTolerance{ atol, rtol };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
testQuantities(const ::Opm::parameter::ParameterGroup& param)
|
||||||
|
{
|
||||||
|
return StringUtils::VectorValue::
|
||||||
|
get<std::string>(param.get<std::string>("quant"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceSolution
|
||||||
|
loadReference(const ::Opm::parameter::ParameterGroup& param,
|
||||||
|
const std::string& quant,
|
||||||
|
const int step,
|
||||||
|
const int nDigits)
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
using VRef = std::reference_wrapper<std::vector<double>>;
|
||||||
|
|
||||||
|
auto x = ReferenceSolution{};
|
||||||
|
auto ref = std::array<VRef,2>{{ std::ref(x.raw) ,
|
||||||
|
std::ref(x.SI ) }};
|
||||||
|
|
||||||
|
auto i = 0;
|
||||||
|
|
||||||
|
for (const auto* q : { "raw", "SI" }) {
|
||||||
|
auto fname = fs::path(param.get<std::string>("ref-dir"))
|
||||||
|
/ boost::algorithm::to_lower_copy(quant);
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << q << '-'
|
||||||
|
<< std::setw(nDigits) << std::setfill('0')
|
||||||
|
<< step << ".txt";
|
||||||
|
|
||||||
|
fname /= os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::ifstream input(fname);
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
ref[i].get().assign(std::istream_iterator<double>(input),
|
||||||
|
std::istream_iterator<double>());
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.raw.size() != x.SI.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Unable to Read Consistent Reference Data From '"
|
||||||
|
<< param.get<std::string>("ref-dir") << "' In Step "
|
||||||
|
<< step;
|
||||||
|
|
||||||
|
throw std::out_of_range(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void computeErrors(const Reference& ref,
|
||||||
|
const Calculated& calc,
|
||||||
|
AggregateErrors& E)
|
||||||
|
{
|
||||||
|
const auto diff =
|
||||||
|
VectorDifference(calc.data, ref.data); // calc - ref
|
||||||
|
|
||||||
|
using Vector1 = std::decay<decltype(diff)>::type;
|
||||||
|
using Vector2 = std::decay<decltype(ref.data)>::type;
|
||||||
|
using Ratio = VectorRatio<Vector1, Vector2>;
|
||||||
|
|
||||||
|
const auto rat = Ratio(diff, ref.data); // (tof - ref) / ref
|
||||||
|
|
||||||
|
auto abs = ErrorMeasurement{};
|
||||||
|
{
|
||||||
|
abs.volume = volumeMetric(diff);
|
||||||
|
abs.inf = pointMetric (diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rel = ErrorMeasurement{};
|
||||||
|
{
|
||||||
|
rel.volume = volumeMetric(rat);
|
||||||
|
rel.inf = pointMetric (rat);
|
||||||
|
}
|
||||||
|
|
||||||
|
E.absolute.push_back(std::move(abs));
|
||||||
|
E.relative.push_back(std::move(rel));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<AggregateErrors, 2>
|
||||||
|
sampleDifferences(const ::Opm::ECLGraph& graph,
|
||||||
|
const ::Opm::parameter::ParameterGroup& param,
|
||||||
|
const std::string& quant,
|
||||||
|
const std::vector<int>& steps)
|
||||||
|
{
|
||||||
|
const auto ECLquant = boost::algorithm::to_upper_copy(quant);
|
||||||
|
|
||||||
|
auto unit = VectorUnits()
|
||||||
|
.getUnit(boost::algorithm::to_lower_copy(quant));
|
||||||
|
|
||||||
|
const auto start =
|
||||||
|
std::vector<Opm::FlowDiagnostics::CellSet>{};
|
||||||
|
|
||||||
|
const auto nDigits = numDigits(steps);
|
||||||
|
|
||||||
|
auto E = std::array<AggregateErrors, 2>{};
|
||||||
|
|
||||||
|
for (const auto& step : steps) {
|
||||||
|
if (! graph.selectReportStep(step)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ref = loadReference(param, quant, step, nDigits);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto raw = Calculated {
|
||||||
|
graph.rawLinearisedCellData<double>(ECLquant)
|
||||||
|
};
|
||||||
|
|
||||||
|
computeErrors(Reference{ ref.raw }, raw, E[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto SI = Calculated {
|
||||||
|
graph.linearisedCellData(ECLquant, unit)
|
||||||
|
};
|
||||||
|
|
||||||
|
computeErrors(Reference{ ref.SI }, SI, E[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool errorAcceptable(const std::vector<ErrorMeasurement>& E,
|
||||||
|
const double tol)
|
||||||
|
{
|
||||||
|
return std::accumulate(std::begin(E), std::end(E), true,
|
||||||
|
[tol](const bool ok, const ErrorMeasurement& e)
|
||||||
|
{
|
||||||
|
// Fine if at least one of .volume or .inf <= tol.
|
||||||
|
return ok && ! ((e.volume > tol) && (e.inf > tol));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool everythingFine(const AggregateErrors& E,
|
||||||
|
const ErrorTolerance& tol)
|
||||||
|
{
|
||||||
|
return errorAcceptable(E.absolute, tol.absolute)
|
||||||
|
&& errorAcceptable(E.relative, tol.relative);
|
||||||
|
}
|
||||||
|
} // 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 steps = availableReportSteps(pth);
|
||||||
|
const auto graph = example::initGraph(pth);
|
||||||
|
|
||||||
|
auto all_ok = true;
|
||||||
|
for (const auto& quant : testQuantities(prm)) {
|
||||||
|
const auto E = sampleDifferences(graph, prm, quant, steps);
|
||||||
|
|
||||||
|
const auto ok =
|
||||||
|
everythingFine(E[0], tol) && everythingFine(E[1], tol);
|
||||||
|
|
||||||
|
std::cout << quant << ": " << (ok ? "OK" : "FAIL") << '\n';
|
||||||
|
|
||||||
|
all_ok = all_ok && ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! all_ok) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "Caught Exception: " << e.what() << '\n';
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
229
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/runTransTest.cpp
vendored
Normal file
229
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/runTransTest.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 <opm/utility/ECLGraph.hpp>
|
||||||
|
|
||||||
|
#include <examples/exampleSetup.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <iterator>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class VectorDifference
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Vector = std::vector<double>;
|
||||||
|
using size_type = Vector::size_type;
|
||||||
|
|
||||||
|
VectorDifference(const Vector& x, const Vector& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y_.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return this->size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] - y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector& x_;
|
||||||
|
const Vector& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Vector1, class Vector2>
|
||||||
|
class VectorRatio
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using size_type = typename std::decay<
|
||||||
|
decltype(std::declval<Vector1>()[0])
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
VectorRatio(const Vector1& x, const Vector2& y)
|
||||||
|
: x_(x), y_(y)
|
||||||
|
{
|
||||||
|
if (x_.size() != y.size()) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Incompatible Array Sizes: Expected 2x"
|
||||||
|
<< x_.size() << ", but got ("
|
||||||
|
<< x_.size() << ", " << y_.size() << ')';
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const
|
||||||
|
{
|
||||||
|
return x_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return x_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator[](const size_type i) const
|
||||||
|
{
|
||||||
|
return x_[i] / y_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Vector1& x_;
|
||||||
|
const Vector2& y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErrorTolerance
|
||||||
|
{
|
||||||
|
double absolute;
|
||||||
|
double relative;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class FieldVariable>
|
||||||
|
double pointMetric(const FieldVariable& x)
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<typename std::decay<decltype(std::abs(x[0]))>::type, double>::value,
|
||||||
|
"Field Variable Value Type Must be 'double'");
|
||||||
|
|
||||||
|
if (x.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto max = 0*x[0] - 1;
|
||||||
|
|
||||||
|
for (decltype(x.size())
|
||||||
|
i = 0, n = x.size(); i < n; ++i)
|
||||||
|
{
|
||||||
|
const auto t = std::abs(x[i]);
|
||||||
|
|
||||||
|
if (t > max) {
|
||||||
|
max = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorTolerance
|
||||||
|
testTolerances(const ::Opm::parameter::ParameterGroup& param)
|
||||||
|
{
|
||||||
|
const auto atol = param.getDefault("atol", 1.0e-8);
|
||||||
|
const auto rtol = param.getDefault("rtol", 5.0e-12);
|
||||||
|
|
||||||
|
return ErrorTolerance{ atol, rtol };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double>
|
||||||
|
loadReference(const ::Opm::parameter::ParameterGroup& param)
|
||||||
|
{
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
const auto fname =
|
||||||
|
fs::path(param.get<std::string>("ref-dir")) / "trans.txt";
|
||||||
|
|
||||||
|
fs::ifstream input(fname);
|
||||||
|
|
||||||
|
if (! input) {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Unable to Open Reference Trans-Data File "
|
||||||
|
<< fname.filename();
|
||||||
|
|
||||||
|
throw std::domain_error(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
std::istream_iterator<double>(input),
|
||||||
|
std::istream_iterator<double>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transfieldAcceptable(const ::Opm::parameter::ParameterGroup& param,
|
||||||
|
const std::vector<double>& trans)
|
||||||
|
{
|
||||||
|
const auto Tref = loadReference(param);
|
||||||
|
|
||||||
|
if (Tref.size() != trans.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto diff = VectorDifference{ trans, Tref };
|
||||||
|
|
||||||
|
using Vector1 = std::decay<decltype(diff)>::type;
|
||||||
|
using Vector2 = std::decay<decltype(Tref)>::type;
|
||||||
|
using Ratio = VectorRatio<Vector1, Vector2>;
|
||||||
|
|
||||||
|
const auto rat = Ratio(diff, Tref);
|
||||||
|
const auto tol = testTolerances(param);
|
||||||
|
|
||||||
|
return ! ((pointMetric(diff) > tol.absolute) ||
|
||||||
|
(pointMetric(rat) > tol.relative));
|
||||||
|
}
|
||||||
|
} // namespace Anonymous
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
try {
|
||||||
|
const auto prm = example::initParam(argc, argv);
|
||||||
|
const auto pth = example::FilePaths(prm);
|
||||||
|
const auto G = example::initGraph(pth);
|
||||||
|
const auto T = G.transmissibility();
|
||||||
|
const auto ok = transfieldAcceptable(prm, T);
|
||||||
|
|
||||||
|
std::cout << (ok ? "OK" : "FAIL") << '\n';
|
||||||
|
|
||||||
|
if (! ok) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "Caught Exception: " << e.what() << '\n';
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
235
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclunithandling.cpp
vendored
Normal file
235
ThirdParty/custom-opm-flowdiag-app/opm-flowdiagnostics-applications/tests/test_eclunithandling.cpp
vendored
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
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_ASSEMBLED_CONNECTIONS
|
||||||
|
|
||||||
|
#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/ECLUnitHandling.hpp>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE (Basic_Conversion)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE (Constructor)
|
||||||
|
{
|
||||||
|
auto M = ::Opm::ECLUnits::createUnitSystem(1); // METRIC
|
||||||
|
auto F = ::Opm::ECLUnits::createUnitSystem(2); // FIELD
|
||||||
|
auto L = ::Opm::ECLUnits::createUnitSystem(3); // LAB
|
||||||
|
auto P = ::Opm::ECLUnits::createUnitSystem(4); // PVT-M
|
||||||
|
|
||||||
|
BOOST_CHECK_THROW(::Opm::ECLUnits::createUnitSystem( 5), std::runtime_error);
|
||||||
|
BOOST_CHECK_THROW(::Opm::ECLUnits::createUnitSystem(-1), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE (Metric)
|
||||||
|
{
|
||||||
|
auto M = ::Opm::ECLUnits::createUnitSystem(1);
|
||||||
|
|
||||||
|
// Pressure (bars)
|
||||||
|
{
|
||||||
|
const auto scale = M->pressure();
|
||||||
|
const auto expect = 100.0e3;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume Rate (rm^3 / day)
|
||||||
|
{
|
||||||
|
const auto scale = M->reservoirRate();
|
||||||
|
const auto expect = 1.157407407407407e-05;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume (rm^3)
|
||||||
|
{
|
||||||
|
const auto scale = M->reservoirVolume();
|
||||||
|
const auto expect = 1.0;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time (day)
|
||||||
|
{
|
||||||
|
const auto scale = M->time();
|
||||||
|
const auto expect = 86.400e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmissibility ((cP * m^3) / (day * barsa))
|
||||||
|
{
|
||||||
|
const auto scale = M->transmissibility();
|
||||||
|
const auto expect = 1.157407407407407e-13;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE (Field)
|
||||||
|
{
|
||||||
|
auto F = ::Opm::ECLUnits::createUnitSystem(2);
|
||||||
|
|
||||||
|
// Pressure (psi)
|
||||||
|
{
|
||||||
|
const auto scale = F->pressure();
|
||||||
|
const auto expect = 6.894757293168360e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume Rate (rb / day)
|
||||||
|
{
|
||||||
|
const auto scale = F->reservoirRate();
|
||||||
|
const auto expect = 1.840130728333334e-06;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume (rb)
|
||||||
|
{
|
||||||
|
const auto scale = F->reservoirVolume();
|
||||||
|
const auto expect = 1.589872949280001e-01;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time (day)
|
||||||
|
{
|
||||||
|
const auto scale = F->time();
|
||||||
|
const auto expect = 86.400e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmissibility ((cP * rb) / (day * psia))
|
||||||
|
{
|
||||||
|
const auto scale = F->transmissibility();
|
||||||
|
const auto expect = 2.668883979653090e-13;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE (Lab)
|
||||||
|
{
|
||||||
|
auto L = ::Opm::ECLUnits::createUnitSystem(3);
|
||||||
|
|
||||||
|
// Pressure (atm)
|
||||||
|
{
|
||||||
|
const auto scale = L->pressure();
|
||||||
|
const auto expect = 101.325e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume Rate (r(cm)^3 / h)
|
||||||
|
{
|
||||||
|
const auto scale = L->reservoirRate();
|
||||||
|
const auto expect = 2.777777777777778e-10;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume (r(cm)^3)
|
||||||
|
{
|
||||||
|
const auto scale = L->reservoirVolume();
|
||||||
|
const auto expect = 1.0e-06;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time (hour)
|
||||||
|
{
|
||||||
|
const auto scale = L->time();
|
||||||
|
const auto expect = 3600.0;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmissibility ((cP * (cm)^3) / (h * atm))
|
||||||
|
{
|
||||||
|
const auto scale = L->transmissibility();
|
||||||
|
const auto expect = 2.741453518655592e-18;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE (PVT_M)
|
||||||
|
{
|
||||||
|
auto P = ::Opm::ECLUnits::createUnitSystem(4);
|
||||||
|
|
||||||
|
// Pressure (atm)
|
||||||
|
{
|
||||||
|
const auto scale = P->pressure();
|
||||||
|
const auto expect = 101.325e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume Rate (rm^3 / day)
|
||||||
|
{
|
||||||
|
const auto scale = P->reservoirRate();
|
||||||
|
const auto expect = 1.157407407407407e-05;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservoir Volume (rm^3)
|
||||||
|
{
|
||||||
|
const auto scale = P->reservoirVolume();
|
||||||
|
const auto expect = 1.0;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time (day)
|
||||||
|
{
|
||||||
|
const auto scale = P->time();
|
||||||
|
const auto expect = 86.400e+03;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmissibility ((cP * rm^3 / (day * atm))
|
||||||
|
{
|
||||||
|
const auto scale = P->transmissibility();
|
||||||
|
const auto expect = 1.142272299439830e-13;
|
||||||
|
|
||||||
|
BOOST_CHECK_CLOSE(scale, expect, 1.0e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END ()
|
||||||
@@ -13,47 +13,60 @@
|
|||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||||
Copyright 2009, 2010, 2011, 2012 Statoil ASA.
|
Copyright 2009, 2010, 2011, 2012 Statoil ASA.
|
||||||
This file is part of the Open Porous Media project (OPM).
|
|
||||||
OPM is free software: you can redistribute it and/or modify
|
This file is part of the Open Porous Media project (OPM).
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
OPM is free software: you can redistribute it and/or modify
|
||||||
(at your option) any later version.
|
it under the terms of the GNU General Public License as published by
|
||||||
OPM is distributed in the hope that it will be useful,
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
(at your option) any later version.
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
OPM is distributed in the hope that it will be useful,
|
||||||
You should have received a copy of the GNU General Public License
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
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_UNITS_HEADER
|
#ifndef OPM_UNITS_HEADER
|
||||||
#define OPM_UNITS_HEADER
|
#define OPM_UNITS_HEADER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \file
|
The unit sets emplyed in ECLIPSE, in particular the FIELD units,
|
||||||
* Constants and routines to assist in handling units of measurement. These are
|
are quite inconsistent. Ideally one should choose units for a set
|
||||||
* geared towards handling common units in reservoir descriptions.
|
of base quantities like Mass,Time and Length and then derive the
|
||||||
*/
|
units for e.g. pressure and flowrate in a consisten manner. However
|
||||||
|
that is not the case; for instance in the metric system we have:
|
||||||
|
|
||||||
namespace Opm
|
[Length] = meters
|
||||||
{
|
[time] = days
|
||||||
namespace prefix
|
[mass] = kg
|
||||||
|
|
||||||
|
This should give:
|
||||||
|
|
||||||
|
[Pressure] = [mass] / ([length] * [time]^2) = kg / (m * days * days)
|
||||||
|
|
||||||
|
Instead pressure is given in Bars. When it comes to FIELD units the
|
||||||
|
number of such examples is long.
|
||||||
|
*/
|
||||||
|
namespace Opm {
|
||||||
|
namespace prefix
|
||||||
/// Conversion prefix for units.
|
/// Conversion prefix for units.
|
||||||
{
|
{
|
||||||
const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */
|
constexpr const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */
|
||||||
const double milli = 1.0e-3; /**< Unit prefix [m] */
|
constexpr const double milli = 1.0e-3; /**< Unit prefix [m] */
|
||||||
const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */
|
constexpr const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */
|
||||||
const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */
|
constexpr const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */
|
||||||
const double kilo = 1.0e3; /**< Unit prefix [k] */
|
constexpr const double kilo = 1.0e3; /**< Unit prefix [k] */
|
||||||
const double mega = 1.0e6; /**< Unit prefix [M] */
|
constexpr const double mega = 1.0e6; /**< Unit prefix [M] */
|
||||||
const double giga = 1.0e9; /**< Unit prefix [G] */
|
constexpr const double giga = 1.0e9; /**< Unit prefix [G] */
|
||||||
} // namespace prefix
|
} // namespace prefix
|
||||||
|
|
||||||
namespace unit
|
namespace unit
|
||||||
/// Definition of various units.
|
/// Definition of various units.
|
||||||
/// All the units are defined in terms of international standard
|
/// All the units are defined in terms of international standard
|
||||||
/// units (SI). Example of use: We define a variable \c k which
|
/// units (SI). Example of use: We define a variable \c k which
|
||||||
@@ -68,162 +81,246 @@ namespace Opm
|
|||||||
/// using namespace Opm::prefix
|
/// using namespace Opm::prefix
|
||||||
/// double k = 1.0*milli*darcy;
|
/// double k = 1.0*milli*darcy;
|
||||||
/// \endcode
|
/// \endcode
|
||||||
{
|
{
|
||||||
///\name Common powers
|
///\name Common powers
|
||||||
/// @{
|
/// @{
|
||||||
inline double square(double v) { return v * v; }
|
constexpr double square(double v) { return v * v; }
|
||||||
inline double cubic (double v) { return v * v * v; }
|
constexpr double cubic (double v) { return v * v * v; }
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
// Basic (fundamental) units and conversions
|
// Basic (fundamental) units and conversions
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
/// \name Length
|
/// \name Length
|
||||||
/// @{
|
/// @{
|
||||||
const double meter = 1;
|
constexpr const double meter = 1;
|
||||||
const double inch = 2.54 * prefix::centi*meter;
|
constexpr const double inch = 2.54 * prefix::centi*meter;
|
||||||
const double feet = 12 * inch;
|
constexpr const double feet = 12 * inch;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Time
|
/// \name Time
|
||||||
/// @{
|
/// @{
|
||||||
const double second = 1;
|
constexpr const double second = 1;
|
||||||
const double minute = 60 * second;
|
constexpr const double minute = 60 * second;
|
||||||
const double hour = 60 * minute;
|
constexpr const double hour = 60 * minute;
|
||||||
const double day = 24 * hour;
|
constexpr const double day = 24 * hour;
|
||||||
const double year = 365 * day;
|
constexpr const double year = 365 * day;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Volume
|
/// \name Volume
|
||||||
/// @{
|
/// @{
|
||||||
const double gallon = 231 * cubic(inch);
|
constexpr const double gallon = 231 * cubic(inch);
|
||||||
const double stb = 42 * gallon;
|
constexpr const double stb = 42 * gallon;
|
||||||
const double liter = 1 * cubic(prefix::deci*meter);
|
constexpr const double liter = 1 * cubic(prefix::deci*meter);
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Mass
|
/// \name Mass
|
||||||
/// @{
|
/// @{
|
||||||
const double kilogram = 1;
|
constexpr const double kilogram = 1;
|
||||||
// http://en.wikipedia.org/wiki/Pound_(mass)#Avoirdupois_pound
|
constexpr const double gram = 1.0e-3 * kilogram;
|
||||||
const double pound = 0.45359237 * kilogram;
|
// http://en.wikipedia.org/wiki/Pound_(mass)#Avoirdupois_pound
|
||||||
/// @}
|
constexpr const double pound = 0.45359237 * kilogram;
|
||||||
|
/// @}
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
// Standardised constants
|
// Standardised constants
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
/// \name Standardised constant
|
/// \name Standardised constant
|
||||||
/// @{
|
/// @{
|
||||||
const double gravity = 9.80665 * meter/square(second);
|
constexpr const double gravity = 9.80665 * meter/square(second);
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
// Derived units and conversions
|
// Derived units and conversions
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
/// \name Force
|
/// \name Force
|
||||||
/// @{
|
/// @{
|
||||||
const double Newton = kilogram*meter / square(second); // == 1
|
constexpr const double Newton = kilogram*meter / square(second); // == 1
|
||||||
const double lbf = pound * gravity; // Pound-force
|
constexpr const double dyne = 1e-5*Newton;
|
||||||
|
constexpr const double lbf = pound * gravity; // Pound-force
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Pressure
|
/// \name Pressure
|
||||||
/// @{
|
/// @{
|
||||||
const double Pascal = Newton / square(meter); // == 1
|
constexpr const double Pascal = Newton / square(meter); // == 1
|
||||||
const double barsa = 100000 * Pascal;
|
constexpr const double barsa = 100000 * Pascal;
|
||||||
const double atm = 101325 * Pascal;
|
constexpr const double atm = 101325 * Pascal;
|
||||||
const double psia = lbf / square(inch);
|
constexpr const double psia = lbf / square(inch);
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// \name Temperature. This one is more complicated
|
||||||
|
/// because the unit systems used by Eclipse (i.e. degrees
|
||||||
|
/// Celsius and degrees Fahrenheit require to add or
|
||||||
|
/// subtract an offset for the conversion between from/to
|
||||||
|
/// Kelvin
|
||||||
|
/// @{
|
||||||
|
constexpr const double degCelsius = 1.0; // scaling factor <20>C -> K
|
||||||
|
constexpr const double degCelsiusOffset = 273.15; // offset for the <20>C -> K conversion
|
||||||
|
|
||||||
|
constexpr const double degFahrenheit = 5.0/9; // scaling factor <20>F -> K
|
||||||
|
constexpr const double degFahrenheitOffset = 255.37; // offset for the <20>C -> K conversion
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Viscosity
|
/// \name Viscosity
|
||||||
/// @{
|
/// @{
|
||||||
const double Pas = Pascal * second; // == 1
|
constexpr const double Pas = Pascal * second; // == 1
|
||||||
const double Poise = prefix::deci*Pas;
|
constexpr const double Poise = prefix::deci*Pas;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
namespace perm_details {
|
namespace perm_details {
|
||||||
const double p_grad = atm / (prefix::centi*meter);
|
constexpr const double p_grad = atm / (prefix::centi*meter);
|
||||||
const double area = square(prefix::centi*meter);
|
constexpr const double area = square(prefix::centi*meter);
|
||||||
const double flux = cubic (prefix::centi*meter) / second;
|
constexpr const double flux = cubic (prefix::centi*meter) / second;
|
||||||
const double velocity = flux / area;
|
constexpr const double velocity = flux / area;
|
||||||
const double visc = prefix::centi*Poise;
|
constexpr const double visc = prefix::centi*Poise;
|
||||||
const double darcy = (velocity * visc) / p_grad;
|
constexpr const double darcy = (velocity * visc) / p_grad;
|
||||||
// == 1e-7 [m^2] / 101325
|
// == 1e-7 [m^2] / 101325
|
||||||
// == 9.869232667160130e-13 [m^2]
|
// == 9.869232667160130e-13 [m^2]
|
||||||
}
|
}
|
||||||
/// \name Permeability
|
/// \name Permeability
|
||||||
/// @{
|
/// @{
|
||||||
///
|
///
|
||||||
/// A porous medium with a permeability of 1 darcy permits a flow (flux)
|
/// A porous medium with a permeability of 1 darcy permits a flow (flux)
|
||||||
/// of \f$1\,\mathit{cm}^3/s\f$ of a fluid with viscosity
|
/// of \f$1\,\mathit{cm}^3/s\f$ of a fluid with viscosity
|
||||||
/// \f$1\,\mathit{cP}\f$ (\f$1\,mPa\cdot s\f$) under a pressure gradient
|
/// \f$1\,\mathit{cP}\f$ (\f$1\,mPa\cdot s\f$) under a pressure gradient
|
||||||
/// of \f$1\,\mathit{atm}/\mathit{cm}\f$ acting across an area of
|
/// of \f$1\,\mathit{atm}/\mathit{cm}\f$ acting across an area of
|
||||||
/// \f$1\,\mathit{cm}^2\f$.
|
/// \f$1\,\mathit{cm}^2\f$.
|
||||||
///
|
///
|
||||||
const double darcy = perm_details::darcy;
|
constexpr const double darcy = perm_details::darcy;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit conversion routines.
|
* Unit conversion routines.
|
||||||
*/
|
*/
|
||||||
namespace convert {
|
namespace convert {
|
||||||
/**
|
/**
|
||||||
* Convert from external units of measurements to equivalent
|
* Convert from external units of measurements to equivalent
|
||||||
* internal units of measurements. Note: The internal units of
|
* internal units of measurements. Note: The internal units of
|
||||||
* measurements are *ALWAYS*, and exclusively, SI.
|
* measurements are *ALWAYS*, and exclusively, SI.
|
||||||
*
|
*
|
||||||
* Example: Convert a double @c kx, containing a permeability value
|
* Example: Convert a double @c kx, containing a permeability value
|
||||||
* in units of milli-darcy (mD) to the equivalent value in SI units
|
* in units of milli-darcy (mD) to the equivalent value in SI units
|
||||||
* (i.e., \f$m^2\f$).
|
* (i.e., \f$m^2\f$).
|
||||||
* \code
|
* \code
|
||||||
* using namespace Opm::unit;
|
* using namespace Opm::unit;
|
||||||
* using namespace Opm::prefix;
|
* using namespace Opm::prefix;
|
||||||
* convert::from(kx, milli*darcy);
|
* convert::from(kx, milli*darcy);
|
||||||
* \endcode
|
* \endcode
|
||||||
*
|
*
|
||||||
* @param[in] q Physical quantity.
|
* @param[in] q Physical quantity.
|
||||||
* @param[in] unit Physical unit of measurement.
|
* @param[in] unit Physical unit of measurement.
|
||||||
* @return Value of @c q in equivalent SI units of measurements.
|
* @return Value of @c q in equivalent SI units of measurements.
|
||||||
*/
|
*/
|
||||||
inline double from(const double q, const double unit)
|
constexpr double from(const double q, const double unit)
|
||||||
{
|
{
|
||||||
return q * unit;
|
return q * unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert from internal units of measurements to equivalent
|
* Convert from internal units of measurements to equivalent
|
||||||
* external units of measurements. Note: The internal units of
|
* external units of measurements. Note: The internal units of
|
||||||
* measurements are *ALWAYS*, and exclusively, SI.
|
* measurements are *ALWAYS*, and exclusively, SI.
|
||||||
*
|
*
|
||||||
* Example: Convert a <CODE>std::vector<double> p</CODE>, containing
|
* Example: Convert a <CODE>std::vector<double> p</CODE>, containing
|
||||||
* pressure values in the SI unit Pascal (i.e., unit::Pascal) to the
|
* pressure values in the SI unit Pascal (i.e., unit::Pascal) to the
|
||||||
* equivalent values in Psi (unit::psia).
|
* equivalent values in Psi (unit::psia).
|
||||||
* \code
|
* \code
|
||||||
* using namespace Opm::unit;
|
* using namespace Opm::unit;
|
||||||
* std::transform(p.begin(), p.end(), p.begin(),
|
* std::transform(p.begin(), p.end(), p.begin(),
|
||||||
* boost::bind(convert::to, _1, psia));
|
* boost::bind(convert::to, _1, psia));
|
||||||
* \endcode
|
* \endcode
|
||||||
*
|
*
|
||||||
* @param[in] q Physical quantity, measured in SI units.
|
* @param[in] q Physical quantity, measured in SI units.
|
||||||
* @param[in] unit Physical unit of measurement.
|
* @param[in] unit Physical unit of measurement.
|
||||||
* @return Value of @c q in unit <CODE>unit</CODE>.
|
* @return Value of @c q in unit <CODE>unit</CODE>.
|
||||||
*/
|
*/
|
||||||
inline double to(const double q, const double unit)
|
constexpr double to(const double q, const double unit)
|
||||||
{
|
{
|
||||||
return q / unit;
|
return q / unit;
|
||||||
}
|
}
|
||||||
} // namespace convert
|
} // namespace convert
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Metric {
|
||||||
|
using namespace prefix;
|
||||||
|
using namespace unit;
|
||||||
|
constexpr const double Pressure = barsa;
|
||||||
|
constexpr const double Temperature = degCelsius;
|
||||||
|
constexpr const double TemperatureOffset = degCelsiusOffset;
|
||||||
|
constexpr const double AbsoluteTemperature = degCelsius; // actually [K], but the these two are identical
|
||||||
|
constexpr const double Length = meter;
|
||||||
|
constexpr const double Time = day;
|
||||||
|
constexpr const double Mass = kilogram;
|
||||||
|
constexpr const double Permeability = milli*darcy;
|
||||||
|
constexpr const double Transmissibility = centi*Poise*cubic(meter)/(day*barsa);
|
||||||
|
constexpr const double LiquidSurfaceVolume = cubic(meter);
|
||||||
|
constexpr const double GasSurfaceVolume = cubic(meter);
|
||||||
|
constexpr const double ReservoirVolume = cubic(meter);
|
||||||
|
constexpr const double GasDissolutionFactor = GasSurfaceVolume/LiquidSurfaceVolume;
|
||||||
|
constexpr const double OilDissolutionFactor = LiquidSurfaceVolume/GasSurfaceVolume;
|
||||||
|
constexpr const double Density = kilogram/cubic(meter);
|
||||||
|
constexpr const double PolymerDensity = kilogram/cubic(meter);
|
||||||
|
constexpr const double Salinity = kilogram/cubic(meter);
|
||||||
|
constexpr const double Viscosity = centi*Poise;
|
||||||
|
constexpr const double Timestep = day;
|
||||||
|
constexpr const double SurfaceTension = dyne/(centi*meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef HAS_ATTRIBUTE_UNUSED
|
namespace Field {
|
||||||
namespace detail {
|
using namespace prefix;
|
||||||
// Some units are sometimes unused, and generate a (potentially) large number of warnings
|
using namespace unit;
|
||||||
// Adding them here silences these warnings, and should have no side-effects
|
constexpr const double Pressure = psia;
|
||||||
//JJS double __attribute__((unused)) unused_units = stb + liter + barsa + psia + darcy;
|
constexpr const double Temperature = degFahrenheit;
|
||||||
} // namespace detail
|
constexpr const double TemperatureOffset = degFahrenheitOffset;
|
||||||
#endif
|
constexpr const double AbsoluteTemperature = degFahrenheit; // actually [<5B>R], but the these two are identical
|
||||||
|
constexpr const double Length = feet;
|
||||||
|
constexpr const double Time = day;
|
||||||
|
constexpr const double Mass = pound;
|
||||||
|
constexpr const double Permeability = milli*darcy;
|
||||||
|
constexpr const double Transmissibility = centi*Poise*stb/(day*psia);
|
||||||
|
constexpr const double LiquidSurfaceVolume = stb;
|
||||||
|
constexpr const double GasSurfaceVolume = 1000*cubic(feet);
|
||||||
|
constexpr const double ReservoirVolume = stb;
|
||||||
|
constexpr const double GasDissolutionFactor = GasSurfaceVolume/LiquidSurfaceVolume;
|
||||||
|
constexpr const double OilDissolutionFactor = LiquidSurfaceVolume/GasSurfaceVolume;
|
||||||
|
constexpr const double Density = pound/cubic(feet);
|
||||||
|
constexpr const double PolymerDensity = pound/stb;
|
||||||
|
constexpr const double Salinity = pound/stb;
|
||||||
|
constexpr const double Viscosity = centi*Poise;
|
||||||
|
constexpr const double Timestep = day;
|
||||||
|
constexpr const double SurfaceTension = dyne/(centi*meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace Lab {
|
||||||
|
using namespace prefix;
|
||||||
|
using namespace unit;
|
||||||
|
constexpr const double Pressure = atm;
|
||||||
|
constexpr const double Temperature = degCelsius;
|
||||||
|
constexpr const double TemperatureOffset = degCelsiusOffset;
|
||||||
|
constexpr const double AbsoluteTemperature = degCelsius; // actually [K], but the these two are identical
|
||||||
|
constexpr const double Length = centi*meter;
|
||||||
|
constexpr const double Time = hour;
|
||||||
|
constexpr const double Mass = gram;
|
||||||
|
constexpr const double Permeability = milli*darcy;
|
||||||
|
constexpr const double Transmissibility = centi*Poise*cubic(centi*meter)/(hour*atm);
|
||||||
|
constexpr const double LiquidSurfaceVolume = cubic(centi*meter);
|
||||||
|
constexpr const double GasSurfaceVolume = cubic(centi*meter);
|
||||||
|
constexpr const double ReservoirVolume = cubic(centi*meter);
|
||||||
|
constexpr const double GasDissolutionFactor = GasSurfaceVolume/LiquidSurfaceVolume;
|
||||||
|
constexpr const double OilDissolutionFactor = LiquidSurfaceVolume/GasSurfaceVolume;
|
||||||
|
constexpr const double Density = gram/cubic(centi*meter);
|
||||||
|
constexpr const double PolymerDensity = gram/cubic(centi*meter);
|
||||||
|
constexpr const double Salinity = gram/cubic(centi*meter);
|
||||||
|
constexpr const double Viscosity = centi*Poise;
|
||||||
|
constexpr const double Timestep = hour;
|
||||||
|
constexpr const double SurfaceTension = dyne/(centi*meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace unit
|
|
||||||
} // namespace Opm
|
|
||||||
#endif // OPM_UNITS_HEADER
|
#endif // OPM_UNITS_HEADER
|
||||||
@@ -323,18 +323,14 @@ namespace FlowDiagnostics
|
|||||||
|
|
||||||
// Compute time-of-flight and tracer.
|
// Compute time-of-flight and tracer.
|
||||||
tracer_[cell] = is_start_[cell] ? 1.0 : upwind_tracer_contrib / total_influx;
|
tracer_[cell] = is_start_[cell] ? 1.0 : upwind_tracer_contrib / total_influx;
|
||||||
//tof_[cell] = (pv_[cell]*tracer_[cell] + upwind_tof_contrib) / (total_influx*tracer_[cell]);
|
|
||||||
|
|
||||||
if ( tracer_[cell] > 0.0 )
|
if (tracer_[cell] > 0.0) {
|
||||||
{
|
|
||||||
tof_[cell] = (pv_[cell]*tracer_[cell] + upwind_tof_contrib)
|
tof_[cell] = (pv_[cell]*tracer_[cell] + upwind_tof_contrib)
|
||||||
/ (total_influx * tracer_[cell]);
|
/ (total_influx * tracer_[cell]);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
tof_[cell] = max_tof_;
|
tof_[cell] = max_tof_;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user