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_PARSER_GITHUB_SHA "a3496df501a4369fd827fbf0ff893d03deff425f")
|
||||
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "80190c28ae0fd4a82cfe88c827559029982db83b")
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "01ecb8fc22cb70d883edaad398bffc49878859c3")
|
||||
set(OPM_FLOWDIAGNOSTICS_SHA "02503abf0043bb08062e358e460a0c8231b6225c")
|
||||
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "09057e5767e492fa44c5e9ae78e9e1fcc9694b6c")
|
||||
|
||||
# sha for Units.hpp 9a679071dd0066236154852c39a9e0b3c3ac4873
|
||||
|
||||
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)
|
||||
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
|
||||
# separate file (in cmake/Modules sub-directory) because it is shared
|
||||
# with the find module
|
||||
@@ -91,3 +95,7 @@ endmacro (install_hook)
|
||||
|
||||
# all setup common to the OPM library modules is done here
|
||||
include (OpmLibMain)
|
||||
|
||||
if (HAVE_OPM_DATA)
|
||||
include (${CMAKE_CURRENT_SOURCE_DIR}/AcceptanceTests.cmake)
|
||||
endif()
|
||||
|
||||
@@ -21,22 +21,30 @@
|
||||
# the library needs it.
|
||||
|
||||
list (APPEND MAIN_SOURCE_FILES
|
||||
opm/utility/ECLFluxCalc.cpp
|
||||
opm/utility/ECLGraph.cpp
|
||||
opm/utility/ECLResultData.cpp
|
||||
opm/utility/ECLUnitHandling.cpp
|
||||
opm/utility/ECLWellSolution.cpp
|
||||
)
|
||||
|
||||
list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_eclunithandling.cpp
|
||||
)
|
||||
|
||||
list (APPEND EXAMPLE_SOURCE_FILES
|
||||
examples/computeLocalSolutions.cpp
|
||||
examples/computeToFandTracers.cpp
|
||||
examples/computeTracers.cpp
|
||||
tests/runAcceptanceTest.cpp
|
||||
tests/runLinearisedCellDataTest.cpp
|
||||
tests/runTransTest.cpp
|
||||
)
|
||||
|
||||
list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/utility/ECLFluxCalc.hpp
|
||||
opm/utility/ECLGraph.hpp
|
||||
opm/utility/ECLResultData.hpp
|
||||
opm/utility/ECLUnitHandling.hpp
|
||||
opm/utility/ECLWellSolution.hpp
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <opm/flowdiagnostics/ConnectionValues.hpp>
|
||||
#include <opm/flowdiagnostics/Toolbox.hpp>
|
||||
|
||||
#include <opm/utility/ECLFluxCalc.hpp>
|
||||
#include <opm/utility/ECLGraph.hpp>
|
||||
#include <opm/utility/ECLWellSolution.hpp>
|
||||
|
||||
@@ -80,38 +81,31 @@ namespace example {
|
||||
}
|
||||
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
extractFluxField(const Opm::ECLGraph& G)
|
||||
extractFluxField(const Opm::ECLGraph& G, const bool compute_fluxes)
|
||||
{
|
||||
using ConnVals = Opm::FlowDiagnostics::ConnectionValues;
|
||||
|
||||
using NConn = ConnVals::NumConnections;
|
||||
using NPhas = ConnVals::NumPhases;
|
||||
|
||||
const auto nconn = NConn{G.numConnections()};
|
||||
const auto nphas = NPhas{3};
|
||||
|
||||
auto flux = ConnVals(nconn, nphas);
|
||||
auto flux = ConnVals(ConnVals::NumConnections{G.numConnections()},
|
||||
ConnVals::NumPhases{3});
|
||||
|
||||
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::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()) {
|
||||
assert (pflux.size() == nconn.total);
|
||||
|
||||
assert (pflux.size() == flux.numConnections());
|
||||
auto conn = ConnVals::ConnID{0};
|
||||
|
||||
for (const auto& v : pflux) {
|
||||
flux(conn, phas) = v;
|
||||
|
||||
conn.id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
phas.id += 1;
|
||||
}
|
||||
|
||||
@@ -138,72 +132,60 @@ namespace example {
|
||||
return inflow;
|
||||
}
|
||||
|
||||
namespace Hack {
|
||||
inline Opm::FlowDiagnostics::ConnectionValues
|
||||
convert_flux_to_SI(Opm::FlowDiagnostics::ConnectionValues&& fl)
|
||||
|
||||
|
||||
|
||||
struct FilePaths
|
||||
{
|
||||
using Co = Opm::FlowDiagnostics::ConnectionValues::ConnID;
|
||||
using Ph = Opm::FlowDiagnostics::ConnectionValues::PhaseID;
|
||||
|
||||
const auto nconn = fl.numConnections();
|
||||
const auto nphas = fl.numPhases();
|
||||
|
||||
for (auto phas = Ph{0}; phas.id < nphas; ++phas.id) {
|
||||
for (auto conn = Co{0}; conn.id < nconn; ++conn.id) {
|
||||
fl(conn, phas) /= 86400;
|
||||
}
|
||||
FilePaths(const Opm::parameter::ParameterGroup& param)
|
||||
{
|
||||
const string casename = param.getDefault<string>("case", "DEFAULT_CASE_NAME");
|
||||
grid = param.has("grid") ? param.get<string>("grid")
|
||||
: deriveFileName(casename, { ".EGRID", ".FEGRID", ".GRID", ".FGRID" });
|
||||
init = param.has("init") ? param.get<string>("init")
|
||||
: deriveFileName(casename, { ".INIT", ".FINIT" });
|
||||
restart = param.has("restart") ? param.get<string>("restart")
|
||||
: deriveFileName(casename, { ".UNRST", ".FUNRST" });
|
||||
}
|
||||
|
||||
return fl;
|
||||
}
|
||||
}
|
||||
using path = boost::filesystem::path;
|
||||
using string = std::string;
|
||||
|
||||
inline Opm::ECLGraph
|
||||
initGraph(int argc, char* argv[])
|
||||
path grid;
|
||||
path init;
|
||||
path restart;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
inline Opm::parameter::ParameterGroup
|
||||
initParam(int argc, char** argv)
|
||||
{
|
||||
// Obtain parameters from command line (possibly specifying a parameter file).
|
||||
const bool verify_commandline_syntax = true;
|
||||
const bool parameter_output = false;
|
||||
Opm::parameter::ParameterGroup param(argc, argv, verify_commandline_syntax, parameter_output);
|
||||
|
||||
// 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());
|
||||
return param;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
initToolbox(const Opm::ECLGraph& G, const std::vector<Opm::ECLWellSolution::WellData>& well_fluxes)
|
||||
initToolbox(const Opm::ECLGraph& G)
|
||||
{
|
||||
const auto connGraph = Opm::FlowDiagnostics::
|
||||
ConnectivityGraph{ static_cast<int>(G.numCells()),
|
||||
@@ -211,11 +193,7 @@ namespace example {
|
||||
|
||||
// Create the Toolbox.
|
||||
auto tool = Opm::FlowDiagnostics::Toolbox{ connGraph };
|
||||
|
||||
tool.assignPoreVolume(G.poreVolume());
|
||||
tool.assignConnectionFlux(Hack::convert_flux_to_SI(extractFluxField(G)));
|
||||
|
||||
tool.assignInflowFlux(extractWellFlows(G, well_fluxes));
|
||||
|
||||
return tool;
|
||||
}
|
||||
@@ -226,15 +204,42 @@ namespace example {
|
||||
struct Setup
|
||||
{
|
||||
Setup(int argc, char** argv)
|
||||
: graph(initGraph(argc, argv))
|
||||
, well_fluxes(initWellFluxes(graph))
|
||||
, toolbox(initToolbox(graph, well_fluxes))
|
||||
: param(initParam(argc, argv))
|
||||
, file_paths(param)
|
||||
, 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;
|
||||
std::vector<Opm::ECLWellSolution::WellData> well_fluxes;
|
||||
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/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -41,6 +44,7 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <ert/ecl/ecl_grid.h>
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
#include <ert/ecl/ecl_nnc_export.h>
|
||||
#include <ert/util/ert_unique_ptr.hpp>
|
||||
|
||||
@@ -93,6 +97,18 @@ namespace {
|
||||
std::array<std::size_t,3>
|
||||
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.
|
||||
///
|
||||
/// Specialised tool needed to determine the active cells.
|
||||
@@ -101,7 +117,11 @@ namespace {
|
||||
///
|
||||
/// \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>
|
||||
getPVolVector(const ecl_grid_type* G,
|
||||
const ::Opm::ECLResultData& init,
|
||||
@@ -137,6 +157,11 @@ namespace {
|
||||
const ::Opm::ECLResultData& init,
|
||||
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.
|
||||
std::size_t numCells() const;
|
||||
|
||||
@@ -154,9 +179,18 @@ namespace {
|
||||
///
|
||||
/// Corresponds to the \c PORV vector in the INIT file, possibly
|
||||
/// 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;
|
||||
|
||||
/// 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.
|
||||
int activeCell(const std::size_t globalCell) const;
|
||||
|
||||
@@ -188,6 +222,11 @@ namespace {
|
||||
cellData(const ::Opm::ECLResultData& src,
|
||||
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
|
||||
/// connections in grid.
|
||||
///
|
||||
@@ -200,7 +239,8 @@ namespace {
|
||||
/// all of the grid's Cartesian connections.
|
||||
std::vector<double>
|
||||
connectionData(const ::Opm::ECLResultData& src,
|
||||
const std::string& vector) const;
|
||||
const std::string& vector,
|
||||
const double unit) const;
|
||||
|
||||
private:
|
||||
/// Facility for deriving Cartesian neighbourship in a grid
|
||||
@@ -217,14 +257,18 @@ namespace {
|
||||
/// \param[in] G ERT Grid representation.
|
||||
///
|
||||
/// \param[in] pvol Vector of pore-volumes on all global
|
||||
/// cells of \p G. Typically obtained
|
||||
/// through function getPVolVector().
|
||||
/// cells of \p G. Typically obtained through function
|
||||
/// getPVolVector(). Numerical values assumed to be in
|
||||
/// SI units (rm^3).
|
||||
CartesianCells(const ecl_grid_type* G,
|
||||
const std::vector<double>& pvol);
|
||||
|
||||
/// Retrive global cell indices of all active cells in grid.
|
||||
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;
|
||||
|
||||
/// Map input vector to all global cells.
|
||||
@@ -240,6 +284,30 @@ namespace {
|
||||
std::vector<T>
|
||||
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
|
||||
/// inactive ones.
|
||||
///
|
||||
@@ -315,13 +383,8 @@ namespace {
|
||||
/// Whether or not a particular active cell is subdivided.
|
||||
std::vector<bool> is_divided_;
|
||||
|
||||
/// Identify those grid cells that are further subdivided by
|
||||
/// an LGR.
|
||||
///
|
||||
/// Writes to \c is_divided_.
|
||||
///
|
||||
/// \param
|
||||
void identifySubdividedCells(const ecl_grid_type* G);
|
||||
/// Retrieve number of active cells in grid.
|
||||
std::size_t numActiveCells() const;
|
||||
|
||||
/// Compute linear index of global cell from explicit
|
||||
/// (I,J,K) tuple.
|
||||
@@ -374,6 +437,11 @@ namespace {
|
||||
/// Source cells for each Cartesian connection.
|
||||
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
|
||||
/// defined on the grid's cells.
|
||||
///
|
||||
@@ -415,6 +483,7 @@ namespace {
|
||||
void connectionData(const ::Opm::ECLResultData& src,
|
||||
const CartesianCells::Direction d,
|
||||
const std::string& vector,
|
||||
const double unit,
|
||||
std::vector<double>& x) const;
|
||||
|
||||
/// Form complete name of directional result set vector from
|
||||
@@ -430,7 +499,7 @@ namespace {
|
||||
const CartesianCells::Direction d) const;
|
||||
|
||||
/// Derive neighbourship relations on active cells in particular
|
||||
/// Cartesian directions.
|
||||
/// Cartesian directions and capture transmissibilty field.
|
||||
///
|
||||
/// Writes to \c neigh_ and \c outCell_.
|
||||
///
|
||||
@@ -487,6 +556,13 @@ ECL::getPVolVector(const ecl_grid_type* G,
|
||||
|
||||
assert ((pvol.size() == nglob) &&
|
||||
"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;
|
||||
@@ -524,6 +600,18 @@ ECL::cartesianDimensions(const ecl_grid_type* 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>
|
||||
ECL::loadNNC(const ecl_grid_type* G,
|
||||
const ::Opm::ECLResultData& init)
|
||||
@@ -613,7 +701,7 @@ std::vector<std::size_t>
|
||||
ECL::CartesianGridData::CartesianCells::activeGlobal() const
|
||||
{
|
||||
auto active = std::vector<std::size_t>{};
|
||||
active.reserve(this->rsMap_.subset.size());
|
||||
active.reserve(this->numActiveCells());
|
||||
|
||||
for (const auto& id : this->rsMap_.subset) {
|
||||
active.push_back(id.glob);
|
||||
@@ -656,6 +744,48 @@ CartesianCells::scatterToGlobal(const std::vector<T>& x) const
|
||||
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
|
||||
ECL::CartesianGridData::CartesianCells::numGlobalCells() const
|
||||
{
|
||||
@@ -718,6 +848,12 @@ ECL::CartesianGridData::CartesianCells::isSubdivided(const int cellID) const
|
||||
return this->is_divided_[ix];
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ECL::CartesianGridData::CartesianCells::numActiveCells() const
|
||||
{
|
||||
return this->rsMap_.subset.size();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ECL::CartesianGridData::
|
||||
CartesianCells::globIdx(const IndexTuple& ijk) const
|
||||
@@ -772,6 +908,7 @@ CartesianGridData(const ecl_grid_type* G,
|
||||
|
||||
// Too large, but this is a quick estimate.
|
||||
this->neigh_.reserve(3 * (2 * this->numCells()));
|
||||
this->trans_.reserve(3 * (1 * this->numCells()));
|
||||
|
||||
for (const auto d : { CartesianCells::Direction::I ,
|
||||
CartesianCells::Direction::J ,
|
||||
@@ -781,6 +918,11 @@ CartesianGridData(const ecl_grid_type* G,
|
||||
}
|
||||
}
|
||||
|
||||
int ECL::CartesianGridData::gridID() const
|
||||
{
|
||||
return this->gridID_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ECL::CartesianGridData::numCells() const
|
||||
{
|
||||
@@ -805,6 +947,12 @@ ECL::CartesianGridData::activePoreVolume() const
|
||||
return this->cells_.activePoreVolume();
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
ECL::CartesianGridData::transmissibility() const
|
||||
{
|
||||
return this->trans_;
|
||||
}
|
||||
|
||||
int
|
||||
ECL::CartesianGridData::activeCell(const std::size_t globalCell) const
|
||||
{
|
||||
@@ -839,6 +987,22 @@ cellData(const ::Opm::ECLResultData& src,
|
||||
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
|
||||
ECL::CartesianGridData::
|
||||
haveCellData(const ::Opm::ECLResultData& src,
|
||||
@@ -871,7 +1035,8 @@ haveConnData(const ::Opm::ECLResultData& src,
|
||||
std::vector<double>
|
||||
ECL::CartesianGridData::
|
||||
connectionData(const ::Opm::ECLResultData& src,
|
||||
const std::string& vector) const
|
||||
const std::string& vector,
|
||||
const double unit) const
|
||||
{
|
||||
if (! this->haveConnData(src, vector)) {
|
||||
return {};
|
||||
@@ -883,7 +1048,7 @@ connectionData(const ::Opm::ECLResultData& src,
|
||||
CartesianCells::Direction::J ,
|
||||
CartesianCells::Direction::K })
|
||||
{
|
||||
this->connectionData(src, d, vector, x);
|
||||
this->connectionData(src, d, this->vectorName(vector, d), unit, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
@@ -894,10 +1059,10 @@ ECL::CartesianGridData::
|
||||
connectionData(const ::Opm::ECLResultData& src,
|
||||
const CartesianCells::Direction d,
|
||||
const std::string& vector,
|
||||
const double unit,
|
||||
std::vector<double>& x) const
|
||||
{
|
||||
const auto vname = this->vectorName(vector, d);
|
||||
const auto v = this->cellData(src, vname);
|
||||
const auto v = this->cellData(src, vector);
|
||||
|
||||
const auto& cells = this->outCell_.find(d);
|
||||
|
||||
@@ -905,7 +1070,7 @@ connectionData(const ::Opm::ECLResultData& src,
|
||||
"Direction must be I, J, or K");
|
||||
|
||||
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)
|
||||
: 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];
|
||||
ocell.reserve(gcells.size());
|
||||
|
||||
@@ -976,6 +1149,7 @@ deriveNeighbours(const std::vector<std::size_t>& gcells,
|
||||
this->neigh_.push_back(other);
|
||||
|
||||
ocell.push_back(globID);
|
||||
this->trans_.push_back(SI_trans(T[globID]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,28 +1206,57 @@ public:
|
||||
/// Retrieve number of active cells in graph.
|
||||
std::size_t numCells() const;
|
||||
|
||||
/// Retrive number of connections in graph.
|
||||
/// Retrieve number of connections in graph.
|
||||
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
|
||||
/// neighbours()[2*i + 0] \endcode and \code neighbours()[2*i + 1]
|
||||
/// \endcode.
|
||||
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
|
||||
/// restricted to those active cells for which the pore-volume is
|
||||
/// strictly positive.
|
||||
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;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// \param[in] phase Canonical phase for which to retrive flux.
|
||||
@@ -1068,6 +1271,14 @@ public:
|
||||
std::vector<double>
|
||||
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:
|
||||
/// Collection of non-Cartesian neighbourship relations attributed to a
|
||||
/// 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
|
||||
/// 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.
|
||||
void add(const std::vector<ECL::CartesianGridData>& grids,
|
||||
const std::vector<std::size_t>& offset,
|
||||
const double trans_unit,
|
||||
const ecl_nnc_type& nnc);
|
||||
|
||||
std::vector<Category> allCategories() const;
|
||||
@@ -1199,6 +1415,10 @@ private:
|
||||
/// Access all active non-neighbouring connections.
|
||||
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
|
||||
/// category (i.e., pertaining to a particular set of keywords).
|
||||
///
|
||||
@@ -1214,6 +1434,13 @@ private:
|
||||
/// in linear numbering of all model's active cells.
|
||||
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
|
||||
KeywordIndexMap keywords_;
|
||||
|
||||
@@ -1221,7 +1448,9 @@ private:
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// Identify connection category from connection's grids.
|
||||
@@ -1284,6 +1513,11 @@ private:
|
||||
/// range \code [0 .. numCells()) \endcode).
|
||||
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.
|
||||
std::unique_ptr<ECLResultData> src_;
|
||||
|
||||
@@ -1299,6 +1533,11 @@ private:
|
||||
void defineNNCs(const ecl_grid_type* G,
|
||||
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.
|
||||
///
|
||||
/// \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
|
||||
/// 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
|
||||
/// 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
|
||||
/// input, contains all values corresponding to all fully Cartesian
|
||||
/// connections across all active grids. On output additionally
|
||||
/// contains those values that correspond to the non-neighbouring
|
||||
/// connections (appended onto \p flux).
|
||||
template <class GetFluxUnit>
|
||||
void fluxNNC(const std::string& vector,
|
||||
GetFluxUnit&& fluxUnit,
|
||||
std::vector<double>& flux) const;
|
||||
};
|
||||
|
||||
@@ -1371,6 +1624,7 @@ void
|
||||
Opm::ECLGraph::Impl::
|
||||
NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
||||
const std::vector<std::size_t>& offset,
|
||||
const double trans_unit,
|
||||
const ecl_nnc_type& nnc)
|
||||
{
|
||||
if (! this->isViable(grid, nnc)) {
|
||||
@@ -1395,6 +1649,10 @@ NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
||||
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);
|
||||
|
||||
auto entry = NonNeighKeywordIndexSet::Map {
|
||||
@@ -1411,9 +1669,10 @@ NNC::add(const std::vector<ECL::CartesianGridData>& grid,
|
||||
std::size_t
|
||||
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>&
|
||||
@@ -1422,6 +1681,12 @@ Opm::ECLGraph::Impl::NNC::getNeighbours() const
|
||||
return this->neigh_;
|
||||
}
|
||||
|
||||
const std::vector<double>&
|
||||
Opm::ECLGraph::Impl::NNC::transmissibility() const
|
||||
{
|
||||
return this->trans_;
|
||||
}
|
||||
|
||||
const Opm::ECLGraph::Impl::NNC::FluxRelation&
|
||||
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->defineActivePhases(I);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1579,6 +1845,12 @@ Opm::ECLGraph::Impl::numConnections() const
|
||||
return nconn + this->nnc_.numConnections();
|
||||
}
|
||||
|
||||
const std::vector<Opm::ECLGraph::PhaseIndex>&
|
||||
Opm::ECLGraph::Impl::activePhases() const
|
||||
{
|
||||
return this->activePhases_;
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
Opm::ECLGraph::Impl::neighbours() const
|
||||
{
|
||||
@@ -1625,6 +1897,34 @@ Opm::ECLGraph::Impl::activePoreVolume() const
|
||||
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&
|
||||
Opm::ECLGraph::Impl::rawResultData() const
|
||||
{
|
||||
@@ -1640,6 +1940,11 @@ std::vector<double>
|
||||
Opm::ECLGraph::Impl::
|
||||
flux(const PhaseIndex phase) const
|
||||
{
|
||||
auto fluxUnit = [this](const int gridID)
|
||||
{
|
||||
return ::ECL::getUnitSystem(*this->src_, gridID)->reservoirRate();
|
||||
};
|
||||
|
||||
const auto vector = this->flowVector(phase);
|
||||
|
||||
auto v = std::vector<double>{};
|
||||
@@ -1650,7 +1955,8 @@ flux(const PhaseIndex phase) const
|
||||
v.reserve(totconn);
|
||||
|
||||
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()) {
|
||||
// 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
|
||||
// local grid refinement. Extract pertinent flux values for these
|
||||
// connections.
|
||||
this->fluxNNC(vector, v);
|
||||
this->fluxNNC(vector, std::move(fluxUnit), v);
|
||||
}
|
||||
|
||||
if (v.size() < totconn) {
|
||||
@@ -1677,17 +1983,81 @@ flux(const PhaseIndex phase) const
|
||||
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
|
||||
Opm::ECLGraph::Impl::defineNNCs(const ecl_grid_type* G,
|
||||
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)) {
|
||||
this->nnc_.add(this->grid_, this->activeOffset_, nnc);
|
||||
this->nnc_.add(this->grid_, this->activeOffset_, trans_unit, nnc);
|
||||
}
|
||||
}
|
||||
|
||||
template <class GetFluxUnit>
|
||||
void
|
||||
Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
||||
GetFluxUnit&& fluxUnit,
|
||||
std::vector<double>& flux) const
|
||||
{
|
||||
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 fluxID = rel.makeKeyword(vector);
|
||||
|
||||
auto gridID = 0;
|
||||
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
|
||||
// iteration.
|
||||
gridID += 1;
|
||||
const auto& iset =
|
||||
rel.indexSet().getGridCollection(gridID);
|
||||
|
||||
if (iset.empty()) {
|
||||
// No NNCs for this category in this grid. Skip.
|
||||
@@ -1719,13 +2087,16 @@ Opm::ECLGraph::Impl::fluxNNC(const std::string& vector,
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto flux_unit = fluxUnit(gridID);
|
||||
|
||||
// Data fully available for (category,gridID). Assign
|
||||
// approriate subset of NNC flux vector.
|
||||
for (const auto& ix : iset) {
|
||||
assert (ix.neighIdx < v.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;
|
||||
}
|
||||
@@ -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
|
||||
Opm::ECLGraph::Impl::
|
||||
flowVector(const PhaseIndex phase) const
|
||||
@@ -1834,6 +2231,12 @@ Opm::ECLGraph::numConnections() const
|
||||
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
|
||||
{
|
||||
return this->pImpl_->neighbours();
|
||||
@@ -1844,6 +2247,11 @@ std::vector<double> Opm::ECLGraph::poreVolume() const
|
||||
return this->pImpl_->activePoreVolume();
|
||||
}
|
||||
|
||||
std::vector<double> Opm::ECLGraph::transmissibility() const
|
||||
{
|
||||
return this->pImpl_->transmissibility();
|
||||
}
|
||||
|
||||
bool Opm::ECLGraph::selectReportStep(const int rptstep) const
|
||||
{
|
||||
return this->pImpl_->selectReportStep(rptstep);
|
||||
@@ -1861,3 +2269,28 @@ flux(const PhaseIndex phase) const
|
||||
{
|
||||
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
|
||||
|
||||
#include <opm/utility/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
@@ -46,6 +47,9 @@ namespace Opm {
|
||||
public:
|
||||
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.
|
||||
ECLGraph() = delete;
|
||||
|
||||
@@ -124,6 +128,12 @@ namespace Opm {
|
||||
/// Retrieve number of connections in graph.
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// 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;
|
||||
|
||||
/// 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
|
||||
@@ -156,21 +175,57 @@ namespace Opm {
|
||||
/// to method selectReportStep().
|
||||
const ::Opm::ECLResultData& rawResultData() const;
|
||||
|
||||
/// Enum for indicating requested phase from the flux() method.
|
||||
enum class PhaseIndex { Aqua = 0, Liquid = 1, Vapour = 2 };
|
||||
|
||||
/// Retrive phase flux on all connections defined by \code
|
||||
/// Retrieve phase flux on all connections defined by \code
|
||||
/// 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
|
||||
/// 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
|
||||
/// output to the restart file).
|
||||
/// output to the restart file). Numerical values in SI units
|
||||
/// (rm^3/s).
|
||||
std::vector<double>
|
||||
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:
|
||||
/// Implementation class.
|
||||
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/ECLResultData.hpp>
|
||||
#include <opm/utility/ECLUnitHandling.hpp>
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
#include <ert/ecl_well/well_const.h>
|
||||
@@ -108,15 +109,7 @@ namespace Opm
|
||||
// Reservoir rate units from code used in INTEHEAD.
|
||||
double resRateUnit(const int unit_code)
|
||||
{
|
||||
using prefix::centi;
|
||||
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));
|
||||
}
|
||||
return ECLUnits::createUnitSystem(unit_code)->reservoirRate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 ()
|
||||
@@ -15,15 +15,19 @@
|
||||
/*
|
||||
Copyright 2009, 2010, 2011, 2012 SINTEF ICT, Applied Mathematics.
|
||||
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
|
||||
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/>.
|
||||
*/
|
||||
@@ -31,26 +35,35 @@
|
||||
#ifndef OPM_UNITS_HEADER
|
||||
#define OPM_UNITS_HEADER
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Constants and routines to assist in handling units of measurement. These are
|
||||
* geared towards handling common units in reservoir descriptions.
|
||||
*/
|
||||
The unit sets emplyed in ECLIPSE, in particular the FIELD units,
|
||||
are quite inconsistent. Ideally one should choose units for a set
|
||||
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
|
||||
[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.
|
||||
{
|
||||
const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */
|
||||
const double milli = 1.0e-3; /**< Unit prefix [m] */
|
||||
const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */
|
||||
const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */
|
||||
const double kilo = 1.0e3; /**< Unit prefix [k] */
|
||||
const double mega = 1.0e6; /**< Unit prefix [M] */
|
||||
const double giga = 1.0e9; /**< Unit prefix [G] */
|
||||
constexpr const double micro = 1.0e-6; /**< Unit prefix [\f$\mu\f$] */
|
||||
constexpr const double milli = 1.0e-3; /**< Unit prefix [m] */
|
||||
constexpr const double centi = 1.0e-2; /**< Non-standard unit prefix [c] */
|
||||
constexpr const double deci = 1.0e-1; /**< Non-standard unit prefix [d] */
|
||||
constexpr const double kilo = 1.0e3; /**< Unit prefix [k] */
|
||||
constexpr const double mega = 1.0e6; /**< Unit prefix [M] */
|
||||
constexpr const double giga = 1.0e9; /**< Unit prefix [G] */
|
||||
} // namespace prefix
|
||||
|
||||
namespace unit
|
||||
@@ -71,8 +84,8 @@ namespace Opm
|
||||
{
|
||||
///\name Common powers
|
||||
/// @{
|
||||
inline double square(double v) { return v * v; }
|
||||
inline double cubic (double v) { return v * v * v; }
|
||||
constexpr double square(double v) { return v * v; }
|
||||
constexpr double cubic (double v) { return v * v * v; }
|
||||
/// @}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
@@ -81,32 +94,33 @@ namespace Opm
|
||||
|
||||
/// \name Length
|
||||
/// @{
|
||||
const double meter = 1;
|
||||
const double inch = 2.54 * prefix::centi*meter;
|
||||
const double feet = 12 * inch;
|
||||
constexpr const double meter = 1;
|
||||
constexpr const double inch = 2.54 * prefix::centi*meter;
|
||||
constexpr const double feet = 12 * inch;
|
||||
/// @}
|
||||
|
||||
/// \name Time
|
||||
/// @{
|
||||
const double second = 1;
|
||||
const double minute = 60 * second;
|
||||
const double hour = 60 * minute;
|
||||
const double day = 24 * hour;
|
||||
const double year = 365 * day;
|
||||
constexpr const double second = 1;
|
||||
constexpr const double minute = 60 * second;
|
||||
constexpr const double hour = 60 * minute;
|
||||
constexpr const double day = 24 * hour;
|
||||
constexpr const double year = 365 * day;
|
||||
/// @}
|
||||
|
||||
/// \name Volume
|
||||
/// @{
|
||||
const double gallon = 231 * cubic(inch);
|
||||
const double stb = 42 * gallon;
|
||||
const double liter = 1 * cubic(prefix::deci*meter);
|
||||
constexpr const double gallon = 231 * cubic(inch);
|
||||
constexpr const double stb = 42 * gallon;
|
||||
constexpr const double liter = 1 * cubic(prefix::deci*meter);
|
||||
/// @}
|
||||
|
||||
/// \name Mass
|
||||
/// @{
|
||||
const double kilogram = 1;
|
||||
constexpr const double kilogram = 1;
|
||||
constexpr const double gram = 1.0e-3 * kilogram;
|
||||
// http://en.wikipedia.org/wiki/Pound_(mass)#Avoirdupois_pound
|
||||
const double pound = 0.45359237 * kilogram;
|
||||
constexpr const double pound = 0.45359237 * kilogram;
|
||||
/// @}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
@@ -115,7 +129,7 @@ namespace Opm
|
||||
|
||||
/// \name Standardised constant
|
||||
/// @{
|
||||
const double gravity = 9.80665 * meter/square(second);
|
||||
constexpr const double gravity = 9.80665 * meter/square(second);
|
||||
/// @}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
@@ -124,31 +138,45 @@ namespace Opm
|
||||
|
||||
/// \name Force
|
||||
/// @{
|
||||
const double Newton = kilogram*meter / square(second); // == 1
|
||||
const double lbf = pound * gravity; // Pound-force
|
||||
constexpr const double Newton = kilogram*meter / square(second); // == 1
|
||||
constexpr const double dyne = 1e-5*Newton;
|
||||
constexpr const double lbf = pound * gravity; // Pound-force
|
||||
/// @}
|
||||
|
||||
/// \name Pressure
|
||||
/// @{
|
||||
const double Pascal = Newton / square(meter); // == 1
|
||||
const double barsa = 100000 * Pascal;
|
||||
const double atm = 101325 * Pascal;
|
||||
const double psia = lbf / square(inch);
|
||||
constexpr const double Pascal = Newton / square(meter); // == 1
|
||||
constexpr const double barsa = 100000 * Pascal;
|
||||
constexpr const double atm = 101325 * Pascal;
|
||||
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
|
||||
/// @{
|
||||
const double Pas = Pascal * second; // == 1
|
||||
const double Poise = prefix::deci*Pas;
|
||||
constexpr const double Pas = Pascal * second; // == 1
|
||||
constexpr const double Poise = prefix::deci*Pas;
|
||||
/// @}
|
||||
|
||||
namespace perm_details {
|
||||
const double p_grad = atm / (prefix::centi*meter);
|
||||
const double area = square(prefix::centi*meter);
|
||||
const double flux = cubic (prefix::centi*meter) / second;
|
||||
const double velocity = flux / area;
|
||||
const double visc = prefix::centi*Poise;
|
||||
const double darcy = (velocity * visc) / p_grad;
|
||||
constexpr const double p_grad = atm / (prefix::centi*meter);
|
||||
constexpr const double area = square(prefix::centi*meter);
|
||||
constexpr const double flux = cubic (prefix::centi*meter) / second;
|
||||
constexpr const double velocity = flux / area;
|
||||
constexpr const double visc = prefix::centi*Poise;
|
||||
constexpr const double darcy = (velocity * visc) / p_grad;
|
||||
// == 1e-7 [m^2] / 101325
|
||||
// == 9.869232667160130e-13 [m^2]
|
||||
}
|
||||
@@ -161,7 +189,7 @@ namespace Opm
|
||||
/// of \f$1\,\mathit{atm}/\mathit{cm}\f$ acting across an area of
|
||||
/// \f$1\,\mathit{cm}^2\f$.
|
||||
///
|
||||
const double darcy = perm_details::darcy;
|
||||
constexpr const double darcy = perm_details::darcy;
|
||||
/// @}
|
||||
|
||||
/**
|
||||
@@ -186,7 +214,7 @@ namespace Opm
|
||||
* @param[in] unit Physical unit of measurement.
|
||||
* @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;
|
||||
}
|
||||
@@ -209,21 +237,90 @@ namespace Opm
|
||||
* @param[in] unit Physical unit of measurement.
|
||||
* @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;
|
||||
}
|
||||
} // 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 detail {
|
||||
// Some units are sometimes unused, and generate a (potentially) large number of warnings
|
||||
// Adding them here silences these warnings, and should have no side-effects
|
||||
//JJS double __attribute__((unused)) unused_units = stb + liter + barsa + psia + darcy;
|
||||
} // namespace detail
|
||||
#endif
|
||||
namespace Field {
|
||||
using namespace prefix;
|
||||
using namespace unit;
|
||||
constexpr const double Pressure = psia;
|
||||
constexpr const double Temperature = degFahrenheit;
|
||||
constexpr const double TemperatureOffset = degFahrenheitOffset;
|
||||
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
|
||||
@@ -323,18 +323,14 @@ namespace FlowDiagnostics
|
||||
|
||||
// Compute time-of-flight and tracer.
|
||||
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)
|
||||
/ (total_influx * tracer_[cell]);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
tof_[cell] = max_tof_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user