Compare commits

..

13 Commits

Author SHA1 Message Date
Arne Morten Kvarving
8fa165c0d7 bump version to 2019.04 2019-05-08 08:29:45 +02:00
Arne Morten Kvarving
8ff950227f bump version to 2019.04-rc3 2019-05-03 10:42:48 +02:00
Joakim Hove
bc5aeaf4bf Internalize a global WHISTCTL setting in the Schedule object 2019-05-03 10:40:26 +02:00
Atgeirr Flø Rasmussen
0108437201 Bugfix: do not keep old control modes.
They will be treated as limits, and cause wrong behaviour.
2019-05-03 08:59:01 +02:00
Arne Morten Kvarving
828fe797f0 bump version to 2019.04 rc2 2019-04-29 08:50:42 +02:00
Jostein Alvestad
8f24032612 Added a improvement to the handling of MSW data for writing to restart file
Improvement to handle more general MSW data  for writing to
Eclipse compatible restart file
2019-04-26 09:40:50 +02:00
Joakim Hove
ee6bfbd76f Add configuration for keyword FLUXTYPE 2019-04-25 12:33:57 +02:00
Markus Blatt
54ee583112 Reintroduce overeagerly removed const qualifiers before function parameters.
Actually, the compiler does not complain about const in void func(const T type).
It just complains about const qualifiers before by-value return types.
2019-04-25 11:38:51 +02:00
Markus Blatt
b69010cfc9 reintroduce some const with elegance 2019-04-25 11:38:51 +02:00
Markus Blatt
2859980cb4 Removes nearly all warnings experienced with g++-8.3
Namely:
- unused parameter
- type qualifiers ignored ...
- catchinhg polymorphic type ... by value
2019-04-25 11:38:51 +02:00
Joakim Hove
3ffefd8098 Fix bug in WellSegments== 2019-04-25 08:31:32 +02:00
Joakim Hove
852fdd7ac4 Add "size" : 1 attribute to CARFIN keyword 2019-04-25 08:31:32 +02:00
Arne Morten Kvarving
47a43daeac bump versions to 2019.04-rc1 2019-04-12 10:29:12 +02:00
129 changed files with 4650 additions and 21959 deletions

View File

@@ -158,30 +158,24 @@ endif()
# Build the compare utilities
if(ENABLE_ECL_INPUT)
add_library(testutil STATIC
examples/test_util/EclFile.cpp
examples/test_util/EclFilesComparator.cpp
examples/test_util/EclOutput.cpp
examples/test_util/EclIntegrationTest.cpp
examples/test_util/EclRegressionTest.cpp
examples/test_util/EclUtil.cpp
examples/test_util/EGrid.cpp
examples/test_util/ERft.cpp
examples/test_util/ERst.cpp
examples/test_util/ESmry.cpp)
foreach(target compareECL convertECL)
add_executable(${target} examples/test_util/${target}.cpp)
target_link_libraries(${target} testutil opmcommon)
install(TARGETS ${target} DESTINATION bin)
endforeach()
examples/test_util/summaryComparator.cpp
examples/test_util/summaryIntegrationTest.cpp
examples/test_util/summaryRegressionTest.cpp)
target_link_libraries(testutil ecl)
add_executable(compareECL examples/test_util/compareECL.cpp)
target_link_libraries(compareECL testutil opmcommon)
# Add the tests
set(_libs testutil opmcommon
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
foreach(test test_EclFilesComparator test_EclIO test_EclRegressionTest
test_EGrid test_ERft test_ERst test_ESmry)
foreach(test test_compareSummary test_EclFilesComparator)
opm_add_test(${test} CONDITION ENABLE_ECL_INPUT
LIBRARIES ${_libs}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
LIBRARIES ${_libs})
endforeach()
install(TARGETS compareECL DESTINATION bin)
endif()
# Install build system files

View File

@@ -21,6 +21,7 @@
# the library needs it.
list (APPEND MAIN_SOURCE_FILES
examples/test_util/EclFile.cpp
src/opm/common/data/SimulationDataContainer.cpp
src/opm/common/OpmLog/CounterLog.cpp
src/opm/common/OpmLog/EclipsePRTLog.cpp
@@ -45,7 +46,6 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/Deck/DeckRecord.cpp
src/opm/parser/eclipse/Deck/DeckOutput.cpp
src/opm/parser/eclipse/Deck/Section.cpp
src/opm/parser/eclipse/Deck/UDAValue.cpp
src/opm/parser/eclipse/EclipseState/AquiferCT.cpp
src/opm/parser/eclipse/EclipseState/Aquifetp.cpp
src/opm/parser/eclipse/EclipseState/Aquancon.cpp
@@ -102,7 +102,6 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/Tuning.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Connection.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Well2.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WList.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.cpp
@@ -172,6 +171,7 @@ if(ENABLE_ECL_OUTPUT)
src/opm/output/eclipse/CreateDoubHead.cpp
src/opm/output/eclipse/CreateInteHead.cpp
src/opm/output/eclipse/CreateLogiHead.cpp
src/opm/output/eclipse/WellDataSerializers.cpp
src/opm/output/eclipse/DoubHEAD.cpp
src/opm/output/eclipse/EclipseGridInspector.cpp
src/opm/output/eclipse/EclipseIO.cpp
@@ -190,6 +190,7 @@ if(ENABLE_ECL_OUTPUT)
endif()
list (APPEND TEST_SOURCE_FILES
tests/test_EclFile.cpp
tests/test_calculateCellVol.cpp
tests/test_cmp.cpp
tests/test_cubic.cpp
@@ -289,6 +290,8 @@ if(ENABLE_ECL_OUTPUT)
tests/test_Wells.cpp
tests/test_WindowedArray.cpp
tests/test_writenumwells.cpp
tests/test_serialize_ICON.cpp
tests/test_serialize_SCON.cpp
)
endif()
@@ -317,17 +320,6 @@ endif()
list (APPEND EXAMPLE_SOURCE_FILES
)
if(ENABLE_ECL_INPUT)
list (APPEND TEST_DATA_FILES
tests/ECLFILE.INIT
tests/ECLFILE.FINIT
tests/SPE1CASE1.EGRID
tests/SPE1CASE1.RFT
tests/SPE1_TESTCASE.UNRST
tests/SPE1_TESTCASE.FUNRST
tests/SPE1CASE1.UNSMRY
tests/SPE1CASE1_RST60.SMSPEC
tests/SPE1CASE1_RST60.UNSMRY
)
list (APPEND EXAMPLE_SOURCE_FILES
examples/opmi.cpp
examples/opmpack.cpp
@@ -348,6 +340,8 @@ if(ENABLE_ECL_INPUT)
endif()
list( APPEND PUBLIC_HEADER_FILES
examples/test_util/EclFile.hpp
examples/test_util/data/EclIOdata.hpp
opm/common/ErrorMacros.hpp
opm/common/Exceptions.hpp
opm/common/data/SimulationDataContainer.hpp
@@ -507,7 +501,6 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WList.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.hpp
@@ -555,7 +548,6 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/Deck/DeckOutput.hpp
opm/parser/eclipse/Deck/DeckKeyword.hpp
opm/parser/eclipse/Deck/DeckRecord.hpp
opm/parser/eclipse/Deck/UDAValue.hpp
opm/parser/eclipse/RawDeck/StarToken.hpp
opm/parser/eclipse/RawDeck/RawEnums.hpp
opm/parser/eclipse/RawDeck/RawRecord.hpp
@@ -570,7 +562,6 @@ if(ENABLE_ECL_OUTPUT)
opm/output/eclipse/VectorItems/connection.hpp
opm/output/eclipse/VectorItems/group.hpp
opm/output/eclipse/VectorItems/intehead.hpp
opm/output/eclipse/VectorItems/logihead.hpp
opm/output/eclipse/VectorItems/msw.hpp
opm/output/eclipse/VectorItems/well.hpp
opm/output/eclipse/AggregateGroupData.hpp

View File

@@ -1,6 +1,5 @@
set(genkw_SOURCES src/opm/json/JsonObject.cpp
src/opm/parser/eclipse/Parser/createDefaultKeywordList.cpp
src/opm/parser/eclipse/Deck/UDAValue.cpp
src/opm/parser/eclipse/Deck/Deck.cpp
src/opm/parser/eclipse/Deck/DeckItem.cpp
src/opm/parser/eclipse/Deck/DeckKeyword.cpp

2
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
opm-common (2019.04-pre~xenial) xenial; urgency=medium
opm-common (2019.04-rfinal-1~xenial) xenial; urgency=medium
* New release

View File

@@ -5,8 +5,8 @@
Module: opm-common
Description: Open Porous Media Initiative shared infrastructure
Version: 2019.10-pre
Label: 2019.10-pre
Version: 2019.04
Label: 2019.04
Maintainer: opm@opm-project.org
MaintainerName: OPM community
Url: http://opm-project.org

View File

@@ -1,186 +0,0 @@
/*
Copyright 2019 Equinor 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 "EGrid.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
#include <numeric>
EGrid::EGrid(const std::string &filename) : EclFile(filename)
{
EclFile file(filename);
auto gridhead = get<int>("GRIDHEAD");
nijk[0] = gridhead[1];
nijk[1] = gridhead[2];
nijk[2] = gridhead[3];
if (file.hasKey("ACTNUM")) {
auto actnum = get<int>("ACTNUM");
nactive = 0;
for (unsigned int i = 0; i < actnum.size(); i++) {
if (actnum[i] > 0) {
act_index.push_back(nactive);
glob_index.push_back(i);
nactive++;
} else {
act_index.push_back(-1);
}
}
} else {
int nCells = nijk[0] * nijk[1] * nijk[2];
act_index.resize(nCells);
glob_index.resize(nCells);
std::iota(act_index.begin(), act_index.end(), 0);
std::iota(glob_index.begin(), glob_index.end(), 0);
}
coord_array = get<float>("COORD");
zcorn_array = get<float>("ZCORN");
}
int EGrid::global_index(int i, int j, int k) const
{
if (i < 0 || i >= nijk[0] || j < 0 || j >= nijk[1] || k < 0 || k >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j or/and k out of range");
}
return i + j * nijk[0] + k * nijk[0] * nijk[1];
}
int EGrid::active_index(int i, int j, int k) const
{
int n = i + j * nijk[0] + k * nijk[0] * nijk[1];
if (i < 0 || i >= nijk[0] || j < 0 || j >= nijk[1] || k < 0 || k >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j or/and k out of range");
}
return act_index[n];
}
std::array<int, 3> EGrid::ijk_from_active_index(int actInd) const
{
if (actInd < 0 || actInd >= nactive) {
OPM_THROW(std::invalid_argument, "active index out of range");
}
int _glob = glob_index[actInd];
std::array<int, 3> result;
result[2] = _glob / (nijk[0] * nijk[1]);
int rest = _glob % (nijk[0] * nijk[1]);
result[1] = rest / nijk[0];
result[0] = rest % nijk[0];
return result;
}
std::array<int, 3> EGrid::ijk_from_global_index(int globInd) const
{
if (globInd < 0 || globInd >= nijk[0] * nijk[1] * nijk[2]) {
OPM_THROW(std::invalid_argument, "global index out of range");
}
std::array<int, 3> result;
result[2] = globInd / (nijk[0] * nijk[1]);
int rest = globInd % (nijk[0] * nijk[1]);
result[1] = rest / nijk[0];
result[0] = rest % nijk[0];
return result;
}
void EGrid::getCellCorners(const std::array<int, 3>& ijk,
std::vector<double>& X,
std::vector<double>& Y,
std::vector<double>& Z) const
{
std::vector<int> zind;
std::vector<int> pind;
if (X.size() < 8 || Y.size() < 8 || Z.size() < 8) {
OPM_THROW(std::invalid_argument,
"In routine cellConrner. X, Y and Z should be a vector of size 8");
}
if (ijk[0] < 0 || ijk[0] >= nijk[0] || ijk[1] < 0 || ijk[1] >= nijk[1] || ijk[2] < 0 || ijk[2] >= nijk[2]) {
OPM_THROW(std::invalid_argument, "i, j and/or k out of range");
}
// calculate indices for grid pillars in COORD arrray
pind.push_back(ijk[1]*(nijk[0]+1)*6 + ijk[0]*6);
pind.push_back(pind[0] + 6);
pind.push_back(pind[0] + (nijk[0]+1)*6);
pind.push_back(pind[2] + 6);
// get depths from zcorn array in ZCORN array
zind.push_back(ijk[2]*nijk[0]*nijk[1]*8 + ijk[1]*nijk[0]*4 + ijk[0]*2);
zind.push_back(zind[0] + 1);
zind.push_back(zind[0] + nijk[0]*2);
zind.push_back(zind[2] + 1);
for (int n = 0; n < 4; n++) {
zind.push_back(zind[n] + nijk[0]*nijk[1]*4);
}
for (int n = 0; n< 8; n++){
Z[n] = zcorn_array[zind[n]];
}
for (int n = 0; n < 4; n++) {
double xt = coord_array[pind[n]];
double yt = coord_array[pind[n] + 1];
double zt = coord_array[pind[n] + 2];
double xb = coord_array[pind[n] + 3];
double yb = coord_array[pind[n] + 4];
double zb = coord_array[pind[n]+5];
X[n] = xt + (xb-xt) / (zt-zb) * (zt - Z[n]);
X[n+4] = xt + (xb-xt) / (zt-zb) * (zt-Z[n+4]);
Y[n] = yt+(yb-yt)/(zt-zb)*(zt-Z[n]);
Y[n+4] = yt+(yb-yt)/(zt-zb)*(zt-Z[n+4]);
}
}
void EGrid::getCellCorners(int globindex, std::vector<double>& X,
std::vector<double>& Y, std::vector<double>& Z) const
{
return getCellCorners(ijk_from_global_index(globindex),X,Y,Z);
}

View File

@@ -1,62 +0,0 @@
/*
Copyright 2019 Equinor 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 EGRID_HPP
#define EGRID_HPP
#include "EclFile.hpp"
#include <array>
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class EGrid : public EclFile
{
public:
explicit EGrid(const std::string& filename);
int global_index(int i, int j, int k) const;
int active_index(int i, int j, int k) const;
const std::array<int, 3>& dimension() const { return nijk; }
std::array<int, 3> ijk_from_active_index(int actInd) const;
std::array<int, 3> ijk_from_global_index(int globInd) const;
void getCellCorners(int globindex, std::vector<double>& X, std::vector<double>& Y, std::vector<double>& Z) const;
void getCellCorners(const std::array<int, 3>& ijk, std::vector<double>& X, std::vector<double>& Y, std::vector<double>& Z) const;
int activeCells() const { return nactive; }
int totalNumberOfCells() const { return nijk[0] * nijk[1] * nijk[2]; }
private:
std::array<int, 3> nijk;
int nNNC, nactive;
std::vector<int> act_index;
std::vector<int> glob_index;
std::vector<float> coord_array;
std::vector<float> zcorn_array;
};
#endif

View File

@@ -1,310 +0,0 @@
/*
Copyright 2019 Equinor 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 "ERft.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
ERft::ERft(const std::string &filename) : EclFile(filename)
{
loadData();
std::vector<int> first;
std::vector<int> second;
std::vector<std::string> wellName;
std::vector<RftDate> dates;
auto listOfArrays = getList();
for (size_t i = 0; i < listOfArrays.size(); i++) {
std::string name = std::get<0>(listOfArrays[i]);
if (name == "TIME") {
first.push_back(i);
auto vect1 = get<float>(i);
timeList.push_back(vect1[0]);
}
if (name == "DATE") {
auto vect1 = get<int>(i);
RftDate date(vect1[2],vect1[1],vect1[0]);
dateList.insert(date);
dates.push_back(date);
}
if (name == "WELLETC"){
auto vect1 = get<std::string>(i);
wellList.insert(vect1[1]);
wellName.push_back(vect1[1]);
}
}
for (size_t i = 0; i < first.size(); i++) {
std::pair<int,int> range;
range.first = first[i];
if (i == first.size() - 1) {
range.second = listOfArrays.size();
} else {
range.second = first[i+1];
}
arrIndexRange[i] = range;
}
numReports = first.size();
for (size_t i = 0; i < wellName.size(); i++) {
std::pair<std::string,RftDate> wellDatePair(wellName[i],dates[i]);
reportIndex[wellDatePair] = i;
rftReportList.push_back(wellDatePair);
}
}
bool ERft::hasRft(const std::string& wellName, const RftDate& date) const
{
return reportIndex.find({wellName, date}) != reportIndex.end();
}
bool ERft::hasRft(const std::string& wellName, int year, int month, int day) const
{
RftDate date(year, month, day);
return reportIndex.find({wellName,date}) != reportIndex.end();
}
int ERft::getReportIndex(const std::string& wellName, const RftDate& date) const
{
std::pair<std::string,std::tuple<int,int,int>> wellDatePair(wellName,date);
auto rIndIt = reportIndex.find(wellDatePair);
if (rIndIt == reportIndex.end()) {
int y = std::get<0>(date);
int m = std::get<1>(date);
int d = std::get<2>(date);
std::string dateStr=std::to_string(y) + "/" + std::to_string(m) + "/" + std::to_string(d);
std::string message="RFT data not found for well " + wellName + " at date: " + dateStr;
OPM_THROW(std::invalid_argument, message);
}
return rIndIt->second;
}
bool ERft::hasArray(const std::string& arrayName, const std::string& wellName,
const RftDate& date) const
{
int reportInd = getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(reportInd);
int fromInd = searchInd->second.first;
int toInd = searchInd->second.second;
auto it = std::find(array_name.begin()+fromInd,array_name.begin()+toInd,arrayName);
return it != array_name.begin() + toInd;
}
int ERft::getArrayIndex(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int rInd= getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(rInd);
int fromInd =searchInd->second.first;
int toInd = searchInd->second.second;
auto it=std::find(array_name.begin()+fromInd,array_name.begin()+toInd,name);
if (std::distance(array_name.begin(),it) == toInd) {
int y = std::get<0>(date);
int m = std::get<1>(date);
int d = std::get<2>(date);
std::string dateStr = std::to_string(y) + "/" + std::to_string(m) + "/" + std::to_string(d);
std::string message = "Array " + name + " not found for RFT, well: " + wellName + " date: " + dateStr;
OPM_THROW(std::invalid_argument, message);
}
return std::distance(array_name.begin(),it);
}
template<> const std::vector<float>&
ERft::getRft<float>(const std::string& name, const std::string &wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::REAL) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = real_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<double>&
ERft::getRft<double>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::DOUB) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = doub_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<int>&
ERft::getRft<int>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::INTE) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = inte_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<bool>&
ERft::getRft<bool>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::LOGI) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = logi_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<std::string>&
ERft::getRft<std::string>(const std::string& name, const std::string& wellName,
const RftDate& date) const
{
int arrInd = getArrayIndex(name, wellName, date);
if (array_type[arrInd] != EIOD::CHAR) {
std::string message = "Array " + name + " found in RFT file for selected date and well, but called with wrong type";
OPM_THROW(std::runtime_error, message);
}
auto search_array = char_array.find(arrInd);
return search_array->second;
}
template<> const std::vector<int>&
ERft::getRft<int>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<int>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<float>&
ERft::getRft<float>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<float>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<double>&
ERft::getRft<double>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<double>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<std::string>&
ERft::getRft<std::string>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<std::string>(name, wellName, RftDate{year, month, day});
}
template<> const std::vector<bool>&
ERft::getRft<bool>(const std::string& name, const std::string& wellName,
int year, int month, int day) const
{
return getRft<bool>(name, wellName, RftDate{year, month, day});
}
std::vector<EclFile::EclEntry> ERft::listOfRftArrays(const std::string& wellName,
const RftDate& date) const
{
std::vector<EclEntry> list;
int rInd = getReportIndex(wellName, date);
auto searchInd = arrIndexRange.find(rInd);
for (int i = searchInd->second.first; i < searchInd->second.second; i++) {
list.emplace_back(array_name[i], array_type[i], array_size[i]);
}
return list;
}
std::vector<EclFile::EclEntry> ERft::listOfRftArrays(const std::string& wellName,
int year, int month, int day) const
{
return listOfRftArrays(wellName, RftDate{year, month, day});
}
std::vector<std::string> ERft::listOfWells() const
{
return { this->wellList.begin(), this->wellList.end() };
}
std::vector<ERft::RftDate> ERft::listOfdates() const
{
return { this->dateList.begin(), this->dateList.end() };
}

View File

@@ -1,83 +0,0 @@
/*
Copyright 2019 Equinor 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 ERFT_HPP
#define ERFT_HPP
#include "EclFile.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
#include <set>
class ERft : public EclFile
{
public:
explicit ERft(const std::string &filename);
using RftDate = std::tuple<int,int,int>;
template <typename T>
const std::vector<T>& getRft(const std::string& name, const std::string& wellName,
const RftDate& date) const;
template <typename T>
const std::vector<T>& getRft(const std::string& name, const std::string& wellName,
int year, int month, int day) const;
std::vector<std::string> listOfWells() const;
std::vector<RftDate> listOfdates() const;
using RftReportList = std::vector<std::pair<std::string, RftDate>>;
const RftReportList& listOfRftReports() const { return rftReportList; }
bool hasRft(const std::string& wellName, const RftDate& date) const;
bool hasRft(const std::string& wellName, int year, int month, int day) const;
std::vector<EclEntry> listOfRftArrays(const std::string& wellName,
const RftDate& date) const;
std::vector<EclEntry> listOfRftArrays(const std::string& wellName,
int year, int month, int day) const;
bool hasArray(const std::string& arrayName, const std::string& wellName,
const RftDate& date) const;
private:
std::map<int, std::pair<int,int>> arrIndexRange;
int numReports;
std::vector<float> timeList;
std::set<std::string> wellList;
std::set<RftDate> dateList;
RftReportList rftReportList;
std::map<std::pair<std::string,RftDate>,int> reportIndex; // mapping report index to wellName and date (tupe)
int getReportIndex(const std::string& wellName, const RftDate& date) const;
int getArrayIndex(const std::string& name, const std::string& wellName,
const RftDate& date) const;
};
#endif

View File

@@ -1,178 +0,0 @@
/*
Copyright 2019 Equinor 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 "ERst.hpp"
#include <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
ERst::ERst(const std::string& filename) : EclFile(filename)
{
loadData("SEQNUM");
std::vector<int> firstIndex;
for (size_t i = 0; i < array_name.size(); i++) {
if (array_name[i] == "SEQNUM") {
auto seqn = get<int>(i);
seqnum.push_back(seqn[0]);
firstIndex.push_back(i);
}
}
for (size_t i = 0; i < seqnum.size(); i++) {
std::pair<int,int> range;
range.first = firstIndex[i];
if (i != seqnum.size() - 1) {
range.second = firstIndex[i+1];
} else {
range.second = array_name.size();
}
arrIndexRange[seqnum[i]] = range;
}
nReports = seqnum.size();
for (int i = 0; i < nReports; i++) {
reportLoaded[seqnum[i]] = false;
}
};
bool ERst::hasReportStepNumber(int number) const
{
auto search = arrIndexRange.find(number);
return search != arrIndexRange.end();
}
void ERst::loadReportStepNumber(int number)
{
if (!hasReportStepNumber(number)) {
std::string message="Trying to load non existing report step number " + std::to_string(number);
OPM_THROW(std::invalid_argument, message);
}
std::vector<int> arrayIndexList;
arrayIndexList.reserve(arrIndexRange[number].second - arrIndexRange[number].first + 1);
for (int i = arrIndexRange[number].first; i < arrIndexRange[number].second; i++) {
arrayIndexList.push_back(i);
}
loadData(arrayIndexList);
reportLoaded[number] = true;
}
std::vector<EclFile::EclEntry> ERst::listOfRstArrays(int reportStepNumber)
{
std::vector<EclEntry> list;
if (!hasReportStepNumber(reportStepNumber)) {
std::string message = "Trying to get list of arrays from non existing report step number " + std::to_string(reportStepNumber);
OPM_THROW(std::invalid_argument, message);
}
const auto& rng = this->arrIndexRange[reportStepNumber];
list.reserve(rng.second - rng.first);
for (int i = rng.first; i < rng.second; i++) {
list.emplace_back(array_name[i], array_type[i], array_size[i]);
}
return list;
}
int ERst::getArrayIndex(const std::string& name, int number) const
{
if (!hasReportStepNumber(number)) {
std::string message = "Trying to get vector " + name + " from non existing sequence " + std::to_string(number);
OPM_THROW(std::invalid_argument, message);
}
auto search = reportLoaded.find(number);
if (!search->second) {
std::string message = "Data not loaded for sequence " + std::to_string(number);
OPM_THROW(std::runtime_error, message);
}
auto range_it = arrIndexRange.find(number);
std::pair<int,int> indexRange = range_it->second;
auto it = std::find(array_name.begin() + indexRange.first,
array_name.begin() + indexRange.second, name);
if (std::distance(array_name.begin(),it) == indexRange.second) {
std::string message = "Array " + name + " not found in sequence " + std::to_string(number);
OPM_THROW(std::runtime_error, message);
}
return std::distance(array_name.begin(), it);
}
template<>
const std::vector<int>& ERst::getRst<int>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::INTE, inte_array, "integer");
}
template<>
const std::vector<float>& ERst::getRst<float>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::REAL, real_array, "float");
}
template<>
const std::vector<double>& ERst::getRst<double>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::DOUB, doub_array, "double");
}
template<>
const std::vector<bool>& ERst::getRst<bool>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::LOGI, logi_array, "bool");
}
template<>
const std::vector<std::string>& ERst::getRst<std::string>(const std::string& name, int reportStepNumber)
{
int ind = getArrayIndex(name, reportStepNumber);
return getImpl(ind, EIOD::CHAR, char_array, "string");
}

View File

@@ -1,57 +0,0 @@
/*
Copyright 2019 Equinor 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 ERST_HPP
#define ERST_HPP
#include "EclFile.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class ERst : public EclFile
{
public:
explicit ERst(const std::string& filename);
bool hasReportStepNumber(int number) const;
void loadReportStepNumber(int number);
template <typename T>
const std::vector<T>& getRst(const std::string& name, int reportStepNumber);
const std::vector<int>& listOfReportStepNumbers() const { return seqnum; }
std::vector<EclEntry> listOfRstArrays(int reportStepNumber);
private:
int nReports;
std::vector<int> seqnum; // report step numbers, from SEQNUM array in restart file
std::unordered_map<int,bool> reportLoaded;
std::map<int, std::pair<int,int>> arrIndexRange; // mapping report step number to array indeces (start and end)
int getArrayIndex(const std::string& name, int seqnum) const;
};
#endif

View File

@@ -1,431 +0,0 @@
/*
Copyright 2019 Equinor 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 <string>
#include <string.h>
#include <sstream>
#include <iterator>
#include <iomanip>
#include <algorithm>
#include <unistd.h>
#include <limits>
#include <set>
#include "EclFile.hpp"
#include "ESmry.hpp"
/*
KEYWORDS WGNAMES NUMS | PARAM index Corresponding ERT key
------------------------------------------------+--------------------------------------------------
WGOR OP_1 0 | 0 WGOR:OP_1
FOPT +-+-+-+- 0 | 1 FOPT
WWCT OP_1 0 | 2 WWCT:OP_1
WIR OP_1 0 | 3 WIR:OP_1
WGOR WI_1 0 | 4 WWCT:OP_1
WWCT W1_1 0 | 5 WWCT:WI_1
BPR +-+-+- 12675 | 6 BPR:12675, BPR:i,j,k
RPR +-+-+- 1 | 7 RPR:1
FOPT +-+-+- 0 | 8 FOPT
GGPR NORTH 0 | 9 GGPR:NORTH
COPR OP_1 5628 | 10 COPR:OP_1:56286, COPR:OP_1:i,j,k
RXF +-+-+- 32768*R1(R2 + 10) | 11 RXF:2-3
SOFX OP_1 12675 | 12 SOFX:OP_1:12675, SOFX:OP_1:i,j,jk
*/
ESmry::ESmry(const std::string &filename, bool loadBaseRunData)
{
std::string rootN;
std::vector<int> actInd;
bool formatted=false;
char buff[1000];
getcwd( buff, 1000 );
std::string currentWorkingDir(buff);
std::string currentDir=currentWorkingDir;
std::string smspec_filen;
std::string unsmry_filen;
if (filename.substr(filename.length() - 7, 7) == ".SMSPEC") {
rootN = filename.substr(0,filename.length() -7);
} else if (filename.substr(filename.length() -8, 8) == ".FSMSPEC") {
rootN=filename.substr(0,filename.length() -8);
formatted = true;
} else {
rootN = filename;
}
path = currentWorkingDir;
updatePathAndRootName(path, rootN);
if (formatted) {
smspec_filen = path + "/" + rootN + ".FSMSPEC";
unsmry_filen = path + "/" + rootN + ".FUNSMRY";
} else {
smspec_filen = path + "/" + rootN + ".SMSPEC";
unsmry_filen = path + "/" + rootN + ".UNSMRY";
}
std::vector<std::pair<std::string,int>> smryArray;
EclFile smspec1(smspec_filen);
smspec1.loadData(); // loading all data
std::set<std::string> keywList;
std::vector<int> dimens = smspec1.get<int>("DIMENS");
nI = dimens[1];
nJ = dimens[2];
nK = dimens[3];
std::vector<std::string> restartArray = smspec1.get<std::string>("RESTART");
std::vector<std::string> keywords = smspec1.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec1.get<std::string>("WGNAMES");
std::vector<int> nums = smspec1.get<int>("NUMS");
for (unsigned int i=0; i<keywords.size(); i++) {
std::string str1 = makeKeyString(keywords[i], wgnames[i], nums[i]);
if (str1.length() > 0) {
keywList.insert(str1);
}
}
std::string rstRootN = "";
std::string pathRstFile = path;
getRstString(restartArray, pathRstFile, rstRootN);
smryArray.push_back({smspec_filen, dimens[5]});
// checking if this is a restart run. Supporting nested restarts (restart, from restart, ...)
// std::set keywList is storing keywords from all runs involved
while ((rstRootN != "") && (loadBaseRunData)) {
std::string rstFile=pathRstFile+"/"+rstRootN+".SMSPEC";
EclFile smspec_rst(rstFile);
smspec_rst.loadData();
std::vector<int> dimens = smspec_rst.get<int>("DIMENS");
std::vector<std::string> restartArray = smspec_rst.get<std::string>("RESTART");
std::vector<std::string> keywords = smspec_rst.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec_rst.get<std::string>("WGNAMES");
std::vector<int> nums = smspec_rst.get<int>("NUMS");
std::vector<std::string> units = smspec_rst.get<std::string>("UNITS");
for (size_t i = 0; i < keywords.size(); i++) {
std::string str1 = makeKeyString(keywords[i], wgnames[i], nums[i]);
if (str1.length() > 0) {
keywList.insert(str1);
}
}
smryArray.push_back({rstFile,dimens[5]});
getRstString(restartArray, pathRstFile, rstRootN);
}
int nFiles = static_cast<int>(smryArray.size());
// arrayInd should hold indices for each vector and runs
// n=file number, i = position in param array in file n (one array pr time step), example arrayInd[n][i] = position in keyword list (std::set)
std::vector<std::vector<int>> arrayInd;
for (int i = 0; i < nFiles; i++){
arrayInd.push_back({});
}
int n = nFiles - 1;
while (n >= 0){
auto smry = smryArray[n];
EclFile smspec(std::get<0>(smry));
smspec.loadData();
std::vector<int> dimens = smspec.get<int>("DIMENS");
nI = dimens[1];
nJ = dimens[2];
nK = dimens[3];
std::vector<std::string> keywords = smspec.get<std::string>("KEYWORDS");
std::vector<std::string> wgnames = smspec.get<std::string>("WGNAMES");
std::vector<int> nums = smspec.get<int>("NUMS");
std::vector<int> tmpVect(keywords.size(), -1);
arrayInd[n]=tmpVect;
std::set<std::string>::iterator it;
for (size_t i=0; i < keywords.size(); i++) {
std::string keyw = makeKeyString(keywords[i], wgnames[i], nums[i]);
it = std::find(keywList.begin(), keywList.end(), keyw);
if (it != keywList.end()){
arrayInd[n][i] = distance(keywList.begin(), it);
}
}
n--;
}
// param array used to stor data for the object, defined in the private section of the class
param.assign(keywList.size(), {});
int fromReportStepNumber = 0;
int reportStepNumber = 0;
int toReportStepNumber;
float time = 0.0;
int step = 0;
n = nFiles - 1;
while (n >= 0){
reportStepNumber = fromReportStepNumber;
if (n > 0) {
auto rstFrom = smryArray[n-1];
toReportStepNumber = std::get<1>(rstFrom);
} else {
toReportStepNumber = std::numeric_limits<int>::max();
}
std::string smspecFile = std::get<0>(smryArray[n]);
std::string unsmryFile = smspecFile.substr(0, smspecFile.size() - 6) + "UNSMRY";
EclFile unsmry(unsmryFile);
unsmry.loadData();
std::vector<EclFile::EclEntry> list1 = unsmry.getList();
// 2 or 3 arrays pr time step.
// If timestep is a report step: MINISTEP, PARAMS and SEQHDR
// else : MINISTEP and PARAMS
// if summary file starts with a SEQHDR, this will be ignored
int i = 0;
if (std::get<0>(list1[0]) == "SEQHDR") {
i = 1;
}
while (i < static_cast<int>(list1.size())){
if (std::get<0>(list1[i]) != "MINISTEP"){
std::string message="Reading summary file, expecting keyword MINISTEP, found '" + std::get<0>(list1[i]) + "'";
throw std::invalid_argument(message);
}
std::vector<int> ministep = unsmry.get<int>(i);
i++;
if (std::get<0>(list1[i]) != "PARAMS") {
std::string message="Reading summary file, expecting keyword PARAMS, found '" + std::get<0>(list1[i]) + "'";
throw std::invalid_argument(message);
}
std::vector<float> tmpData = unsmry.get<float>(i);
time = tmpData[0];
if (time == 0.0) {
seqTime.push_back(time);
seqIndex.push_back(step);
}
i++;
if (i < static_cast<int>(list1.size())){
if (std::get<0>(list1[i]) == "SEQHDR") {
i++;
reportStepNumber++;
seqTime.push_back(time);
seqIndex.push_back(step);
}
} else {
reportStepNumber++;
seqTime.push_back(time);
seqIndex.push_back(step);
}
// adding defaut values (0.0) in case vector not found in this particular summary file
for (size_t i = 0; i < param.size(); i++){
param[i].push_back(0.0);
}
for (size_t j = 0; j < tmpData.size(); j++) {
int ind = arrayInd[n][j];
if (ind > -1) {
param[ind][step] = tmpData[j];
}
}
if (reportStepNumber >= toReportStepNumber) {
i = static_cast<int>(list1.size());
}
step++;
}
fromReportStepNumber = toReportStepNumber;
n--;
}
nVect = keywList.size();
for (auto keyw : keywList){
keyword.push_back(keyw);
}
};
void ESmry::getRstString(const std::vector<std::string>& restartArray, std::string& pathRst, std::string& rootN) const {
rootN = "";
for (auto str : restartArray) {
rootN = rootN + str;
}
updatePathAndRootName(pathRst, rootN);
}
void ESmry::updatePathAndRootName(std::string& dir, std::string& rootN) const {
if (rootN.substr(0,2) == "./") {
rootN = rootN.substr(2, rootN.size() - 2);
}
if (rootN.substr(0,1) == "/") {
int p = rootN.find_last_of("/");
dir = rootN.substr(0, p);
rootN = rootN.substr(p + 1, rootN.size() - p - 1);
} else if (rootN.find_first_of("/") != std::string::npos) {
int p = rootN.find_last_of("/");
dir = dir + "/" + rootN.substr(0, p);
rootN = rootN.substr(p + 1, rootN.size() - p - 1);
};
}
bool ESmry::hasKey(const std::string &key) const
{
return std::find(keyword.begin(), keyword.end(), key) != keyword.end();
}
void ESmry::ijk_from_global_index(int glob,int &i,int &j,int &k)
{
int tmpGlob = glob - 1;
k = 1 + tmpGlob / (nI * nJ);
int rest = tmpGlob % (nI * nJ);
j = 1 + rest / nI;
i = 1 + rest % nI;
}
std::string ESmry::makeKeyString(const std::string &keyword, const std::string &wgname, int num)
{
std::string keyStr;
std::vector<std::string> segmExcep= {"STEPTYPE", "SEPARATE", "SUMTHIN"};
if (keyword.substr(0, 1) == "A") {
keyStr = keyword + ":" + std::to_string(num);
} else if (keyword.substr(0, 1) == "B") {
int _i,_j,_k;
ijk_from_global_index(num, _i, _j, _k);
keyStr = keyword + ":" + std::to_string(_i) + "," + std::to_string(_j) + "," + std::to_string(_k);
} else if (keyword.substr(0, 1) == "C") {
int _i,_j,_k;
if (num > 0) {
ijk_from_global_index(num, _i, _j, _k);
keyStr = keyword + ":" + wgname+ ":" + std::to_string(_i) + "," + std::to_string(_j) + "," + std::to_string(_k);
}
} else if (keyword.substr(0, 1) == "G") {
if ( wgname != ":+:+:+:+") {
keyStr = keyword + ":" + wgname;
}
} else if (keyword.substr(0, 1) == "R" && keyword.substr(2, 1) == "F") {
// NUMS = R1 + 32768*(R2 + 10)
int r2 = 0;
int y = 32768 * (r2 + 10) - num;
while (y <0 ) {
r2++;
y = 32768 * (r2 + 10) - num;
}
r2--;
int r1 = num - 32768 * (r2 + 10);
keyStr = keyword + ":" + std::to_string(r1) + "-" + std::to_string(r2);
} else if (keyword.substr(0, 1) == "R") {
keyStr = keyword + ":" + std::to_string(num);
} else if (keyword.substr(0, 1) == "S") {
auto it = std::find(segmExcep.begin(), segmExcep.end(), keyword);
if (it != segmExcep.end()) {
keyStr = keyword;
} else {
keyStr = keyword + ":" + wgname + ":" + std::to_string(num);
}
} else if (keyword.substr(0,1) == "W") {
if (wgname != ":+:+:+:+") {
keyStr = keyword + ":" + wgname;
}
} else {
keyStr = keyword;
}
return keyStr;
}
const std::vector<float>& ESmry::get(const std::string& name) const
{
auto it = std::find(keyword.begin(), keyword.end(), name);
if (it == keyword.end()) {
std::string message="keyword " + name + " not found ";
OPM_THROW(std::invalid_argument, message);
}
int ind = std::distance(keyword.begin(), it);
return param[ind];
}

View File

@@ -1,60 +0,0 @@
/*
Copyright 2019 Equinor 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 ESMRY_HPP
#define ESMRY_HPP
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
class ESmry
{
public:
explicit ESmry(const std::string& filename, bool loadBaseRunData=false); // filename (smspec file) or file root name
const int numberOfVectors() const { return nVect; }
bool hasKey(const std::string& key) const;
const std::vector<float>& get(const std::string& name) const;
const std::vector<std::string>& keywordList() const { return keyword; }
private:
int nVect, nI, nJ, nK;
std::string path="";
void ijk_from_global_index(int glob, int &i, int &j, int &k);
std::vector<std::vector<float>> param;
std::vector<std::string> keyword;
std::vector<int> seqIndex;
std::vector<float> seqTime;
void getRstString(const std::vector<std::string> &restartArray, std::string &path, std::string &rootN) const;
void updatePathAndRootName(std::string &path, std::string &rootN) const;
std::string makeKeyString(const std::string& keyword, const std::string& wgname, int num);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -16,95 +16,26 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECLFILE_HPP
#define ECLFILE_HPP
#include <opm/common/ErrorMacros.hpp>
#include <examples/test_util/data/EclIOdata.hpp>
//#include "EclIO.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <ctime>
#include <map>
#include <unordered_map>
#include <stdio.h>
#include <examples/test_util/data/EclIOdata.hpp>
namespace EIOD = Opm::ecl;
class EclFile
class EclFile
{
public:
explicit EclFile(const std::string& filename);
bool formattedInput() { return formatted; }
};
void loadData(); // load all data
void loadData(const std::string& arrName); // load all arrays with array name equal to arrName
void loadData(int arrIndex); // load data based on array indices in vector arrIndex
void loadData(const std::vector<int>& arrIndex); // load data based on array indices in vector arrIndex
void clearData()
{
inte_array.clear();
real_array.clear();
doub_array.clear();
logi_array.clear();
char_array.clear();
}
using EclEntry = std::tuple<std::string, EIOD::eclArrType, int>;
std::vector<EclEntry> getList() const;
template <typename T>
const std::vector<T>& get(int arrIndex);
template <typename T>
const std::vector<T>& get(const std::string& name);
bool hasKey(const std::string &name) const;
const std::vector<std::string>& arrayNames() const { return array_name; }
protected:
bool formatted;
std::string inputFilename;
std::unordered_map<int, std::vector<int>> inte_array;
std::unordered_map<int, std::vector<bool>> logi_array;
std::unordered_map<int, std::vector<double>> doub_array;
std::unordered_map<int, std::vector<float>> real_array;
std::unordered_map<int, std::vector<std::string>> char_array;
std::vector<std::string> array_name;
std::vector<EIOD::eclArrType> array_type;
std::vector<int> array_size;
std::vector<unsigned long int> ifStreamPos;
std::map<std::string, int> array_index;
template<class T>
const std::vector<T>& getImpl(int arrIndex, EIOD::eclArrType type,
const std::unordered_map<int, std::vector<T>>& array,
const std::string& typeStr)
{
if (array_type[arrIndex] != type) {
std::string message = "Array with index " + std::to_string(arrIndex) + " is not of type " + typeStr;
OPM_THROW(std::runtime_error, message);
}
if (!arrayLoaded[arrIndex]) {
loadData(arrIndex);
}
return array.find(arrIndex)->second;
}
private:
std::vector<bool> arrayLoaded;
void loadArray(std::fstream& fileH, int arrIndex);
};
#endif

View File

@@ -18,6 +18,7 @@
#include "EclFilesComparator.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <opm/common/utility/numeric/calculateCellVol.hpp>
#include <stdio.h>
@@ -27,8 +28,13 @@
#include <algorithm>
#include <cmath>
#include <numeric>
#include <vector>
#include <type_traits>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
@@ -42,70 +48,203 @@
}
template <typename T>
void ECLFilesComparator::printValuesForCell(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const T& value1, const T& value2) const {
namespace {
/*
This is just a basic survival test; we verify that the ERT well
loader which is used in Resinsight can load the well description
from the restart file.
*/
int nActive = -1;
int nTot = -1;
if (grid) {
nActive = grid->activeCells();
nTot = grid->totalNumberOfCells();
void loadWells( const ecl_grid_type * grid , ecl_file_type * rst_file ) {
well_info_type * well_info = well_info_alloc( grid );
well_info_add_UNRST_wells2( well_info , ecl_file_get_global_view( rst_file ), true );
well_info_free( well_info );
}
if (static_cast<int>(kw_size) == nActive) {
auto ijk = grid->ijk_from_active_index(cell);
}
ijk[0]++, ijk[1]++, ijk[2]++;
void ECLFilesComparator::keywordValidForComparing(const std::string& keyword) const {
auto it = std::find(keywords1.begin(), keywords1.end(), keyword);
if (it == keywords1.end()) {
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in first file.");
}
it = find(keywords2.begin(), keywords2.end(), keyword);
if (it == keywords2.end()) {
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in second file.");
}
}
unsigned int ECLFilesComparator::getEclKeywordData(ecl_kw_type*& ecl_kw1, ecl_kw_type*& ecl_kw2, const std::string& keyword, int occurrence1, int occurrence2) const {
ecl_kw1 = ecl_file_iget_named_kw(ecl_file1, keyword.c_str(), occurrence1);
ecl_kw2 = ecl_file_iget_named_kw(ecl_file2, keyword.c_str(), occurrence2);
const unsigned int numCells1 = ecl_kw_get_size(ecl_kw1);
const unsigned int numCells2 = ecl_kw_get_size(ecl_kw2);
if (numCells1 != numCells2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nOccurrence in first file " << occurrence1
<< "\nOccurrence in second file " << occurrence2
<< "\nCells in first file: " << numCells1
<< "\nCells in second file: " << numCells2
<< "\nThe number of cells differ.");
}
return numCells1;
}
template <typename T>
void ECLFilesComparator::printValuesForCell(const std::string& /*keyword*/, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const T& value1, const T& value2) const {
if (kw_size == static_cast<size_t>(ecl_grid_get_active_size(ecl_grid1))) {
int i, j, k;
ecl_grid_get_ijk1A(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
std::cout << std::endl
<< "\nKeyword: " << keyword << ", origin " << reference << "\n"
<< "Global index (zero based) = " << cell << "\n"
<< "Grid coordinate = (" << ijk[0] << ", " << ijk[1] << ", " << ijk[2] << ")" << "\n"
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Value index = " << cell << "\n"
<< "Grid coordinate = (" << i << ", " << j << ", " << k << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
return;
}
if (static_cast<int>(kw_size) == nTot) {
auto ijk = grid->ijk_from_global_index(cell);
ijk[0]++, ijk[1]++, ijk[2]++;
if (kw_size == static_cast<size_t>(ecl_grid_get_global_size(ecl_grid1))) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
std::cout << std::endl
<< "\nKeyword: " << keyword << ", origin " << reference << "\n\n"
<< "Global index (zero based) = " << cell << "\n"
<< "Grid coordinate = (" << ijk[0] << ", " << ijk[1] << ", " << ijk[2] << ")" << "\n"
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Value index = " << cell << "\n"
<< "Grid coordinate = (" << i << ", " << j << ", " << k << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
return;
}
std::cout << std::endl
<< "\nKeyword: " << keyword << ", origin " << reference << "\n\n"
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Value index = " << cell << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
}
template void ECLFilesComparator::printValuesForCell<bool> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const bool& value1, const bool& value2) const;
template void ECLFilesComparator::printValuesForCell<int> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const int& value1, const int& value2) const;
template void ECLFilesComparator::printValuesForCell<double> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const double& value1, const double& value2) const;
template void ECLFilesComparator::printValuesForCell<std::string>(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const std::string& value1, const std::string& value2) const;
// Hack to work around case where std::vector<bool>::const_reference is not a bool. If it is we will initialize printValuesForCell<char> otherwise printValuesForCell<std::vector<bool>::const_reference>
using boolConstReference = typename std::vector<bool>::const_reference;
using boolTypeHelper = typename std::remove_const<typename std::remove_reference<boolConstReference>::type>::type;
using boolType = typename std::conditional<std::is_same<boolTypeHelper, bool>::value, char, boolTypeHelper>::type;
template void ECLFilesComparator::printValuesForCell<boolType> (const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const boolType& value1, const boolType& value2) const;
template void ECLFilesComparator::printValuesForCell<bool> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const bool& value1, const bool& value2) const;
template void ECLFilesComparator::printValuesForCell<int> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const int& value1, const int& value2) const;
template void ECLFilesComparator::printValuesForCell<double> (const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const double& value1, const double& value2) const;
template void ECLFilesComparator::printValuesForCell<std::string>(const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const std::string& value1, const std::string& value2) const;
ECLFilesComparator::ECLFilesComparator(const std::string& basename1,
ECLFilesComparator::ECLFilesComparator(int file_type_arg, const std::string& basename1,
const std::string& basename2,
double absToleranceArg, double relToleranceArg) :
absTolerance(absToleranceArg), relTolerance(relToleranceArg) {
file_type(file_type_arg), absTolerance(absToleranceArg), relTolerance(relToleranceArg) {
rootName1 = basename1;
rootName2 = basename2;
std::string file1, file2;
if (file_type == ECL_UNIFIED_RESTART_FILE) {
file1 = basename1 + ".UNRST";
file2 = basename2 + ".UNRST";
}
else if (file_type == ECL_INIT_FILE) {
file1 = basename1 + ".INIT";
file2 = basename2 + ".INIT";
}
else if (file_type == ECL_RFT_FILE) {
file1 = basename1 + ".RFT";
file2 = basename2 + ".RFT";
}
else {
OPM_THROW(std::invalid_argument, "Unsupported filetype sent to ECLFilesComparator's constructor."
<< "Only unified restart (.UNRST), initial (.INIT) and .RFT files are supported.");
}
ecl_file1 = ecl_file_open(file1.c_str(), 0);
ecl_file2 = ecl_file_open(file2.c_str(), 0);
ecl_grid1 = ecl_grid_load_case(basename1.c_str());
ecl_grid2 = ecl_grid_load_case(basename2.c_str());
if (ecl_file1 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening first file: " << file1);
}
if (ecl_file2 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening second file: " << file2);
}
if (ecl_grid1 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening first grid file: " << basename1);
}
if (ecl_grid2 == nullptr) {
OPM_THROW(std::invalid_argument, "Error opening second grid file. " << basename2);
}
unsigned int numKeywords1 = ecl_file_get_num_distinct_kw(ecl_file1);
unsigned int numKeywords2 = ecl_file_get_num_distinct_kw(ecl_file2);
keywords1.reserve(numKeywords1);
keywords2.reserve(numKeywords2);
for (unsigned int i = 0; i < numKeywords1; ++i) {
std::string keyword(ecl_file_iget_distinct_kw(ecl_file1, i));
keywords1.push_back(keyword);
}
for (unsigned int i = 0; i < numKeywords2; ++i) {
std::string keyword(ecl_file_iget_distinct_kw(ecl_file2, i));
keywords2.push_back(keyword);
}
if (file_type == ECL_UNIFIED_RESTART_FILE) {
loadWells( ecl_grid1 , ecl_file1 );
loadWells( ecl_grid2 , ecl_file2 );
}
}
ECLFilesComparator::~ECLFilesComparator() {
ecl_file_close(ecl_file1);
ecl_file_close(ecl_file2);
ecl_grid_free(ecl_grid1);
ecl_grid_free(ecl_grid2);
}
void ECLFilesComparator::printKeywords() const {
std::cout << "\nKeywords in the first file:\n";
for (const auto& it : keywords1) {
std::cout << std::setw(15) << std::left << it << " of type " << ecl_type_get_name( ecl_file_iget_named_data_type(ecl_file1, it.c_str(), 0)) << std::endl;
}
std::cout << "\nKeywords in second file:\n";
for (const auto& it : keywords2) {
std::cout << std::setw(15) << std::left << it << " of type " << ecl_type_get_name( ecl_file_iget_named_data_type(ecl_file2, it.c_str(), 0)) << std::endl;
}
}
void ECLFilesComparator::printKeywordsDifference() const {
std::vector<std::string> common;
std::vector<std::string> uncommon;
const std::vector<std::string>* keywordsShort = &keywords1;
const std::vector<std::string>* keywordsLong = &keywords2;
if (keywords1.size() > keywords2.size()) {
keywordsLong = &keywords1;
keywordsShort = &keywords2;
}
for (const auto& it : *keywordsLong) {
const auto position = std::find(keywordsShort->begin(), keywordsShort->end(), it);
if (position != keywordsShort->end()) {
common.push_back(*position);
}
else {
uncommon.push_back(it);
}
}
std::cout << "\nCommon keywords for the two cases:\n";
for (const auto& it : common) std::cout << it << std::endl;
std::cout << "\nUncommon keywords for the two cases:\n";
for (const auto& it : uncommon) std::cout << it << std::endl;
}
Deviation ECLFilesComparator::calculateDeviations(double val1, double val2) {
val1 = std::abs(val1);
val2 = std::abs(val2);
@@ -120,15 +259,16 @@ Deviation ECLFilesComparator::calculateDeviations(double val1, double val2) {
}
double ECLFilesComparator::median(std::vector<double> vec) {
if (vec.empty()) {
return 0;
}
else {
size_t n = vec.size() / 2;
size_t n = vec.size()/2;
nth_element(vec.begin(), vec.begin() + n, vec.end());
if (vec.size() % 2 == 0) {
return 0.5 * (vec[n-1] + vec[n]);
return 0.5*(vec[n-1]+vec[n]);
}
else {
return vec[n];
@@ -137,6 +277,7 @@ double ECLFilesComparator::median(std::vector<double> vec) {
}
double ECLFilesComparator::average(const std::vector<double>& vec) {
if (vec.empty()) {
return 0;
@@ -146,3 +287,14 @@ double ECLFilesComparator::average(const std::vector<double>& vec) {
}
double ECLFilesComparator::getCellVolume(const ecl_grid_type* ecl_grid,
const int globalIndex) {
std::vector<double> x(8, 0.0);
std::vector<double> y(8, 0.0);
std::vector<double> z(8, 0.0);
for (int i = 0; i < 8; i++) {
ecl_grid_get_cell_corner_xyz1(ecl_grid, globalIndex, i, &x.data()[i], &y.data()[i], &z.data()[i]);
}
return calculateCellVol(x,y,z);
}

View File

@@ -25,61 +25,103 @@
#include <vector>
#include <string>
#include <examples/test_util/EGrid.hpp>
struct ecl_file_struct; //!< Prototype for eclipse file struct, from ERT library.
typedef struct ecl_file_struct ecl_file_type;
struct ecl_grid_struct; //!< Prototype for eclipse grid struct, from ERT library.
typedef struct ecl_grid_struct ecl_grid_type;
struct ecl_kw_struct; //!< Prototype for eclipse keyword struct, from ERT library.
typedef struct ecl_kw_struct ecl_kw_type;
/*! \brief A class for comparing ECLIPSE files.
\details ECLFilesComparator opens ECLIPSE files
(unified restart, initial and RFT in addition to grid file)
from two simulations. This class has only the functions
printKeywords() and printKeywordsDifference(), in addition to a
couple of get-functions: the comparison logic is implemented in
the subclasses RegressionTest and IntegrationTest. */
class ECLFilesComparator {
public:
ECLFilesComparator(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance);
private:
int file_type;
double absTolerance = 0;
double relTolerance = 0;
protected:
ecl_file_type* ecl_file1 = nullptr;
ecl_grid_type* ecl_grid1 = nullptr;
ecl_file_type* ecl_file2 = nullptr;
ecl_grid_type* ecl_grid2 = nullptr;
std::vector<std::string> keywords1, keywords2;
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform full error analysis
std::map<std::string, std::vector<Deviation>> deviations;
mutable size_t num_errors = 0;
void throwOnErrors(bool dothrow) {
throwOnError = dothrow;
}
//! \brief Checks if the keyword exists in both cases.
//! \param[in] keyword Keyword to check.
//! \details If the keyword does not exist in one of the cases, the function throws an exception.
void keywordValidForComparing(const std::string& keyword) const;
//! \brief Stores keyword data for a given occurrence
//! \param[out] ecl_kw1 Pointer to a ecl_kw_type, which stores keyword data for first case given the occurrence.
//! \param[out] ecl_kw2 Pointer to a ecl_kw_type, which stores keyword data for second case given the occurrence.
//! \param[in] keyword Which keyword to consider.
//! \param[in] occurrence Which keyword occurrence to consider.
//! \details This function stores keyword data for the given keyword and occurrence in #ecl_kw1 and #ecl_kw2, and returns the number of cells (for which the keyword has a value at the occurrence). If the number of cells differ for the two cases, an exception is thrown.
unsigned int getEclKeywordData(ecl_kw_type*& ecl_kw1, ecl_kw_type*& ecl_kw2, const std::string& keyword, int occurrence1, int occurrence2) const;
//! \brief Prints values for a given keyword, occurrence and cell
//! \param[in] keyword Which keyword to consider.
//! \param[in] occurrence Which keyword occurrence to consider.
//! \param[in] cell Which cell occurrence to consider (numbered by global index).
//! \param[in] value1 Value for first file, the data type can be bool, int, double or std::string.
//! \param[in] value2 Value for second file, the data type can be bool, int, double or std::string.
//! \details Templatefunction for printing values when exceptions are thrown. The function is defined for bool, int, double and std::string.
template <typename T>
void printValuesForCell(const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, const T& value1, const T& value2) const;
void doAnalysis(bool analize) {
analysis = analize;
}
public:
//! \brief Open ECLIPSE files and set tolerances and keywords.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details The content of the ECLIPSE files specified in the input is stored in the ecl_file_type and ecl_grid_type member variables. In addition the keywords and absolute and relative tolerances (member variables) are set. If the constructor is unable to open one of the ECLIPSE files, an exception will be thrown.
ECLFilesComparator(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Closing the ECLIPSE files.
~ECLFilesComparator();
size_t getNoErrors() const {
return num_errors;
}
//! \brief Set whether to throw on errors or not.
void throwOnErrors(bool dothrow) { throwOnError = dothrow; }
//! \brief Returns the absolute tolerance stored as a private member variable in the class
double getAbsTolerance() const {
return absTolerance;
}
//! \brief Returns the relative tolerance stored as a private member variable in the class
double getRelTolerance() const {
return relTolerance;
}
//! \brief Set whether to perform a full error analysis.
void doAnalysis(bool analize) { analysis = analize; }
//! \brief Calculate deviations for two values.
//! \details Using absolute values of the input arguments: If one of the values are non-zero, the Deviation::abs returned is the difference between the two input values. In addition, if both values are non-zero, the Deviation::rel returned is the absolute deviation divided by the largest value.
static Deviation calculateDeviations(double val1, double val2);
//! \brief Calculate median of a vector.
//! \details Returning the median of the input vector, i.e. the middle value of the sorted vector if the number of elements is odd or the mean of the two middle values if the number of elements are even. Copy is intentional.
static double median(std::vector<double> vec);
//! \brief Calculate average of a vector.
//! \details Returning the average of the input vector, i.e. the sum of all values divided by the number of elements.
static double average(const std::vector<double>& vec);
//! \brief Returns the number of errors encountered in the performed comparisons.
size_t getNoErrors() const { return num_errors; }
protected:
std::vector<std::string> keywords1, keywords2;
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform full error analysis
std::map<std::string, std::vector<Deviation>> deviations;
mutable size_t num_errors = 0;
//! \brief Returns the ECLIPSE filetype of this
int getFileType() const {return file_type;}
//! \brief Returns the absolute tolerance stored as a private member variable in the class
double getAbsTolerance() const {return absTolerance;}
//! \brief Returns the relative tolerance stored as a private member variable in the class
double getRelTolerance() const {return relTolerance;}
std::string rootName1, rootName2;
//! \brief Print all keywords and their respective Eclipse type for the two input cases.
void printKeywords() const;
//! \brief Print common and uncommon keywords for the two input cases.
void printKeywordsDifference() const;
template <typename T>
void printValuesForCell(const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, EGrid *grid, const T& value1, const T& value2) const;
private:
double absTolerance = 0;
double relTolerance = 0;
//! \brief Calculate deviations for two values.
//! \details Using absolute values of the input arguments: If one of the values are non-zero, the Deviation::abs returned is the difference between the two input values. In addition, if both values are non-zero, the Deviation::rel returned is the absolute deviation divided by the largest value.
static Deviation calculateDeviations(double val1, double val2);
//! \brief Calculate median of a vector.
//! \details Returning the median of the input vector, i.e. the middle value of the sorted vector if the number of elements is odd or the mean of the two middle values if the number of elements are even.
static double median(std::vector<double> vec);
//! \brief Calculate average of a vector.
//! \details Returning the average of the input vector, i.e. the sum of all values divided by the number of elements.
static double average(const std::vector<double>& vec);
//! \brief Obtain the volume of a cell.
static double getCellVolume(const ecl_grid_type* ecl_grid, const int globalIndex);
};
#endif

View File

@@ -0,0 +1,199 @@
/*
Copyright 2016 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 "EclIntegrationTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <set>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else { \
std::cerr << message << std::endl; \
++num_errors; \
} \
}
void ECLIntegrationTest::setCellVolumes() {
double absTolerance = getAbsTolerance();
double relTolerance = getRelTolerance();
const unsigned int globalGridCount1 = ecl_grid_get_global_size(ecl_grid1);
const unsigned int activeGridCount1 = ecl_grid_get_active_size(ecl_grid1);
const unsigned int globalGridCount2 = ecl_grid_get_global_size(ecl_grid2);
const unsigned int activeGridCount2 = ecl_grid_get_active_size(ecl_grid2);
if (globalGridCount1 != globalGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << globalGridCount1
<< "\nCells in second file: " << globalGridCount2
<< "\nThe number of global cells differ.");
}
if (activeGridCount1 != activeGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << activeGridCount1
<< "\nCells in second file: " << activeGridCount2
<< "\nThe number of active cells differ.");
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
OPM_THROW(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with coordinate (" << i << ", " << j << ", " << k << "):"
<< "\nCell volume in first file: " << cellVolume1
<< "\nCell volume in second file: " << cellVolume2
<< "\nThe absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << "."
<< "\nThe relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".");
} // The second input case is used as reference.
cellVolumes.push_back(cellVolume2);
}
}
void ECLIntegrationTest::initialOccurrenceCompare(const std::string& keyword) {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, 0, 0);
std::vector<double> values1(numCells);
initialCellValues.resize(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, initialCellValues.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the keyword value for the first case for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedTotal = 0;
for (size_t cell = 0; cell < initialCellValues.size(); ++cell) {
weightedTotal += initialCellValues[cell]*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
}
if (weightedTotal != 0) {
double ratioValue = weightedDifference/weightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence 0:"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
void ECLIntegrationTest::occurrenceCompare(const std::string& keyword, int occurrence) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence, occurrence);
std::vector<double> values1(numCells), values2(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, values2.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the difference between the keyword value for the occurrence and the initial keyword value for each cell. The sum is weighted with respect to the cell volume of each cell.
double relativeWeightedTotal = 0;
for (size_t cell = 0; cell < values1.size(); ++cell) {
relativeWeightedTotal += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - values2[cell])*cellVolumes[cell];
}
if (relativeWeightedTotal != 0) {
double ratioValue = weightedDifference/relativeWeightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence " << occurrence << ":"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
ECLIntegrationTest::ECLIntegrationTest(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance) :
ECLFilesComparator(ECL_UNIFIED_RESTART_FILE, basename1, basename2, absTolerance, relTolerance) {
std::cout << "\nUsing cell volumes and keyword values from case " << basename2
<< " as reference." << std::endl << std::endl;
setCellVolumes();
}
bool ECLIntegrationTest::elementInWhitelist(const std::string& keyword) const {
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
return it != keywordWhitelist.end();
}
void ECLIntegrationTest::equalNumKeywords() const {
if (keywords1.size() != keywords2.size()) {
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
}
}
void ECLIntegrationTest::results() {
for (const auto& it : keywordWhitelist)
resultsForKeyword(it);
}
void ECLIntegrationTest::resultsForKeyword(const std::string& keyword) {
std::cout << "Comparing " << keyword << "...";
keywordValidForComparing(keyword);
const unsigned int occurrences1 = ecl_file_get_num_named_kw(ecl_file1, keyword.c_str());
const unsigned int occurrences2 = ecl_file_get_num_named_kw(ecl_file2, keyword.c_str());
if (occurrences1 != occurrences2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nKeyword occurrences in first file: " << occurrences1
<< "\nKeyword occurrences in second file: " << occurrences2
<< "\nThe number of occurrences differ.");
}
initialOccurrenceCompare(keyword);
for (unsigned int occurrence = 1; occurrence < occurrences1; ++occurrence) {
occurrenceCompare(keyword, occurrence);
}
std::cout << "done." << std::endl;
}

View File

@@ -0,0 +1,78 @@
/*
Copyright 2016 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 ECLINTEGRATIONTEST_HPP
#define ECLINTEGRATIONTEST_HPP
#include "EclFilesComparator.hpp"
/*! \brief A class for executing a integration test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and closes
the input cases and stores keywordnames. The three public functions
equalNumKeywords(), results() and resultsForKeyword() can be invoked
to compare griddata or keyworddata for all keywords or a given
keyword (resultsForKeyword()).
*/
class ECLIntegrationTest: public ECLFilesComparator {
private:
std::vector<double> cellVolumes; //!< Vector of cell volumes in second input case (indexed by global index)
std::vector<double> initialCellValues; //!< Keyword values for all cells at first occurrence (index by global index)
// These are the only keywords which are compared, since SWAT should be "1 - SOIL - SGAS", this keyword is omitted.
const std::vector<std::string> keywordWhitelist = {"SGAS", "SWAT", "PRESSURE"};
void setCellVolumes();
void initialOccurrenceCompare(const std::string& keyword);
void occurrenceCompare(const std::string& keyword, int occurrence) const;
public:
//! \brief Sets up the integration test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor calls the constructor of the superclass, with input filetype unified restart. See the docs for ECLFilesComparator for more information.
ECLIntegrationTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Checks if a keyword is supported for comparison.
//! \param[in] keyword Keyword to check.
bool elementInWhitelist(const std::string& keyword) const;
//! \brief Checks if the number of keywords equal in the two input cases.
//! \param[in] keyword Keyword to check.
void equalNumKeywords() const;
//! \brief Finds deviations for all supported keywords.
//! \details results() loops through all supported keywords for integration test (defined in keywordWhitelist -- this is SGAS, SWAT and PRESSURE) and calls resultsForKeyword() for each keyword.
void results();
//! \brief Finds deviations for a specific keyword.
//! \param[in] keyword Keyword to check.
/*! \details First, resultsForKeyword() checks if the keyword exits in both cases, and if the number of keyword occurrences in the two cases differ. If these tests fail, an exception is thrown. Then deviaitons are calculated as described below for each occurrence, and an exception is thrown if the relative error ratio \f$E\f$ is larger than the relative tolerance.
* Calculation:\n
* Let the keyword values for occurrence \f$n\f$ and cell \f$i\f$ be \f$p_{n,i}\f$ and \f$q_{n,i}\f$ for input case 1 and 2, respectively.
* Consider first the initial occurrence (\f$n=0\f$). The function uses the second cases as reference, and calculates the volume weighted sum of \f$q_{0,i}\f$ over all cells \f$i\f$:
* \f[ S_0 = \sum_{i} q_{0,i} v_i \f]
* where \f$v_{i}\f$ is the volume of cell \f$i\f$ in case 2. Then, the deviations between the cases for each cell are calculated:
* \f[ \Delta = \sum_{i} |p_{0,i} - q_{0,i}| v_i.\f]
* The error ratio is then \f$E = \Delta/S_0\f$.\n
* For all other occurrences \f$n\f$, the deviation value \f$\Delta\f$ is calculated the same way, but the total value \f$S\f$ is calculated relative to the initial occurrence total \f$S_0\f$:
* \f[ S = \sum_{i} |q_{n,i} - q_{0,i}| v_i. \f]
* The error ratio is \f$ E = \Delta/S\f$. */
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -1,397 +0,0 @@
/*
Copyright 2019 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 "EclOutput.hpp"
#include "EclUtil.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <stdexcept>
#include <typeinfo>
#include <sstream>
#include <stdio.h>
EclOutput::EclOutput(const std::string& inputFile, bool formatted) :
isFormatted(formatted)
{
ofileH.open(inputFile, isFormatted ? std::ios::out : std::ios::out | std::ios::binary);
}
template<>
void EclOutput::write<std::string>(const std::string& name,
const std::vector<std::string>& data)
{
if (isFormatted)
{
writeFormattedHeader(name, data.size(), EIOD::CHAR);
writeFormattedCharArray(data);
}
else
{
writeBinaryHeader(name, data.size(), EIOD::CHAR);
writeBinaryCharArray(data);
}
}
void EclOutput::writeBinaryHeader(const std::string&arrName, int size, EIOD::eclArrType arrType)
{
std::string name = arrName + std::string(8 - arrName.size(),' ');
int flippedSize = EIOD::flipEndianInt(size);
int bhead = EIOD::flipEndianInt(16);
ofileH.write(reinterpret_cast<char*>(&bhead), sizeof(bhead));
ofileH.write(name.c_str(), 8);
ofileH.write(reinterpret_cast<char*>(&flippedSize), sizeof(flippedSize));
switch(arrType) {
case EIOD::INTE:
ofileH.write("INTE", 4);
break;
case EIOD::REAL:
ofileH.write("REAL", 4);
break;
case EIOD::DOUB:
ofileH.write("DOUB", 4);
break;
case EIOD::LOGI:
ofileH.write("LOGI", 4);
break;
case EIOD::CHAR:
ofileH.write("CHAR", 4);
break;
case EIOD::MESS:
ofileH.write("MESS", 4);
break;
}
ofileH.write(reinterpret_cast<char *>(&bhead), sizeof(bhead));
}
template <typename T>
void EclOutput::writeBinaryArray(const std::vector<T>& data)
{
int rest,num,rval;
int dhead;
float value_f;
double value_d;
int intVal;
int n = 0;
int size = data.size();
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(std::vector<T>) == typeid(std::vector<int>)) {
arrType = EIOD::INTE;
} else if (typeid(std::vector<T>) == typeid(std::vector<float>)) {
arrType = EIOD::REAL;
} else if (typeid(std::vector<T>) == typeid(std::vector<double>)) {
arrType = EIOD::DOUB;
} else if (typeid(std::vector<T>) == typeid(std::vector<bool>)) {
arrType = EIOD::LOGI;
}
auto sizeData = block_size_data_binary(arrType);
int sizeOfElement = std::get<0>(sizeData);
int maxBlockSize = std::get<1>(sizeData);
int maxNumberOfElements = maxBlockSize / sizeOfElement;
if (!ofileH.is_open()) {
OPM_THROW(std::runtime_error, "fstream fileH not open for writing");
}
rest = size * sizeOfElement;
while (rest > 0) {
if (rest > maxBlockSize) {
rest -= maxBlockSize;
num = maxNumberOfElements;
} else {
num = rest / sizeOfElement;
rest = 0;
}
dhead = EIOD::flipEndianInt(num * sizeOfElement);
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
for (int i = 0; i < num; i++) {
if (arrType == EIOD::INTE) {
rval = EIOD::flipEndianInt(data[n]);
ofileH.write(reinterpret_cast<char*>(&rval), sizeof(rval));
} else if (arrType == EIOD::REAL) {
value_f = EIOD::flipEndianFloat(data[n]);
ofileH.write(reinterpret_cast<char*>(&value_f), sizeof(value_f));
} else if (arrType == EIOD::DOUB) {
value_d = EIOD::flipEndianDouble(data[n]);
ofileH.write(reinterpret_cast<char*>(&value_d), sizeof(value_d));
} else if (arrType == EIOD::LOGI) {
intVal = data[n] ? EIOD::true_value : EIOD::false_value;
ofileH.write(reinterpret_cast<char*>(&intVal), sizeOfElement);
} else {
std::cout << "type not supported in write binaryarray" << std::endl;
exit(1);
}
n++;
}
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
}
}
template void EclOutput::writeBinaryArray<int>(const std::vector<int>& data);
template void EclOutput::writeBinaryArray<float>(const std::vector<float>& data);
template void EclOutput::writeBinaryArray<double>(const std::vector<double>& data);
template void EclOutput::writeBinaryArray<bool>(const std::vector<bool>& data);
template void EclOutput::writeBinaryArray<char>(const std::vector<char>& data);
void EclOutput::writeBinaryCharArray(const std::vector<std::string>& data)
{
int num,dhead;
int n = 0;
int size = data.size();
auto sizeData = EIOD::block_size_data_binary(EIOD::CHAR);
int sizeOfElement = std::get<0>(sizeData);
int maxBlockSize = std::get<1>(sizeData);
int maxNumberOfElements = maxBlockSize / sizeOfElement;
int rest = size * sizeOfElement;
if (!ofileH.is_open()) {
OPM_THROW(std::runtime_error,"fstream fileH not open for writing");
}
while (rest > 0) {
if (rest > maxBlockSize) {
rest -= maxBlockSize;
num = maxNumberOfElements;
} else {
num = rest / sizeOfElement;
rest = 0;
}
dhead = EIOD::flipEndianInt(num * sizeOfElement);
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
for (int i = 0; i < num; i++) {
std::string tmpStr = data[n] + std::string(8 - data[n].size(),' ');
ofileH.write(tmpStr.c_str(), sizeOfElement);
n++;
}
ofileH.write(reinterpret_cast<char*>(&dhead), sizeof(dhead));
}
}
void EclOutput::writeFormattedHeader(const std::string& arrName, int size, EIOD::eclArrType arrType)
{
std::string name = arrName + std::string(8 - arrName.size(),' ');
ofileH << " '" << name << "' " << std::setw(11) << size;
switch (arrType) {
case EIOD::INTE:
ofileH << " 'INTE'" << std::endl;
break;
case EIOD::REAL:
ofileH << " 'REAL'" << std::endl;
break;
case EIOD::DOUB:
ofileH << " 'DOUB'" << std::endl;
break;
case EIOD::LOGI:
ofileH << " 'LOGI'" << std::endl;
break;
case EIOD::CHAR:
ofileH << " 'CHAR'" << std::endl;
break;
case EIOD::MESS:
ofileH << " 'MESS'" << std::endl;
break;
}
}
std::string EclOutput::make_real_string(float value) const
{
char buffer [15];
sprintf (buffer, "%10.7E", value);
if (value == 0.0) {
return "0.00000000E+00";
} else {
std::string tmpstr(buffer);
int exp = value < 0.0 ? std::stoi(tmpstr.substr(11, 3)) : std::stoi(tmpstr.substr(10, 3));
if (value < 0.0) {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 7) + "E";
} else {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 7) +"E";
}
sprintf (buffer, "%+03i", exp+1);
tmpstr = tmpstr+buffer;
return tmpstr;
}
}
std::string EclOutput::make_doub_string(double value) const
{
char buffer [21];
sprintf (buffer, "%19.13E", value);
if (value == 0.0) {
return "0.00000000000000D+00";
} else {
std::string tmpstr(buffer);
int exp = value < 0.0 ? std::stoi(tmpstr.substr(17, 4)) : std::stoi(tmpstr.substr(16, 4));
if (value < 0.0) {
if (abs(exp) < 100) {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 13) + "D";
} else {
tmpstr = "-0." + tmpstr.substr(1, 1) + tmpstr.substr(3, 13);
}
} else {
if (abs(exp) < 100) {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 13) + "D";
} else {
tmpstr = "0." + tmpstr.substr(0, 1) + tmpstr.substr(2, 13);
}
}
sprintf (buffer, "%+03i", exp+1);
tmpstr = tmpstr + buffer;
return tmpstr;
}
}
template <typename T>
void EclOutput::writeFormattedArray(const std::vector<T>& data)
{
int size = data.size();
int n = 0;
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(T) == typeid(int)) {
arrType = EIOD::INTE;
} else if (typeid(T) == typeid(float)) {
arrType = EIOD::REAL;
} else if (typeid(T) == typeid(double)) {
arrType = EIOD::DOUB;
} else if (typeid(T) == typeid(bool)) {
arrType = EIOD::LOGI;
}
auto sizeData = EIOD::block_size_data_formatted(arrType);
int maxBlockSize = std::get<0>(sizeData);
int nColumns = std::get<1>(sizeData);
int columnWidth = std::get<2>(sizeData);
for (int i = 0; i < size; i++) {
n++;
switch (arrType) {
case EIOD::INTE:
ofileH << std::setw(columnWidth) << data[i];
break;
case EIOD::REAL:
ofileH << std::setw(columnWidth) << make_real_string(data[i]);
break;
case EIOD::DOUB:
ofileH << std::setw(columnWidth) << make_doub_string(data[i]);
break;
case EIOD::LOGI:
if (data[i]) {
ofileH << " T";
} else {
ofileH << " F";
}
break;
default:
break;
}
if ((n % nColumns) == 0 || (n % maxBlockSize) == 0) {
ofileH << std::endl;
}
if ((n % maxBlockSize) == 0) {
n=0;
}
}
if ((n % nColumns) != 0 && (n % maxBlockSize) != 0) {
ofileH << std::endl;
}
}
template void EclOutput::writeFormattedArray<int>(const std::vector<int>& data);
template void EclOutput::writeFormattedArray<float>(const std::vector<float>& data);
template void EclOutput::writeFormattedArray<double>(const std::vector<double>& data);
template void EclOutput::writeFormattedArray<bool>(const std::vector<bool>& data);
template void EclOutput::writeFormattedArray<char>(const std::vector<char>& data);
void EclOutput::writeFormattedCharArray(const std::vector<std::string>& data)
{
auto sizeData = EIOD::block_size_data_formatted(EIOD::CHAR);
int nColumns = std::get<1>(sizeData);
int size = data.size();
for (int i = 0; i < size; i++) {
std::string str1(8,' ');
str1 = data[i] + std::string(8 - data[i].size(),' ');
ofileH << " '" << str1 << "'";
if ((i+1) % nColumns == 0) {
ofileH << std::endl;
}
}
if ((size % nColumns) != 0) {
ofileH << std::endl;
}
}

View File

@@ -1,94 +0,0 @@
/*
Copyright 2019 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 ECL_OUTPUT_HPP
#define ECL_OUTPUT_HPP
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>
#include <typeinfo>
#include <examples/test_util/data/EclIOdata.hpp>
namespace EIOD = Opm::ecl;
class EclOutput
{
public:
EclOutput(const std::string& inputFile, bool formatted);
template<typename T>
void write(const std::string& name,
const std::vector<T>& data)
{
EIOD::eclArrType arrType = EIOD::MESS;
if (typeid(T) == typeid(int))
arrType = EIOD::INTE;
else if (typeid(T) == typeid(float))
arrType = EIOD::REAL;
else if (typeid(T) == typeid(double))
arrType = EIOD::DOUB;
else if (typeid(T) == typeid(bool))
arrType = EIOD::LOGI;
else if (typeid(T) == typeid(char))
arrType = EIOD::MESS;
if (isFormatted)
{
writeFormattedHeader(name, data.size(), arrType);
if (arrType != EIOD::MESS)
writeFormattedArray(data);
}
else
{
writeBinaryHeader(name, data.size(), arrType);
if (arrType != EIOD::MESS)
writeBinaryArray(data);
}
}
private:
void writeBinaryHeader(const std::string& arrName, int size, EIOD::eclArrType arrType);
template <typename T>
void writeBinaryArray(const std::vector<T>& data);
void writeBinaryCharArray(const std::vector<std::string>& data);
void writeFormattedHeader(const std::string& arrName, int size, EIOD::eclArrType arrType);
template <typename T>
void writeFormattedArray(const std::vector<T>& data);
void writeFormattedCharArray(const std::vector<std::string>& data);
std::string make_real_string(float value) const;
std::string make_doub_string(double value) const;
std::ofstream ofileH;
bool isFormatted;
};
template<>
void EclOutput::write<std::string>(const std::string& name,
const std::vector<std::string>& data);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 Equinor ASA.
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
@@ -21,11 +21,6 @@
#define ECLREGRESSIONTEST_HPP
#include "EclFilesComparator.hpp"
#include "data/EclIOdata.hpp"
namespace EIOD = Opm::ecl;
/*! \brief A class for executing a regression test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and
@@ -34,146 +29,65 @@ namespace EIOD = Opm::ecl;
resultsForKeyword() can be invoked to compare griddata
or keyworddata for all keywords or a given keyword (resultsForKeyword()).
*/
class ECLRegressionTest: public ECLFilesComparator {
public:
//! \brief Sets up the regression test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(basename1, basename2, absTolerance, relTolerance) {}
private:
// These vectors store absolute and relative deviations, respecively. Note that they are whiped clean for every new keyword comparison.
std::vector<double> absDeviation, relDeviation;
// Keywords which should not contain negative values, i.e. uses allowNegativeValues = false in deviationsForCell():
const std::vector<std::string> keywordDisallowNegatives = {"SGAS", "SWAT", "PRESSURE"};
~ECLRegressionTest();
// Only compare last occurrence
bool onlyLastOccurrence = false;
//! \brief Option to only compare last occurrence
void setOnlyLastReportNumber(bool onlyLastSequenceArg) {
this->onlyLastSequence = onlyLastSequenceArg;
}
// Accept extra keywords in the restart file of the 'new' simulation.
bool acceptExtraKeywords = false;
int countDev() { return deviations.size(); }
// Accept extra keywords: If this switch is set to true the comparison
// will ignore extra keywords which are only present
// in the new simulation.
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
void setAcceptExtraKeywords(bool acceptExtraKeywordsArg) {
this->acceptExtraKeywords = acceptExtraKeywordsArg;
}
// Function which compares data at specific occurrences and for a specific keyword type. The functions takes two occurrence inputs to also be able to
// compare keywords which are shifted relative to each other in the two files. This is for instance handy when running flow with restart from different timesteps,
// and comparing the last timestep from the two runs.
void boolComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void charComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void intComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const;
void doubleComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2);
// deviationsForCell throws an exception if both the absolute deviation AND the relative deviation
// are larger than absTolerance and relTolerance, respectively. In addition,
// if allowNegativeValues is passed as false, an exception will be thrown when the absolute value
// of a negative value exceeds absTolerance. If no exceptions are thrown, the absolute and relative deviations are added to absDeviation and relDeviation.
void deviationsForCell(double val1, double val2, const std::string& keyword, int occurrence1, int occurrence2, size_t kw_size, size_t cell, bool allowNegativeValues = true);
public:
//! \brief Sets up the regression test.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(file_type, basename1, basename2, absTolerance, relTolerance) {}
void setIntegrationTest(bool inregrationTestArg) {
this->integrationTest = inregrationTestArg;
}
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrenceArg) {this->onlyLastOccurrence = onlyLastOccurrenceArg;}
void setPrintKeywordOnly(bool printArg) {
this->printKeywordOnly = printArg;
}
// Accept extra keywords: If this switch is set to true the comparison
// of restart files will ignore extra keywords which are only present
// in the new simulation.
void setAcceptExtraKeywords(bool acceptExtraKeywordsArg) { this->acceptExtraKeywords = acceptExtraKeywordsArg; }
void compareSpesificKeyword(std::string keyword) {
this->spesificKeyword = keyword;
}
void compareSpesificRstReportStepNumber(int seqn) {
this->spesificSequence = seqn;
}
void setLoadBaseRunData(bool loadArg) {
this->loadBaseRunData = loadArg;
}
void loadGrids();
void printDeviationReport();
void gridCompare();
void results_rst();
void results_init();
void results_smry();
void results_rft();
private:
bool checkFileName(const std::string& rootName, const std::string& extension, std::string& filename);
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
void printComparisonForKeywordLists(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2) const;
void printComparisonForKeywordLists(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2,
const std::vector<EIOD::eclArrType>& arrayType1,
const std::vector<EIOD::eclArrType>& arrayType2) const;
void printMissingKeywords(const std::vector<std::string>& arrayList1,
const std::vector<std::string>& arrayList2) const;
void compareKeywords(const std::vector<std::string>& keywords1,
const std::vector<std::string>& keywords2,
const std::string& reference);
void checkSpesificKeyword(std::vector<std::string>& keywords1,
std::vector<std::string>& keywords2,
std::vector<EIOD::eclArrType>& arrayType1,
std::vector<EIOD::eclArrType>& arrayType2,
const std::string& reference);
template <typename T>
void compareVectors(const std::vector<T>& t1, const std::vector<T>& t2,
const std::string& keyword, const std::string& reference);
template <typename T>
void compareFloatingPointVectors(const std::vector<T>& t1, const std::vector<T> &t2,
const std::string& keyword, const std::string& reference);
// deviationsForCell throws an exception if both the absolute deviation AND the relative deviation
// are larger than absTolerance and relTolerance, respectively. In addition,
// if allowNegativeValues is passed as false, an exception will be thrown when the absolute value
// of a negative value exceeds absTolerance. If no exceptions are thrown, the absolute and relative deviations are added to absDeviation and relDeviation.
// void deviationsForCell(double val1, double val2, const std::string& keyword, const std::string reference, size_t kw_size, size_t cell, bool allowNegativeValues = true);
void deviationsForCell(double val1, double val2, const std::string& keyword,
const std::string& reference, size_t kw_size, size_t cell,
bool allowNegativeValues, bool useStrictTol);
template <typename T>
void deviationsForNonFloatingPoints(T val1, T val2, const std::string& keyword,
const std::string& reference,
size_t kw_size, size_t cell);
// These vectors store absolute and relative deviations, respecively. Note that they are whiped clean for every new keyword comparison.
std::vector<double> absDeviation, relDeviation;
// Keywords which should not contain negative values, i.e. uses allowNegativeValues = false in deviationsForCell():
const std::vector<std::string> keywordDisallowNegatives = {"SGAS", "SWAT", "PRESSURE"};
double strictAbsTol = 1e-6;
double strictRelTol = 1e-6;
// keywords that triggers strict tolerances
const std::vector<std::string> keywordsStrictTol = {"COORD", "ZCORN", "PORV", "DEPTH", "DX", "DY", "DZ", "PERMX", "PERMY", "PERMZ", "NTG",
"TRANX", "TRANY", "TRANZ", "TRANNNC", "SGRP", "SWEL", "SCON", "DOUBHEAD"
};
// Only compare last occurrence
bool onlyLastSequence = false;
bool integrationTest = false;
bool printKeywordOnly = false;
bool loadBaseRunData = false;
// spesific keyword to be compared
std::string spesificKeyword;
// spesific restart sequence to be compared
int spesificSequence = -1;
// Accept extra keywords in the restart file of the 'new' simulation.
bool acceptExtraKeywords = false;
EGrid* grid1 = nullptr;
EGrid* grid2 = nullptr;
//! \brief Compares grid properties of the two cases.
// gridCompare() checks if both the number of active and global cells in the two cases are the same. If they are, and volumecheck is true, all cells are looped over to calculate the cell volume deviation for the two cases. If the both the relative and absolute deviation exceeds the tolerances, an exception is thrown.
void gridCompare(const bool volumecheck) const;
//! \brief Calculates deviations for all keywords.
// This function checks if the number of keywords of the two cases are equal, and if it is, resultsForKeyword() is called for every keyword. If not, an exception is thrown.
void results();
//! \brief Calculates deviations for a specific keyword.
//! \param[in] keyword Keyword which should be compared, if this keyword is absent in one of the cases, an exception will be thrown.
//! \details This function loops through every report step and every cell and compares the values for the given keyword from the two input cases. If the absolute or relative deviation between the two values for each step exceeds both the absolute tolerance and the relative tolerance (stored in ECLFilesComparator), an exception is thrown. In addition, some keywords are marked for "disallow negative values" -- these are SGAS, SWAT and PRESSURE. An exception is thrown if a value of one of these keywords is both negative and has an absolute value larger than the absolute tolerance. If no exceptions are thrown, resultsForKeyword() uses the private member funtion printResultsForKeyword to print the average and median deviations.
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -1,127 +0,0 @@
/*
Copyright 2019 Equinor 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 "EclUtil.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <stdexcept>
namespace EIOD = Opm::ecl;
int Opm::ecl::flipEndianInt(int num)
{
unsigned int tmp = __builtin_bswap32(num);
return static_cast<int>(tmp);
}
float Opm::ecl::flipEndianFloat(float num)
{
float value = num;
char* floatToConvert = reinterpret_cast<char*>(&value);
std::reverse(floatToConvert, floatToConvert+4);
return value;
}
double Opm::ecl::flipEndianDouble(double num)
{
double value = num;
char* doubleToConvert = reinterpret_cast<char*>(&value);
std::reverse(doubleToConvert, doubleToConvert+8);
return value;
}
std::tuple<int, int> Opm::ecl::block_size_data_binary(eclArrType arrType)
{
using BlockSizeTuple = std::tuple<int, int>;
switch (arrType) {
case EIOD::INTE:
return BlockSizeTuple{EIOD::sizeOfInte, EIOD::MaxBlockSizeInte};
break;
case EIOD::REAL:
return BlockSizeTuple{EIOD::sizeOfReal, EIOD::MaxBlockSizeReal};
break;
case EIOD::DOUB:
return BlockSizeTuple{EIOD::sizeOfDoub, EIOD::MaxBlockSizeDoub};
break;
case EIOD::LOGI:
return BlockSizeTuple{EIOD::sizeOfLogi, EIOD::MaxBlockSizeLogi};
break;
case EIOD::CHAR:
return BlockSizeTuple{EIOD::sizeOfChar, EIOD::MaxBlockSizeChar};
break;
case EIOD::MESS:
OPM_THROW(std::invalid_argument, "Type 'MESS' have no associated data");
break;
default:
OPM_THROW(std::invalid_argument, "Unknown field type");
break;
}
}
std::tuple<int, int, int> Opm::ecl::block_size_data_formatted(EIOD::eclArrType arrType)
{
using BlockSizeTuple = std::tuple<int, int, int>;
switch (arrType) {
case EIOD::INTE:
return BlockSizeTuple{EIOD::MaxNumBlockInte, EIOD::numColumnsInte, EIOD::columnWidthInte};
break;
case EIOD::REAL:
return BlockSizeTuple{EIOD::MaxNumBlockReal,EIOD::numColumnsReal, EIOD::columnWidthReal};
break;
case EIOD::DOUB:
return BlockSizeTuple{EIOD::MaxNumBlockDoub,EIOD::numColumnsDoub, EIOD::columnWidthDoub};
break;
case EIOD::LOGI:
return BlockSizeTuple{EIOD::MaxNumBlockLogi,EIOD::numColumnsLogi, EIOD::columnWidthLogi};
break;
case EIOD::CHAR:
return BlockSizeTuple{EIOD::MaxNumBlockChar,EIOD::numColumnsChar, EIOD::columnWidthChar};
break;
case EIOD::MESS:
OPM_THROW(std::invalid_argument, "Type 'MESS' have no associated data") ;
break;
default:
OPM_THROW(std::invalid_argument, "Unknown field type");
break;
}
}
std::string Opm::ecl::trimr(const std::string &str1)
{
if (str1 == " ") {
return "";
} else {
int p = str1.find_last_not_of(" ");
return str1.substr(0,p+1);
}
}

View File

@@ -1,40 +0,0 @@
/*
Copyright 2019 Equinor 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 ECL_UTIL_HPP
#define ECL_UTIL_HPP
#include <string>
#include <tuple>
#include <examples/test_util/data/EclIOdata.hpp>
namespace Opm {
namespace ecl {
int flipEndianInt(int num);
float flipEndianFloat(float num);
double flipEndianDouble(double num);
std::tuple<int, int> block_size_data_binary(eclArrType arrType);
std::tuple<int, int, int> block_size_data_formatted(eclArrType arrType);
std::string trimr(const std::string &str1);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 Equinor ASA.
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
@@ -16,129 +16,221 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclIntegrationTest.hpp"
#include "EclRegressionTest.hpp"
#include "summaryIntegrationTest.hpp"
#include "summaryRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/util/util.h>
#include <ert/util/stringlist.h>
#include <ert/ecl/ecl_endian_flip.h>
#include <ert/ecl/ecl_file.h>
#include <iostream>
#include <string>
#include <getopt.h>
static void printHelp() {
std::cout << "\ncompareECL compares ECLIPSE files (restart (.RST), unified restart (.UNRST), initial (.INIT), summary (.SMRY), unified summary (.UNSMRY) or .RFT) and gridsizes (from .EGRID or .GRID file) from two simulations.\n"
<< "The program takes four arguments:\n\n"
<< "1. Case number 1 (full path without extension)\n"
<< "2. Case number 2 (full path without extension)\n"
<< "3. Absolute tolerance\n"
<< "4. Relative tolerance (between 0 and 1)\n\n"
<< "In addition, the program takes these options (which must be given before the arguments):\n\n"
<< "-a Run a full analysis of errors.\n"
<< "-h Print help and exit.\n"
<< "-i Execute integration test (regression test is default).\n"
<< " The integration test compares SGAS, SWAT and PRESSURE in unified restart files, and WOPR, WGPR, WWPR and WBHP (all wells) in summary file. \n"
<< "-k Specify specific keyword to compare (capitalized), for examples -k PRESSURE or -k WOPR:A-1H \n"
<< "-l Only do comparison for the last Report Step. This option is only valid for restart files.\n"
<< "-n Do not throw on errors.\n"
<< "-p Print keywords in both cases and exit.\n"
<< "-r compare a spesific report time step number in a restart file.\n"
<< "-t Specify ECLIPSE filetype to compare, (default behaviour is that all files are compared if found). Different possible arguments are:\n"
<< " -t UNRST \t Compare two unified restart files (.UNRST). This the default value, so it is the same as not passing option -t.\n"
<< " -t EGRID \t Compare two EGrid files (.EGRID).\n"
<< " -t INIT \t Compare two initial files (.INIT).\n"
<< " -t RFT \t Compare two RFT files (.RFT).\n"
<< " -t SMRY \t Compare two cases consistent of (unified) summary files.\n"
<< "-x Allow extra keywords in case number 2. These additional keywords (not found in case number1) will be ignored in the comparison.\n"
<< "\nExample usage of the program: \n\n"
<< "compareECL -k PRESSURE <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -t INIT -k PORO <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -i <path to first casefile> <path to second casefile> 0.01 1e-6\n\n"
<< "Exceptions are thrown (and hence program exits) when deviations are larger than the specified "
<< "tolerances, or when the number of cells does not match -- either in the grid file or for a "
<< "specific keyword. Information about the keyword, keyword occurrence (zero based) and cell "
<< "coordinate is printed when an exception is thrown. For more information about how the cases "
<< "are compared, see the documentation of the EclFilesComparator class.\n\n";
<< "The program takes four arguments:\n\n"
<< "1. Case number 1 (full path without extension)\n"
<< "2. Case number 2 (full path without extension)\n"
<< "3. Absolute tolerance\n"
<< "4. Relative tolerance (between 0 and 1)\n\n"
<< "In addition, the program takes these options (which must be given before the arguments):\n\n"
<< "-a Run a full analysis of errors.\n"
<< "-g Will print the vector with the greatest error ratio.\n"
<< "-h Print help and exit.\n"
<< "-i Execute integration test (regression test is default).\n"
<< " The integration test compares SGAS, SWAT and PRESSURE in unified restart files, so this option can not be used in combination with -t.\n"
<< "-I Same as -i, but throws an exception when the number of keywords in the two cases differ. Can not be used in combination with -t.\n"
<< "-k Specify specific keyword to compare (capitalized), for example -k PRESSURE.\n"
<< "-K Will not allow different amount of keywords in the two files. Throws an exception if the amount are different.\n"
<< "-l Only do comparison for the last occurrence. This option is only for the regression test, and can therefore not be used in combination with -i or -I.\n"
<< "-m mainVar. Will calculate the error ratio for one main variable. Valid input is WOPR, WWPR, WGPR or WBHP.\n"
<< "-n Do not throw on errors.\n"
<< "-p Print keywords in both cases and exit. Can not be used in combination with -P.\n"
<< "-P Print common and uncommon keywords in both cases and exit. Can not be used in combination with -p.\n"
<< "-R Will allow comparison between a restarted simulation and a normal simulation for summary regression tests. The files must end at the same time.\n"
<< "-s int Sets the number of spikes that are allowed for each keyword in summary integration tests.\n"
<< "-t Specify ECLIPSE filetype to compare (unified restart is default). Can not be used in combination with -i or -I. Different possible arguments are:\n"
<< " -t UNRST \t Compare two unified restart files (.UNRST). This the default value, so it is the same as not passing option -t.\n"
<< " -t INIT \t Compare two initial files (.INIT).\n"
<< " -t RFT \t Compare two RFT files (.RFT).\n"
<< " -t RST \t Compare two cases consisting of restart files (.Xnnnn).\n"
<< " -t SMRY \t Compare two cases consistent of (unified) summary files.\n"
<< " -t RST1 \t Compare two cases where the first case consists of restart files (.Xnnnn), and the second case consists of a unified restart file (.UNRST).\n"
<< " -t RST2 \t Compare two cases where the first case consists of a unified restart file (.UNRST), and the second case consists of restart files (.Xnnnn).\n"
<< " Note that when dealing with restart files (.Xnnnn), the program concatenates all of them into one unified restart file, which is used for comparison and stored in the same directory as the restart files.\n"
<< " This will overwrite any existing unified restart file in that directory.\n\n"
<< "-v For the rate keywords WOPR, WGPR, WWPR and WBHP. Calculates the error volume of the two summary files. This is printed to screen.\n"
<< "\nExample usage of the program: \n\n"
<< "compareECL -k PRESSURE <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -t INIT -k PORO <path to first casefile> <path to second casefile> 1e-3 1e-5\n"
<< "compareECL -i <path to first casefile> <path to second casefile> 0.01 1e-6\n\n"
<< "Exceptions are thrown (and hence program exits) when deviations are larger than the specified "
<< "tolerances, or when the number of cells does not match -- either in the grid file or for a "
<< "specific keyword. Information about the keyword, keyword occurrence (zero based) and cell "
<< "coordinate is printed when an exception is thrown. For more information about how the cases "
<< "are compared, see the documentation of the EclFilesComparator class.\n\n";
}
void splitBasename(const std::string& basename, std::string& path, std::string& filename) {
const size_t lastSlashIndex = basename.find_last_of("/\\");
path = basename.substr(0,lastSlashIndex);
filename = basename.substr(lastSlashIndex+1);
}
// Inspired by the ecl_pack application in the ERT library
void concatenateRestart(const std::string& basename) {
std::string inputPath, inputBase;
splitBasename(basename, inputPath, inputBase);
stringlist_type* inputFiles = stringlist_alloc_new();
const int numFiles = ecl_util_select_filelist(inputPath.c_str(), inputBase.c_str(), ECL_RESTART_FILE, false, inputFiles);
const char* target_file_name = ecl_util_alloc_filename(inputPath.c_str(), inputBase.c_str(), ECL_UNIFIED_RESTART_FILE, false, -1);
fortio_type* target = fortio_open_writer(target_file_name, false, ECL_ENDIAN_FLIP);
int dummy;
ecl_kw_type* seqnum_kw = ecl_kw_alloc_new("SEQNUM", 1, ECL_INT, &dummy);
int reportStep = 0;
for (int i = 0; i < numFiles; ++i) {
ecl_util_get_file_type(stringlist_iget(inputFiles, i), nullptr, &reportStep);
ecl_file_type* src_file = ecl_file_open(stringlist_iget(inputFiles, i), 0);
ecl_kw_iset_int(seqnum_kw, 0, reportStep);
ecl_kw_fwrite(seqnum_kw, target);
ecl_file_fwrite_fortio(src_file, target, 0);
ecl_file_close(src_file);
}
fortio_fclose(target);
stringlist_free(inputFiles);
}
//------------------------------------------------//
int main(int argc, char** argv) {
bool integrationTest = false;
bool onlyLastSequence = false;
bool printKeywords = false;
bool specificKeyword = false;
bool specificReportStepNumber = false;
bool specificFileType = false;
bool throwOnError = true;
bool restartFile = false;
bool acceptExtraKeywords = false;
bool analysis = false;
char* keyword = nullptr;
int c = 0;
int reportStepNumber = -1;
std::string fileTypeString;
// Restart is default
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE;
// RegressionTest is default
bool integrationTest = false;
bool allowDifferentAmount = true;
bool checkNumKeywords = false;
bool findGreatestErrorRatio = false;
bool findVolumeError = false;
bool onlyLastOccurrence = false;
bool printKeywords = false;
bool printKeywordsDifference = false;
bool restartFile = false;
bool specificKeyword = false;
bool specificFileType = false;
bool allowSpikes = false;
bool throwOnError = true;
bool throwTooGreatErrorRatio = true;
bool acceptExtraKeywords = false;
bool analysis = false;
bool volumecheck = true;
char* keyword = nullptr;
char* fileTypeCstr = nullptr;
const char* mainVariable = nullptr;
int c = 0;
int spikeLimit = -1;
while ((c = getopt(argc, argv, "hik:alnpt:Rr:x")) != -1) {
while ((c = getopt(argc, argv, "hiIk:alnpPt:VRgs:m:vKx")) != -1) {
switch (c) {
case 'a':
analysis = true;
break;
case 'h':
printHelp();
return 0;
case 'i':
integrationTest = true;
break;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 'l':
onlyLastSequence = true;
break;
case 'n':
throwOnError = false;
break;
case 'p':
printKeywords = true;
break;
case 'r':
specificReportStepNumber=true;
reportStepNumber = atoi(optarg);
break;
case 'R':
restartFile = true;
break;
case 't':
specificFileType = true;
fileTypeString=optarg;
break;
case 'x':
acceptExtraKeywords = true;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cerr << "Option " << optopt << " requires a keyword as argument, see manual (-h) for more information." << std::endl;
case 'a':
analysis = true;
break;
case 'g':
findGreatestErrorRatio = true;
throwTooGreatErrorRatio = false;
break;
case 'h':
printHelp();
return 0;
case 'i':
integrationTest = true;
break;
case 'I':
integrationTest = true;
checkNumKeywords = true;
break;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 'K':
allowDifferentAmount = false;
break;
case 'l':
onlyLastOccurrence = true;
break;
case 'm':
mainVariable = optarg;
break;
case 'n':
throwOnError = false;
break;
case 'p':
printKeywords = true;
break;
case 'P':
printKeywordsDifference = true;
break;
case 'R':
restartFile = true;
break;
case 's':
allowSpikes = true;
spikeLimit = atof(optarg);
break;
case 't':
specificFileType = true;
fileTypeCstr = optarg;
break;
case 'v':
findVolumeError = true;
break;
case 'V':
volumecheck = false;
break;
case 'x':
acceptExtraKeywords = true;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cerr << "Option " << optopt << " requires a keyword as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else if (optopt == 't') {
std::cerr << "Option t requires an ECLIPSE filetype as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else {
std::cerr << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
else if (optopt == 't') {
std::cerr << "Option t requires an ECLIPSE filetype as argument, see manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
else {
std::cerr << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if ((printKeywords && printKeywordsDifference) ||
(integrationTest && specificFileType) ||
(integrationTest && onlyLastOccurrence)) {
std::cerr << "Error: Options given which can not be combined. "
<< "Please see the manual (-h) for more information." << std::endl;
return EXIT_FAILURE;
}
if (argc != argOffset + 4) {
std::cerr << "Error: The number of options and arguments given is not correct. "
<< "Please run compareECL -h to see manual." << std::endl;
<< "Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
@@ -147,85 +239,143 @@ int main(int argc, char** argv) {
double absTolerance = strtod(argv[argOffset + 2], nullptr);
double relTolerance = strtod(argv[argOffset + 3], nullptr);
std::cout << "Comparing '" << basename1 << "' to '" << basename2 << "'." << std::endl;
try {
ECLRegressionTest comparator(basename1, basename2, absTolerance, relTolerance);
comparator.throwOnErrors(throwOnError);
comparator.doAnalysis(analysis);
comparator.setAcceptExtraKeywords(acceptExtraKeywords);
if (integrationTest) {
comparator.setIntegrationTest(true);
if (specificFileType) {
std::string fileTypeString(fileTypeCstr);
for (auto& ch: fileTypeString) ch = toupper(ch);
if (fileTypeString== "UNRST") {} //Do nothing
else if (fileTypeString == "RST") {
concatenateRestart(basename1);
concatenateRestart(basename2);
}
if (printKeywords) {
comparator.setPrintKeywordOnly(printKeywords);
else if (fileTypeString == "RST1") {
concatenateRestart(basename1);
}
if (onlyLastSequence) {
comparator.setOnlyLastReportNumber(true);
else if (fileTypeString == "RST2") {
concatenateRestart(basename2);
}
if (specificKeyword) {
comparator.compareSpesificKeyword(keyword);
else if (fileTypeString == "INIT") {
file_type = ECL_INIT_FILE;
}
if (specificReportStepNumber) {
comparator.compareSpesificRstReportStepNumber(reportStepNumber);
else if (fileTypeString == "RFT") {
file_type = ECL_RFT_FILE;
}
if (restartFile) {
comparator.setLoadBaseRunData(true);
else if (fileTypeString == "SMRY")
file_type = ECL_SUMMARY_FILE;
else {
std::cerr << "Unknown ECLIPSE filetype specified with option -t. Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
comparator.loadGrids();
if (integrationTest && specificFileType) {
if (fileTypeString=="EGRID" || fileTypeString=="INIT" || fileTypeString=="RFT") {
std::cerr << "Integration test and spesific file type, only valid for UNRST and SMRY" << std::endl;
return EXIT_FAILURE;
}
}
if (specificFileType) {
if (fileTypeString == "EGRID") {
comparator.gridCompare();
} else if (fileTypeString == "INIT") {
comparator.results_init();
} else if (fileTypeString == "UNRST") {
comparator.results_rst();
} else if (fileTypeString == "SMRY") {
comparator.results_smry();
} else if (fileTypeString == "RFT") {
comparator.results_rft();
} else {
std::cerr << "Unknown ECLIPSE filetype specified with option -t. Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
} else if (integrationTest) {
comparator.results_rst();
comparator.results_smry();
} else {
comparator.gridCompare();
comparator.results_init();
comparator.results_rst();
comparator.results_smry();
comparator.results_rft();
}
if (comparator.getNoErrors() > 0)
OPM_THROW(std::runtime_error, comparator.getNoErrors() << " errors encountered in comparisons.");
}
if (restartFile && (file_type != ECL_SUMMARY_FILE || integrationTest)) {
std::cerr << "Error: -R can only be used in for summary regression tests." << std::endl;
return EXIT_FAILURE;
}
std::cout << "Comparing '" << basename1 << "' to '" << basename2 << "'." << std::endl;
try {
if (file_type == ECL_SUMMARY_FILE) {
if(!integrationTest){
SummaryRegressionTest compare(basename1,basename2,absTolerance,relTolerance);
compare.throwOnErrors(throwOnError);
compare.doAnalysis(analysis);
compare.setPrintKeywords(printKeywords);
compare.setIsRestartFile(restartFile);
compare.setAllowDifferentNumberOfKeywords(acceptExtraKeywords);
if(specificKeyword){
compare.getRegressionTest(keyword);
}
else{
compare.setPrintKeywords(printKeywords);
compare.getRegressionTest();
}
} else {
SummaryIntegrationTest compare(basename1,basename2,absTolerance,relTolerance);
compare.throwOnErrors(throwOnError);
compare.setFindVectorWithGreatestErrorRatio(findGreatestErrorRatio);
compare.setAllowSpikes(allowSpikes);
if (mainVariable) {
compare.setOneOfTheMainVariables(true);
std::string str(mainVariable);
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
if(str == "WOPR" ||str=="WWPR" ||str=="WGPR" || str == "WBHP"){
compare.setMainVariable(str);
}else{
throw std::invalid_argument("The input is not a main variable. -m option requires a valid main variable.");
}
}
compare.setFindVolumeError(findVolumeError);
if (spikeLimit != -1) {
compare.setSpikeLimit(spikeLimit);
}
compare.setAllowDifferentAmountOfKeywords(allowDifferentAmount);
compare.setPrintKeywords(printKeywords);
compare.setThrowExceptionForTooGreatErrorRatio(throwTooGreatErrorRatio);
if(specificKeyword){
compare.setPrintSpecificKeyword(specificKeyword);
compare.getIntegrationTest(keyword);
return 0;
}
compare.getIntegrationTest();
}
}
else if (integrationTest) {
ECLIntegrationTest comparator(basename1, basename2, absTolerance, relTolerance);
if (printKeywords) {
comparator.printKeywords();
return 0;
}
if (printKeywordsDifference) {
comparator.printKeywordsDifference();
return 0;
}
if (checkNumKeywords) {
comparator.equalNumKeywords();
}
if (specificKeyword) {
if (comparator.elementInWhitelist(keyword)) {
comparator.resultsForKeyword(keyword);
}
else {
std::cerr << "Keyword " << keyword << " is not supported for the integration test. Use SGAS, SWAT or PRESSURE." << std::endl;
return EXIT_FAILURE;
}
}
else {
comparator.results();
}
}
else {
ECLRegressionTest comparator(file_type, basename1, basename2, absTolerance, relTolerance);
comparator.throwOnErrors(throwOnError);
comparator.doAnalysis(analysis);
comparator.setAcceptExtraKeywords(acceptExtraKeywords);
if (printKeywords) {
comparator.printKeywords();
return 0;
}
if (printKeywordsDifference) {
comparator.printKeywordsDifference();
return 0;
}
if (onlyLastOccurrence) {
comparator.setOnlyLastOccurrence(true);
}
if (specificKeyword) {
comparator.gridCompare(volumecheck);
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare(volumecheck);
comparator.results();
}
if (comparator.getNoErrors() > 0)
OPM_THROW(std::runtime_error, comparator.getNoErrors() << " errors encountered in comparisons.");
}
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "\nProgram finished \n" << std::endl;
return 0;
}

View File

@@ -1,88 +0,0 @@
#include <iostream>
#include <chrono>
#include <tuple>
#include <iomanip>
#include <examples/test_util/EclFile.hpp>
#include <examples/test_util/EclOutput.hpp>
template<typename T>
void write(EclOutput& outFile, EclFile& file1,
const std::string& name, int index)
{
auto vect = file1.get<T>(index);
outFile.write(name, vect);
}
int main(int argc, char **argv) {
if (argc != 2) {
std::cout << "\nInvalid input, need 1 argument which should be the eclipse output file to be converted \n" << std::endl;
exit(1);
}
// start reading
auto start = std::chrono::system_clock::now();
std::string filename = argv[1];
EclFile file1(filename);
file1.loadData();
auto end1 = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds1 = end1 - start;
bool formattedOutput = file1.formattedInput() ? false : true;
int p = filename.find_last_of(".");
int l = filename.length();
std::string rootN = filename.substr(0,p);
std::string extension = filename.substr(p,l-p);
std::string resFile;
if (formattedOutput) {
resFile = rootN + ".F" + extension.substr(1, extension.length() - 1);
} else {
resFile = rootN + "." + extension.substr(2, extension.length() - 2);
}
std::cout << "\033[1;31m" << "\nconverting " << argv[1] << " -> " << resFile << "\033[0m\n" << std::endl;
EclOutput outFile(resFile, formattedOutput);
auto arrayList = file1.getList();
for (size_t index = 0; index < arrayList.size(); index++) {
std::string name = std::get<0>(arrayList[index]);
EIOD::eclArrType arrType = std::get<1>(arrayList[index]);
if (arrType == EIOD::INTE) {
write<int>(outFile, file1, name, index);
} else if (arrType == EIOD::REAL) {
write<float>(outFile, file1, name,index);
} else if (arrType == EIOD::DOUB) {
write<double>(outFile, file1, name, index);
} else if (arrType == EIOD::LOGI) {
write<bool>(outFile, file1, name, index);
} else if (arrType == EIOD::CHAR) {
write<std::string>(outFile, file1, name, index);
} else if (arrType == EIOD::MESS) {
// shold not be any associated data
outFile.write(name,std::vector<char>());
} else {
std::cout << "unknown array type " << std::endl;
exit(1);
}
}
auto end2 = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds2 = end2-end1;
std::cout << "\ntime to load from file : " << argv[1] << ": " << elapsed_seconds1.count() << " seconds" << std::endl;
std::cout << "time to write to file : " << resFile << ": " << elapsed_seconds2.count() << " seconds\n" << std::endl;
return 0;
}

View File

@@ -32,8 +32,6 @@ namespace Opm {
enum eclArrType {
INTE, REAL, DOUB, CHAR, LOGI, MESS
};
// named constants related to binary file format
const unsigned int true_value = 0xffffffff;
const unsigned int false_value = 0x00000000;
@@ -50,26 +48,6 @@ namespace Opm {
const int MaxBlockSizeLogi = 4000; // Maximum block size for LOGI arrays in binary files
const int MaxBlockSizeChar = 840; // Maximum block size for CHAR arrays in binary files
// named constants related to formatted file file format
const int MaxNumBlockInte = 1000; // maximum number of Inte values in block => hard line shift
const int MaxNumBlockReal = 1000; // maximum number of Real values in block => hard line shift
const int MaxNumBlockDoub = 1000; // maximum number of Doub values in block => hard line shift
const int MaxNumBlockLogi = 1000; // maximum number of Logi values in block => hard line shift
const int MaxNumBlockChar = 105; // maximum number of Char values in block => hard line shift
const int numColumnsInte = 6; // number of columns for Inte values
const int numColumnsReal = 4; // number of columns for Real values
const int numColumnsDoub = 3; // number of columns for Doub values
const int numColumnsLogi = 25; // number of columns for Logi values
const int numColumnsChar = 7; // number of columns for Char values
const int columnWidthInte = 12; // number of characters fore each Inte Element
const int columnWidthReal = 17; // number of characters fore each Inte Element
const int columnWidthDoub = 23; // number of characters fore each Inte Element
const int columnWidthLogi = 3; // number of characters fore each Inte Element
const int columnWidthChar = 11; // number of characters fore each Inte Element
} // ecl
} // Opm

View File

@@ -0,0 +1,240 @@
/*
Copyright 2016 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 "summaryComparator.hpp"
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.hpp>
#include <ert/util/int_vector.hpp>
#include <ert/util/bool_vector.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <cmath>
#include <numeric>
SummaryComparator::SummaryComparator(const std::string& basename1,
const std::string& basename2,
double absoluteTol, double relativeTol){
ecl_sum1 = ecl_sum_fread_alloc_case(basename1.c_str(), ":");
ecl_sum2 = ecl_sum_fread_alloc_case(basename2.c_str(), ":");
if (ecl_sum1 == nullptr || ecl_sum2 == nullptr) {
OPM_THROW(std::runtime_error, "Not able to open files");
}
absoluteTolerance = absoluteTol;
relativeTolerance = relativeTol;
keys1 = stringlist_alloc_new();
keys2 = stringlist_alloc_new();
ecl_sum_select_matching_general_var_list( ecl_sum1 , "*" , this->keys1);
stringlist_sort(this->keys1 , nullptr );
ecl_sum_select_matching_general_var_list( ecl_sum2 , "*" , this->keys2);
stringlist_sort(this->keys2 , nullptr );
if(stringlist_get_size(keys1) <= stringlist_get_size(keys2)){
this->keysShort = this->keys1;
this->keysLong = this->keys2;
}else{
this->keysShort = this->keys2;
this->keysLong = this->keys1;
}
}
SummaryComparator::~SummaryComparator(){
ecl_sum_free(ecl_sum1);
ecl_sum_free(ecl_sum2);
stringlist_free(keys1);
stringlist_free(keys2);
}
Deviation SummaryComparator::calculateDeviations(double val1, double val2){
double absDev;
Deviation deviation;
absDev = std::abs(val1 - val2);
deviation.abs = absDev;
if (val1 != 0 || val2 != 0) {
deviation.rel = absDev/double(std::max(std::abs(val1), std::abs(val2)));
}
return deviation;
}
void SummaryComparator::setTimeVecs(std::vector<double> &timeVec1,
std::vector<double> &timeVec2){
timeVec1.reserve(ecl_sum_get_data_length(ecl_sum1));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum1); time_index++){
timeVec1.push_back(ecl_sum_iget_sim_days(ecl_sum1 , time_index ));
}
timeVec2.reserve(ecl_sum_get_data_length(ecl_sum2));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum2); time_index++){
timeVec2.push_back(ecl_sum_iget_sim_days(ecl_sum2 , time_index ));
}
}
void SummaryComparator::getDataVecs(std::vector<double> &dataVec1,
std::vector<double> &dataVec2,
const char* keyword){
dataVec1.reserve(ecl_sum_get_data_length(ecl_sum1));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum1); time_index++){
dataVec1.push_back(ecl_sum_iget(ecl_sum1, time_index, ecl_sum_get_general_var_params_index( ecl_sum1 , keyword )));
}
dataVec2.reserve(ecl_sum_get_data_length(ecl_sum2));
for (int time_index = 0; time_index < ecl_sum_get_data_length(ecl_sum2); time_index++){
dataVec2.push_back(ecl_sum_iget(ecl_sum2, time_index, ecl_sum_get_general_var_params_index( ecl_sum2 , keyword )));
}
}
void SummaryComparator::setDataSets(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2){
if(timeVec1.size() < timeVec2.size()){
ecl_sum_fileShort = this->ecl_sum1;
ecl_sum_fileLong = this->ecl_sum2;
}
else{
ecl_sum_fileShort = this->ecl_sum2;
ecl_sum_fileLong = this->ecl_sum1;
}
}
void SummaryComparator::chooseReference(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec1,
const std::vector<double>& dataVec2){
if(timeVec1.size() <= timeVec2.size()){
referenceVec = &timeVec1; // time vector
referenceDataVec = &dataVec1; //data vector
checkVec = &timeVec2;
checkDataVec = &dataVec2;
}
else{
referenceVec = &timeVec2;
referenceDataVec = &dataVec2;
checkVec = &timeVec1;
checkDataVec = &dataVec1;
}
}
void SummaryComparator::getDeviation(size_t refIndex, size_t &checkIndex, Deviation &dev){
if((*referenceVec)[refIndex] == (*checkVec)[checkIndex]){
dev = SummaryComparator::calculateDeviations((*referenceDataVec)[refIndex], (*checkDataVec)[checkIndex]);
checkIndex++;
return;
}
else if((*referenceVec)[refIndex]<(*checkVec)[checkIndex]){
double value = SummaryComparator::unitStep((*checkDataVec)[checkIndex]);
/*Must be a little careful here. Flow writes out old value first,
than changes value. Say there should be a change in production rate from A to B at timestep 300.
Then the data of time step 300 is A and the next timestep will have value B. Must use the upper limit. */
dev = SummaryComparator::calculateDeviations((*referenceDataVec)[refIndex], value);
checkIndex++;
return;
}
else{
checkIndex++;
getDeviation(refIndex, checkIndex , dev);
}
if(checkIndex == checkVec->size() -1 ){
return;
}
}
void SummaryComparator::printUnits(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
std::cout << stringlist_iget(keysLong, jvar) << " unit: " << ecl_sum_get_unit(ecl_sum_fileShort, stringlist_iget(keysLong, jvar)) << std::endl;
}
}
//Called only when the keywords are equal in the getDeviations()-function
const char* SummaryComparator::getUnit(const char* keyword){
return ecl_sum_get_unit(ecl_sum_fileShort, keyword);
}
void SummaryComparator::printKeywords(){
int ivar = 0;
std::vector<std::string> noMatchString;
std::cout << "Keywords that are common for the files:" << std::endl;
while(ivar < stringlist_get_size(keysLong)){
const char* keyword = stringlist_iget(keysLong, ivar);
if (stringlist_contains(keysLong, keyword) && stringlist_contains(keysShort, keyword)){
std::cout << keyword << std::endl;
ivar++;
}
else{
noMatchString.push_back(keyword);
ivar++;
}
}
if(noMatchString.size() == 0){
std::cout << "No keywords were different" << std::endl;
return;
}
std::cout << "Keywords that are different: " << std::endl;
for (const auto& it : noMatchString) std::cout << it << std::endl;
std::cout << "\nOf the " << stringlist_get_size(keysLong) << " keywords " << stringlist_get_size(keysLong)-noMatchString.size() << " were equal and " << noMatchString.size() << " were different" << std::endl;
}
void SummaryComparator::printDataOfSpecificKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
size_t ivar = 0;
size_t jvar = 0;
const char separator = ' ';
const int numWidth = 14;
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Time";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Ref data";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "Check data" << std::endl;
while(ivar < referenceVec->size()){
if(ivar == referenceVec->size() || jvar == checkVec->size() ){
break;
}
if((*referenceVec)[ivar] == (*checkVec)[jvar]){
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceDataVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkDataVec)[jvar] << std::endl;
ivar++;
jvar++;
}else if((*referenceVec)[ivar] < (*checkVec)[jvar]){
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*referenceDataVec)[ivar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "" << std::endl;
ivar++;
}
else{
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkVec)[jvar];
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << "";
std::cout << std::left << std::setw(numWidth) << std::setfill(separator) << (*checkDataVec)[jvar] << std::endl;
jvar++;
}
}
}

View File

@@ -0,0 +1,182 @@
/*
Copyright 2016 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 SUMMARYCOMPARATOR_HPP
#define SUMMARYCOMPARATOR_HPP
#include "Deviation.hpp"
#include <iostream>
#include <iomanip>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else \
std::cerr << message << std::endl; \
}
//! \brief Prototyping struct, encapsuling the stringlist libraries.
struct stringlist_struct;
typedef struct stringlist_struct stringlist_type;
//! \brief Prototyping struct, encapsuling the ert libraries.
struct ecl_sum_struct;
typedef struct ecl_sum_struct ecl_sum_type;
class SummaryComparator {
private:
double absoluteTolerance = 0; //!< The maximum absolute deviation that is allowed between two values.
double relativeTolerance = 0; //!< The maximum relative deviation that is allowed between twi values.
protected:
ecl_sum_type * ecl_sum1 = nullptr; //!< Struct that contains file1
ecl_sum_type * ecl_sum2 = nullptr; //!< Struct that contains file2
ecl_sum_type * ecl_sum_fileShort = nullptr; //!< For keeping track of the file with most/fewest timesteps
ecl_sum_type * ecl_sum_fileLong = nullptr; //!< For keeping track of the file with most/fewest timesteps
stringlist_type* keys1 = nullptr; //!< For storing all the keywords of file1
stringlist_type* keys2 = nullptr; //!< For storing all the keywords of file2
stringlist_type * keysShort = nullptr; //!< For keeping track of the file with most/fewest keywords
stringlist_type * keysLong = nullptr; //!< For keeping track of the file with most/fewest keywords
const std::vector<double> * referenceVec = nullptr; //!< For storing the values of each time step for the file containing the fewer time steps.
const std::vector<double> * referenceDataVec = nullptr; //!< For storing the data corresponding to each time step for the file containing the fewer time steps.
const std::vector<double> * checkVec = nullptr; //!< For storing the values of each time step for the file containing the more time steps.
const std::vector<double> * checkDataVec = nullptr; //!< For storing the data values corresponding to each time step for the file containing the more time steps.
bool printKeyword = false; //!< Boolean value for choosing whether to print the keywords or not
bool printSpecificKeyword = false; //!< Boolean value for choosing whether to print the vectors of a keyword or not
bool throwOnError = true; //!< Throw on first error
bool analysis = false; //!< Perform error analysis
std::map<std::string, std::vector<Deviation>> deviations;
//! \brief Calculate deviation between two data values and stores it in a Deviation struct.
//! \param[in] refIndex Index in reference data
//! \param[in] checkindex Index in data to be checked.
//! \param[out] dev Holds the result from the comparison on return.
//! \details Uses the #referenceVec as basis, and checks its values against the values in #checkDataVec. The function is reccursive, and will update the iterative index j of the #checkVec until #checkVec[j] >= #referenceVec[i]. \n When #referenceVec and #checkVec have the same time value (i.e. #referenceVec[i] == #checkVec[j]) a direct comparison is used, \n when this is not the case, when #referenceVec[i] do not excist as an element in #checkVec, a value is generated, either by the principle of unit step or by interpolation.
void getDeviation(size_t refIndex, size_t &checkIndex, Deviation &dev);
//! \brief Figure out which data file contains the most / less timesteps and assign member variable pointers accordingly.
//! \param[in] timeVec1 Data from first file
//! \param[in] timeVec2 Data from second file
//! \details Figure out which data file that contains the more/fewer time steps and assigns the private member variable pointers #ecl_sum_fileShort / #ecl_sum_fileLong to the correct data sets #ecl_sum1 / #ecl_sum2.
void setDataSets(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2);
//! \brief Reads in the time values of each time step.
//! \param[in] timeVec1 Vector for storing the time steps from file1
//! \param[in] timeVec2 Vector for storing the time steps from file2
void setTimeVecs(std::vector<double> &timeVec1,std::vector<double> &timeVec2);
//! \brief Read the data for one specific keyword into two separate vectors.
//! \param[in] dataVec1 Vector for storing the data for one specific keyword from file1
//! \param[in] dataVec2 Vector for storing the data for one specific keyword from file2
//! \details The two data files do not necessarily have the same amount of data values, but the values must correspond to the same interval in time. Thus possible to interpolate values.
void getDataVecs(std::vector<double> &dataVec1,
std::vector<double> &dataVec2, const char* keyword);
//! \brief Sets one data set as a basis and the other as values to check against.
//! \param[in] timeVec1 Used to figure out which dataset that have the more/fewer time steps.
//! \param[in] timeVec2 Used to figure out which dataset that have the more/fewer time steps.
//! \param[in] dataVec1 For assiging the the correct pointer to the data vector.
//! \param[in] dataVec2 For assiging the the correct pointer to the data vector.
//! \details Figures out which time vector that contains the fewer elements. Sets this as #referenceVec and its corresponding data as #referenceDataVec. \n The remaining data set is set as #checkVec (the time vector) and #checkDataVec.
void chooseReference(const std::vector<double> &timeVec1,
const std::vector<double> &timeVec2,
const std::vector<double> &dataVec1,
const std::vector<double> &dataVec2);
//! \brief Returns the relative tolerance.
double getRelTolerance(){return this->relativeTolerance;}
//! \brief Returns the absolute tolerance.
double getAbsTolerance(){return this->absoluteTolerance;}
//! \brief Returns the unit of the values of a keyword
//! \param[in] keyword The keyword of interest.
//! \param[out] ret The unit of the keyword as a const char*.
const char* getUnit(const char* keyword);
//! \brief Prints the units of the files.
void printUnits();
//! \brief Prints the keywords of the files.
//! \details The function prints first the common keywords, than the keywords that are different.
void printKeywords();
//! \brief Prints the summary vectors from the two files.
//! \details The function requires that the summary vectors of the specific file have been read into the member variables referenceVec etc.
void printDataOfSpecificKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword);
public:
//! \brief Creates an SummaryComparator class object
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] absoluteTolerance The absolute tolerance which is to be used in the test.
//! \param[in] relativeTolerance The relative tolerance which is to be used in the test.
//! \details The constructor creates an object of the class, and openes the files, an exception is thrown if the opening of the files fails. \n It creates stringlists, in which keywords are to be stored, and figures out which keylist that contains the more/less keywords. \n Also the private member variables aboluteTolerance and relativeTolerance are set.
SummaryComparator(const std::string& basename1,
const std::string& basename2,
double absoluteTolerance, double relativeTolerance);
//! \brief Destructor
//! \details The destructor takes care of the allocated memory in which data has been stored.
~SummaryComparator();
//! \brief Calculates the deviation between two values
//! \param[in] val1 The first value of interest.
//! \param[in] val2 The second value if interest.
//! \param[out] ret Returns a Deviation struct.
//! \details The function takes two values, calculates the absolute and relative deviation and returns the result as a Deviation struct.
static Deviation calculateDeviations( double val1, double val2);
//! \brief Sets the private member variable printKeywords
//! \param[in] boolean Boolean value
//! \details The function sets the private member variable printKeywords. When it is true the function printKeywords will be called.
void setPrintKeywords(bool boolean){this->printKeyword = boolean;}
//! \brief Sets the private member variable printSpecificKeyword
//! \param[in] boolean Boolean value
//! \details The function sets the private member variable printSpecificKeyword. When true, the summary vector of the keyword for both files will be printed.
void setPrintSpecificKeyword(bool boolean){this->printSpecificKeyword = boolean;}
//! \brief Unit step function
//! \param[in] value The input value should be the last know value
//! \param[out] ret Return the unit-step-function value.
//! \details In this case: The unit step function is used when the data from the two data set, which is to be compared, don't match in time. \n The unit step function is then to be called on the #checkDataVec 's value at the last time step which is before the time of comparison.
//! \brief Returns a value based on the unit step principle.
static double unitStep(double value){return value;}
//! \brief Set whether to throw on errors or not.
void throwOnErrors(bool dothrow) { throwOnError = dothrow; }
//! \brief Set whether or not to perform error analysis.
void doAnalysis(bool analyse) { analysis = analyse; }
};
#endif

View File

@@ -0,0 +1,386 @@
/*
Copyright 2016 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 "summaryIntegrationTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.hpp>
#include <cmath>
void SummaryIntegrationTest::getIntegrationTest(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
int ivar = 0;
if(!allowDifferentAmountOfKeywords){
if(stringlist_get_size(keysShort) != stringlist_get_size(keysLong)){
OPM_THROW(std::invalid_argument, "Different ammont of keywords in the two summary files.");
}
}
if(printKeyword){
printKeywords();
return;
}
std::string keywordWithGreatestErrorRatio;
double greatestRatio = 0;
//Iterates over all keywords from the restricted file, use iterator "ivar". Searches for a match in the file with more keywords, use the itarator "jvar".
while(ivar < stringlist_get_size(keysShort)){
const char* keyword = stringlist_iget(keysShort, ivar);
if(oneOfTheMainVariables){
std::string keywordString(keyword);
std::string substr = keywordString.substr(0,4);
if(substr!= mainVariable){
ivar++;
continue;
}
}
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
if (strcmp(keyword, stringlist_iget(keysLong, jvar)) == 0){ //When the keywords are equal, proceed in comparing summary files.
/* if(!checkUnits(keyword)){
OPM_THROW(std::runtime_error, "For keyword " << keyword << " the unit of the two files is not equal. Not possible to compare.");
} //Comparing the unit of the two vectors.*/
checkForKeyword(timeVec1, timeVec2, keyword);
if(findVectorWithGreatestErrorRatio){
WellProductionVolume volume = getSpecificWellVolume(timeVec1,timeVec2, keyword);
findGreatestErrorRatio(volume,greatestRatio, keyword, keywordWithGreatestErrorRatio);
}
break;
}
//will only enter here if no keyword match
if(jvar == stringlist_get_size(keysLong)-1){
if(!allowDifferentAmountOfKeywords){
OPM_THROW(std::invalid_argument, "No match on keyword");
}
}
}
ivar++;
}
if(findVectorWithGreatestErrorRatio){
std::cout << "The keyword " << keywordWithGreatestErrorRatio << " had the greatest error ratio, which was " << greatestRatio << std::endl;
}
if((findVolumeError || oneOfTheMainVariables) && !findVectorWithGreatestErrorRatio){
evaluateWellProductionVolume();
}
if(allowSpikes){
std::cout << "checkWithSpikes succeeded." << std::endl;
}
}
void SummaryIntegrationTest::getIntegrationTest(const char* keyword){
if(stringlist_contains(keysShort,keyword) && stringlist_contains(keysLong, keyword)){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2);
if(printSpecificKeyword){
printDataOfSpecificKeyword(timeVec1, timeVec2, keyword);
}
if(findVolumeError){
WellProductionVolume volume = getSpecificWellVolume(timeVec1, timeVec2, keyword);
if(volume.error == 0){
std::cout << "For keyword " << keyword << " the total production volume is 0" << std::endl;
}
else{
std::cout << "For keyword " << keyword << " the total production volume is "<< volume.total;
std::cout << ", the error volume is " << volume.error << " the error ratio is " << volume.error/volume.total << std::endl;
}
}
checkForKeyword(timeVec1, timeVec2, keyword);
return;
}
OPM_THROW(std::invalid_argument, "The keyword used is not common for the two files.");
}
void SummaryIntegrationTest::checkForKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
if(allowSpikes){
checkWithSpikes(keyword);
}
if(findVolumeError ||oneOfTheMainVariables ){
volumeErrorCheck(keyword);
}
}
int SummaryIntegrationTest::checkDeviation(const Deviation& deviation){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel> relTol && deviation.abs > absTol){
return 1;
}
return 0;
}
void SummaryIntegrationTest::findGreatestErrorRatio(const WellProductionVolume& volume,
double &greatestRatio,
const char* currentKeyword,
std::string &greatestErrorRatio){
if (volume.total != 0 && (volume.total - volume.error > getAbsTolerance()) ){
if(volume.error/volume.total > greatestRatio){
greatestRatio = volume.error/volume.total;
std::string currentKeywordStr(currentKeyword);
greatestErrorRatio = currentKeywordStr;
}
}
}
void SummaryIntegrationTest::volumeErrorCheck(const char* keyword){
const ecl::smspec_node * node = ecl_sum_get_general_var_node (ecl_sum_fileShort ,keyword);//doesn't matter which ecl_sum_file one uses, the kewyord SHOULD be equal in terms of smspec data.
if (node->is_historical())
return;
if (!mainVariable.empty()){
std::string keywordString(keyword);
std::string firstFour = keywordString.substr(0,4);
if(mainVariable == firstFour && firstFour == "WOPR"){
if(firstFour == "WOPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WOP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WWPR"){
if(firstFour == "WWPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WWP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WGPR"){
if(firstFour == "WGPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WGP += result;
return;
}
}
if(mainVariable == firstFour && firstFour == "WBHP"){
if(firstFour == "WBHP"){
WellProductionVolume result = getWellProductionVolume(keyword);
WBHP += result;
return;
}
}
}
updateVolumeError(keyword);
}
void SummaryIntegrationTest::updateVolumeError(const char* keyword){
std::string keywordString(keyword);
std::string firstFour = keywordString.substr(0,4);
if(firstFour == "WOPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WOP += result;
}
if(firstFour == "WWPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WWP += result;
}
if(firstFour == "WGPR"){
WellProductionVolume result = getWellProductionVolume(keyword);
WGP += result;
}
if(firstFour == "WBHP"){
WellProductionVolume result = getWellProductionVolume(keyword);
WBHP += result;
}
}
WellProductionVolume SummaryIntegrationTest::getWellProductionVolume(const char * keyword){
double total = integrate(*referenceVec, *referenceDataVec);
double error = integrateError(*referenceVec, *referenceDataVec,
*checkVec, *checkDataVec);
WellProductionVolume wPV;
wPV.total = total;
wPV.error = error;
if(wPV.total != 0 && wPV.total-wPV.error > getAbsTolerance()){
if( (wPV.error/wPV.total > getRelTolerance()) && throwExceptionForTooGreatErrorRatio){
OPM_THROW(std::runtime_error, "For the keyword "<< keyword << " the error ratio was " << wPV.error/wPV.total << " which is greater than the tolerance " << getRelTolerance());
}
}
return wPV;
}
void SummaryIntegrationTest::evaluateWellProductionVolume(){
if(mainVariable.empty()){
double ratioWOP, ratioWWP, ratioWGP, ratioWBHP;
ratioWOP = WOP.error/WOP.total;
ratioWWP = WWP.error/WWP.total;
ratioWGP = WGP.error/WGP.total;
ratioWBHP = WBHP.error/WBHP.total;
std::cout << "\n The total oil volume is " << WOP.total << ". The error volume is "<< WOP.error << ". The error ratio is " << ratioWOP << std::endl;
std::cout << "\n The total water volume is " << WWP.total << ". The error volume is "<< WWP.error << ". The error ratio is " << ratioWWP << std::endl;
std::cout << "\n The total gas volume is " << WGP.total <<". The error volume is "<< WGP.error << ". The error ratio is " << ratioWGP << std::endl;
std::cout << "\n The total area under the WBHP curve is " << WBHP.total << ". The area under the error curve is "<< WBHP.error << ". The error ratio is " << ratioWBHP << std::endl << std::endl;
}
if(mainVariable == "WOPR"){
std::cout << "\nThe total oil volume is " << WOP.total << ". The error volume is "<< WOP.error << ". The error ratio is " << WOP.error/WOP.total << std::endl<< std::endl;
}
if(mainVariable == "WWPR"){
std::cout << "\nThe total water volume is " << WWP.total << ". The error volume is "<< WWP.error << ". The error ratio is " << WWP.error/WWP.total << std::endl<< std::endl;
}
if(mainVariable == "WGPR"){
std::cout << "\nThe total gas volume is " << WGP.total <<". The error volume is "<< WGP.error << ". The error ratio is " << WGP.error/WGP.total << std::endl<< std::endl;
}
if(mainVariable == "WBHP"){
std::cout << "\nThe total area under the WBHP curve " << WBHP.total << ". The area under the error curve is "<< WBHP.error << ". The error ratio is " << WBHP.error/WBHP.total << std::endl << std::endl;
}
}
void SummaryIntegrationTest::checkWithSpikes(const char* keyword){
int errorOccurrences = 0;
size_t jvar = 0 ;
bool spikeCurrent = false;
Deviation deviation;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
int errorOccurrencesPrev = errorOccurrences;
bool spikePrev = spikeCurrent;
getDeviation(ivar,jvar, deviation);
errorOccurrences += checkDeviation(deviation);
if (errorOccurrences != errorOccurrencesPrev){
spikeCurrent = true;
} else{
spikeCurrent = false;
}
if(spikePrev&&spikeCurrent){
std::cout << "For keyword " << keyword << " at time step " << (*referenceVec)[ivar] <<std::endl;
OPM_THROW(std::invalid_argument, "For keyword " << keyword << " at time step " << (*referenceVec)[ivar] << ", wwo deviations in a row exceed the limit. Not a spike value. Integration test fails." );
}
if(errorOccurrences > this->spikeLimit){
std::cout << "For keyword " << keyword << std::endl;
OPM_THROW(std::invalid_argument, "For keyword " << keyword << " too many spikes in the vector. Integration test fails.");
}
}
}
WellProductionVolume
SummaryIntegrationTest::getSpecificWellVolume(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
return getWellProductionVolume(keyword);
}
double SummaryIntegrationTest::integrate(const std::vector<double>& timeVec,
const std::vector<double>& dataVec){
double totalSum = 0;
if(timeVec.size() != dataVec.size()){
OPM_THROW(std::runtime_error, "The size of the time vector does not match the size of the data vector.");
}
for(size_t i = 0; i < timeVec.size()-1; i++){
double width = timeVec[i+1] - timeVec[i];
double height = dataVec[i+1];
totalSum += getRectangleArea(height, width);
}
return totalSum;
}
double SummaryIntegrationTest::integrateError(const std::vector<double>& timeVec1,
const std::vector<double>& dataVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec2){
// When the data corresponds to a rate the integration will become a Riemann
// sum. This function calculates the Riemann sum of the error. The reason why
// a Riemann sum is used is because of the way the data is written to file.
// When a change occur (e.g. change of a rate), the data (value and time) is
// written to file, THEN the change happens in the simulator, i.e., we will
// notice the change at the next step.
//
// Keep in mind that the summary vector is NOT a continuous curve, only points
// of data (time, value). We have to guess what happens between the data
// points, we do this by saying: "There are no change, the only change happens
// at the data points." As stated above, the value of this constant "height" of
// the rectangle corresponds to the value of the last time step. Thus we have
// to use the "right hand side value of the rectangle as height
//
// someDataVector[ivar] instead of someDataVector[ivar-1]
//
// (which intuition is saying is the correct value to use).
if(timeVec1.size() != dataVec1.size() || timeVec2.size() != dataVec2.size() ){
OPM_THROW(std::runtime_error, "The size of the time vector does not match the size of the data vector.");
}
double errorSum = 0;
double rightEdge, leftEdge, width;
size_t i = 1;
size_t j = 1;
leftEdge = timeVec1[0];
while(i < timeVec1.size()){
if(j == timeVec2.size() ){
break;
}
if(timeVec1[i] == timeVec2[j]){
rightEdge = timeVec1[i];
width = rightEdge - leftEdge;
double dev = std::fabs(dataVec1[i] - dataVec2[j]);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
i++;
j++;
continue;
}
if(timeVec1[i] < timeVec2[j]){
rightEdge = timeVec1[i];
width = rightEdge - leftEdge;
double value = unitStep(dataVec2[j]);
double dev = std::fabs(dataVec1[i]-value);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
i++;
continue;
}
if(timeVec2[j] < timeVec1[i]){
rightEdge = timeVec2[j];
width = rightEdge - leftEdge;
double value = unitStep(dataVec1[i]);
double dev = std::fabs(dataVec2[j]-value);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
j++;
continue;
}
}
return errorSum;
}

View File

@@ -0,0 +1,217 @@
/*
Copyright 2016 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 "summaryComparator.hpp"
//! \brief Struct for storing the total area under a graph.
//! \details Used when plotting summary vector against time. In most cases this represents a volume.
struct WellProductionVolume{
double total=0; //!< The total area under the graph when plotting the summary vector against time. In most cases the total production volume.
double error=0; //!< The total area under the graph when plotting the deviation vector against time. In most cases the total error volume.
//! \brief Overloaded operator
//! \param[in] rhs WellProductionVolume struct
WellProductionVolume& operator+=(const WellProductionVolume& rhs){
this->total += rhs.total;
this->error += rhs.error;
return *this;
}
};
//! \details The class inherits from the SummaryComparator class, which takes care of all file reading. \n The IntegrationTest class compares values from the two different files and throws exceptions when the deviation is unsatisfying.
class SummaryIntegrationTest: public SummaryComparator {
private:
bool allowSpikes = false; //!< Boolean value, when true checkForSpikes is included as a sub test in the integration test. By default: false.
bool findVolumeError = false; //!< Boolean value, when true volumeErrorCheck() is included as a sub test in the integration test. By default: false.
bool allowDifferentAmountOfKeywords = true; //!< Boolean value, when false the integration test will check wheter the two files have the same amount of keywords. \nIf they don't, an exception will be thrown. By default: true.
bool findVectorWithGreatestErrorRatio = false; //!< Boolean value, when true the integration test will find the vector that has the greatest error ratio. By default: false.
bool oneOfTheMainVariables = false; //!< Boolean value, when true the integration test will only check for one of the primary variables (WOPR, WGPR, WWPR. WBHP), which will be specified by user. By default: false.
bool throwExceptionForTooGreatErrorRatio = true; //!< Boolean value, when true any volume error ratio that exceeds the relativeTolerance will cause an exception to be thrown. By default: true.
std::string mainVariable; //!< String variable, where the name of the main variable of interest (one of WOPR, WBHP, WWPR, WGPR) is stored. Can be empty.
int spikeLimit = 13370; //!< The limit for how many spikes to allow in the data set of a certain keyword. By default: Set to a high number, \n should not trig the (if spikeOccurrences > spikeLimit){ // throw exception }.
WellProductionVolume WOP; //!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WOPR
WellProductionVolume WWP; //!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WWPR
WellProductionVolume WGP;//!< WellProductionVolume struct for storing the total production volume and total error volume of all the keywords which start with WGPR
WellProductionVolume WBHP; //!< WellProductionVolume struct for storing the value of the area under the graph when plotting summary vector/deviation vector against time.This is for keywords starting with WBHP. \nNote: the name of the struct may be misleading, this is not an actual volume.
//! \brief The function gathers the correct data for comparison for a specific keyword
//! \param[in] timeVec1 A std::vector<double> that contains the time steps of file 1.
//! \param[in] timeVec2 A std::vector<double> that contains the time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \details The function requires an outer loop which iterates over the keywords of the files. It prepares an integration test by gathering the data, stroing it into two vectors, \n deciding which is to be used as a reference/basis and calling the test function.
void checkForKeyword(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2, const char* keyword);
//! \brief The function compares the volume error to the total production volume of a certain type of keyword.
//! param[in] keyword The keyword of interest.
//! \details The function takes in a keyword and checks if it is of interest. Only keywords which say something about the well oil production, well water production, \n well gas production and the well BHP are of interest. The function sums up the total production in the cases where it is possible, \n and sums up the error volumes by a trapezoid integration method. The resulting values are stored in member variable structs of type WellProductionVolume, and double variables. For proper use of the function all the keywords of the file should be checked. This is satisfied if it is called by checkForKeyword.
void volumeErrorCheck(const char* keyword);
//! \brief The function calculates the total production volume and total error volume of a specific keyword
//! \param[in] timeVec1 A std::vector<double> that contains the time steps of file 1.
//! \param[in] timeVec2 A std::vector<double> that contains the time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \param[out] ret Returns a WellProductionWolume struct
//! \details The function reads the data from the two files into the member variable vectors (of the super class). It returns a WellProductionVolume struct calculated from the vectors corresponding to the keyword.
WellProductionVolume getSpecificWellVolume(const std::vector<double>& timeVec1,
const std::vector<double>& timeVec2,
const char* keyword);
//! \brief The function is a regression test which allows spikes.
//! \param[in] keyword The keyword of interest, the keyword the summary vectors "belong" to.
//! \details The function requires the protected member variables referenceVec, referenceDataVec, checkVec and checkDataVec to be stored with data, which is staisfied if it is called by checkForKeyword. \n It compares the two vectors value by value, and if the deviation is unsatisfying, the errorOccurrenceCounter is incremented. If the errorOccurrenceCounter becomes greater than the errorOccurrenceLimit, \n a exception is thrown. The function will allow spike values, however, if two values in a row exceed the deviation limit, they are no longer spikes, and an exception is thrown.
void checkWithSpikes(const char* keyword);
//! \brief Caluculates a deviation, throws exceptions and writes and error message.
//! \param[in] deviation Deviation struct
//! \param[out] int Returns 0/1, depending on wheter the deviation exceeded the limit or not.
//! \details The function checks the values of the Deviation struct against the absolute and relative tolerance, which are private member values of the super class. \n When comparing against the relative tolerance an additional term is added, the absolute deviation has to be greater than 1e-6 for the function to throw an exception. \n When the deviations are too great, the function returns 1.
int checkDeviation(const Deviation& deviation);
//! \brief Calculates the keyword's total production volume and error volume
//! \param[in] keyword The keyword of interest.
//! \param[out] wellProductionVolume A struct containing the total production volume and the total error volume.
//! \details The function calculates the total production volume and total error volume of a keyword, by the trapezoid integral method. \n The function throws and exception if the total error volume is negative. The function returns the results as a struct.
WellProductionVolume getWellProductionVolume(const char* keyword);
//! \brief The function function works properly when the private member variables are set (after running the integration test which findVolumeError = true). \n It prints out the total production volume, the total error volume and the error ratio.
void evaluateWellProductionVolume();
//! \brief The function calculates the total production volume and total error volume
//! \param keyword The keyword of interest
//! \details The function uses the data that is stored in the member variable vectors. It calculates the total production volume \n and the total error volume of the specified keyword, and adds the result to the private member WellProductionVolume variables of the class.
void updateVolumeError(const char* keyword);
//! \brief Finds the keyword which has the greates error volume ratio
//! \param[in] volume WellProductionVolume struct which contains the data used for comparison
//! \param[in] greatestRatio Double value taken in by reference. Stores the greatest error ratio value.
//! \param[in] currentKeyword The keyword that is under evaluation
//! \param[in] greatestErrorRatio String which contains the name of the keyword which has the greatest error ratio
//! \details The function requires an outer loop which iterates over the keywords in the files, and calls the function for each keyword. \nThe valiables double greatestRatio and std::string keywordWithTheGreatestErrorRatio must be declared outside the loop. \nWhen the current error ratio is greater than the value stored in greatestRatio, the gratestRatio value is updated with the current error ratio.
void findGreatestErrorRatio(const WellProductionVolume& volume,
double &greatestRatio,
const char* currentKeyword,
std::string &greatestErrorRatio);
#if 0
//! \brief Checks whether the unit of the two data vectors is the same
//! \param[in] keyword The keyword of interest
//! \param[out] boolean True/false, depending on whether the units are equal or not
bool checkUnits(const char* keyword);
#endif
public:
//! \brief Constructor, creates an object of IntegrationTest class.
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] atol The absolute tolerance which is to be used in the test.
//! \param[in] rtol The relative tolerance which is to be used in the test.
//! \details The constructor calls the constructor of the super class.
SummaryIntegrationTest(const std::string& basename1,
const std::string& basename2,
double atol, double rtol) :
SummaryComparator(basename1, basename2, atol, rtol) {}
//! \brief This function sets the private member variable allowSpikes.
//! \param[in] allowSpikes Boolean value
//! \details When allowSpikes is true, the integration test checkWithSpikes is excecuted.
void setAllowSpikes(bool allowSpikesArg){this->allowSpikes = allowSpikesArg;}
//! \brief This function sets the private member variable findVolumeError.
//! \param[in] findVolumeError Boolean value
//! \details When findVolumeError is true, the integration test volumeErrorCheck and the function evaluateWellProductionVolume are excecuted.
void setFindVolumeError(bool findVolumeErrorArg){this->findVolumeError = findVolumeErrorArg;}
//! \brief This function sets the private member variable oneOfTheMainVariables
//! \param[in] oneOfTheMainVariables Boolean value
//! \details When oneOfTheMainVariables is true, the integration test runs the substest volumeErrorCheckForOneSpecificVariable.
void setOneOfTheMainVariables(bool oneOfTheMainVariablesArg){this->oneOfTheMainVariables = oneOfTheMainVariablesArg;}
//! \brief This function sets the member variable string #mainVariable
//! \param[in] mainVar This is the string should contain one of the main variables. e.g. WOPR
void setMainVariable(std::string mainVar){this->mainVariable = mainVar;}
//! \brief This function sets the private member variable spikeLimit.
//! \param[in] lim The value which the spike limit is to be given.
void setSpikeLimit(int lim){this->spikeLimit = lim;}
//! \brief This function sets the private member variable findVectorWithGreatestErrorRatio
//! \param[in] findVolumeError Boolean value
//! \details When findVectorWithGreatestErrorRatio is true, the integration test will print the vector with the greatest error ratio.
void setFindVectorWithGreatestErrorRatio(bool boolean){this->findVectorWithGreatestErrorRatio = boolean;}
//! \brief This function sets the private member variable allowDifferentAmountsOfKeywords
//! \param[in] boolean Boolean value
//! \details When allowDifferentAmountOfKeywords is false, the amount of kewyord in the two files will be compared. \nIf the number of keywords are different an exception will be thrown.
void setAllowDifferentAmountOfKeywords(bool boolean){this->allowDifferentAmountOfKeywords = boolean;}
//! \brief This function sets the private member variable throwExceptionForTooGreatErrorRatio
//! \param[in] boolean Boolean value
//! \details When throwExceptionForTooGreatErrorRatio is false, the function getWellProductionVolume will throw an exception.
void setThrowExceptionForTooGreatErrorRatio(bool boolean){this->throwExceptionForTooGreatErrorRatio = boolean;}
//! \brief This function executes a integration test for all the keywords. If the two files do not match in amount of keywords, an exception is thrown. \n Uses the boolean member variables to know which tests to execute.
void getIntegrationTest();
//! \brief This function executes a integration test for one specific keyword. If one or both of the files do not have the keyword, an exception is thorwn. \n Uses the boolean member variables to know which tests to execute.
void getIntegrationTest(const char* keyword);
//! \brief This function calculates the area of an rectangle of height height and width time-timePrev
//! \param[in] height The height of the rectangle. See important statement of use below.
//! \param[in] width The width of the rectangle
//! \param[out] area Returns the area of the rectangle
//! \details This function is simple. When using it on a summary vector (data values plotted againt time), calculating the area between the two points i and i+1 note this:\nThe width is time_of_i+1 - time_of_i, the height is data_of_i+1 NOT data_of_i. The upper limit must be used.
static double getRectangleArea(double height, double width){return height*width;}
//! \brief This function calculates the area under a graph by doing a Riemann sum
//! \param[in] timeVec Contains the time values
//! \param[in] dataVec Contains the data values
//! \details The function does a Riemann sum integration of the graph formed
//! by the points (timeVec[i] , dataVec[i]).
//! In the case of a summary vector, the summary vector of quantity
//! corresponding to a rate, is a piecewise continus function consisting
//! of unit step functions. Thus the Riemann sum will become an
//! exact expression for the integral of the graph.
//! Important: For the data values correspoding to time i and i-1,
//! the fixed value of the height of the rectangles in the Riemann sum
//! is set by the data value i. The upper limit must be used.
static double integrate(const std::vector<double>& timeVec,
const std::vector<double>& dataVec);
//! \brief This function calculates the Riemann sum of the error between two graphs.
//! \param[in] timeVec1 Contains the time values of graph 1
//! \param[in] dataVec1 Contains the data values of graph 1
//! \param[in] timeVec2 Contains the time values of graph 2
//! \param[in] dataVec2 Contains the data values of graph 2
//! \details This function takes in two graphs and returns the integrated error.
//! In case of ecl summary vectors: if the vectors correspond to a
//! quantity which is a rate, the vectors will be piecewise
//! continous unit step functions. In this case the error will also
//! be a piecewise continous unit step function. The function uses
//! a Riemann sum when calculating the integral. Thus the integral
//! will become exact. Important: For the data values corresponding
//! to time i and i-1, the fixed value of the height of the rectangles
//! in the Riemann sum is set by the data value i.
//! The upper limit must be used.
static double integrateError(const std::vector<double>& timeVec1,
const std::vector<double>& dataVec1,
const std::vector<double>& timeVec2,
const std::vector<double>& dataVec2);
};

View File

@@ -0,0 +1,178 @@
/*
Copyright 2016 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 "summaryRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.hpp>
#include <ert/util/stringlist.h>
#include <string>
void SummaryRegressionTest::getRegressionTest(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2); //Figures which dataset that contains more/less values pr keyword vector.
std::cout << "Comparing " << timeVec1.size() << " steps." << std::endl;
int ivar = 0;
if((! this->allowDifferentNumberOfKeywords) &&
(stringlist_get_size(keysShort) != stringlist_get_size(keysLong)))
{
int missing_count = 0;
std::cout << "Keywords missing from one case: " << std::endl;
for (int i=0; i < stringlist_get_size( keysLong); i++) {
const char * key = stringlist_iget( keysLong , i );
if (!stringlist_contains( keysShort , key)) {
std::cout << key << " ";
missing_count++;
if ((missing_count % 8) == 0)
std::cout << std::endl;
}
}
std::cout << std::endl;
HANDLE_ERROR(std::runtime_error, "Different amount of keywords in the two summary files.");
}
if(printKeyword){
printKeywords();
}
//Iterates over all keywords from the restricted file, use iterator "ivar". Searches for a match in the file with more keywords, use the iterator "jvar".
bool throwAtEnd = false;
while(ivar < stringlist_get_size(keysShort)){
const char* keyword = stringlist_iget(keysShort, ivar);
std::string keywordString(keyword);
for (int jvar = 0; jvar < stringlist_get_size(keysLong); jvar++){
if (strcmp(keyword, stringlist_iget(keysLong, jvar)) == 0){ //When the keywords are equal, proceed in comparing summary files.
if (isRestartFile && keywordString.substr(3,1)=="T"){
break;
}
throwAtEnd |= !checkForKeyword(timeVec1, timeVec2, keyword);
break;
}
//will only enter here if no keyword match
if(jvar == stringlist_get_size(keysLong)-1){
std::cout << "Could not find keyword: " << stringlist_iget(keysShort, ivar) << std::endl;
OPM_THROW(std::runtime_error, "No match on keyword");
}
}
ivar++;
}
if (analysis) {
std::cout << deviations.size() << " summary keyword"
<< (deviations.size() > 1 ? "s":"") << " exhibit failures" << std::endl;
size_t len = ecl_sum_get_data_length(ecl_sum1);
for (const auto& iter : deviations) {
std::cout << "\t" << iter.first << std::endl;
std::cout << "\t\tFails for " << iter.second.size() << " / " << len << " steps." << std::endl;
std::cout.precision(7);
double absErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.abs < b.abs;
})->abs;
double relErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.rel < b.rel;
})->rel;
std::cout << "\t\tLargest absolute error: "
<< std::scientific << absErr << std::endl;
std::cout << "\t\tLargest relative error: "
<< std::scientific << relErr << std::endl;
}
}
if (throwAtEnd)
OPM_THROW(std::runtime_error, "Regression test failed.");
else if (deviations.empty())
std::cout << "Regression test succeeded." << std::endl;
}
void SummaryRegressionTest::getRegressionTest(const char* keyword){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
setDataSets(timeVec1, timeVec2); //Figures which dataset that contains more/less values pr keyword vector.
std::string keywordString(keyword);
if(stringlist_contains(keysShort,keyword) && stringlist_contains(keysLong, keyword)){
if (isRestartFile && keywordString.substr(3,1)=="T"){
return;
}
if (checkForKeyword(timeVec1, timeVec2, keyword))
std::cout << "Regression test succeeded." << std::endl;
else
OPM_THROW(std::runtime_error, "Regression test failed");
return;
}
std::cout << "The keyword suggested, " << keyword << ", is not supported by one or both of the summary files. Please use a different keyword." << std::endl;
OPM_THROW(std::runtime_error, "Input keyword from user does not exist in/is not common for the two summary files.");
}
bool SummaryRegressionTest::checkDeviation(Deviation deviation, const char* keyword, int refIndex, int checkIndex){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel > relTol && deviation.abs > absTol){
if (analysis) {
deviations[keyword].push_back(deviation);
} else {
std::cout << "For keyword " << keyword << std::endl;
std::cout << "(days, reference value) and (days, check value) = (" << (*referenceVec)[refIndex] << ", " << (*referenceDataVec)[refIndex]
<< ") and (" << (*checkVec)[checkIndex-1] << ", " << (*checkDataVec)[checkIndex-1] << ")\n";
// -1 in [checkIndex -1] because checkIndex is updated after leaving getDeviation function
std::cout << "The absolute deviation is " << deviation.abs << ". The tolerance limit is " << absTol << std::endl;
std::cout << "The relative deviation is " << deviation.rel << ". The tolerance limit is " << relTol << std::endl;
HANDLE_ERROR(std::runtime_error, "Deviation exceed the limit.");
}
return false;
}
return true;
}
bool SummaryRegressionTest::checkForKeyword(std::vector<double>& timeVec1, std::vector<double>& timeVec2, const char* keyword){
std::vector<double> dataVec1, dataVec2;
getDataVecs(dataVec1,dataVec2,keyword);
chooseReference(timeVec1, timeVec2,dataVec1,dataVec2);
return startTest(keyword);
}
bool SummaryRegressionTest::startTest(const char* keyword){
size_t jvar = 0;
Deviation deviation;
bool result = true;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
getDeviation(ivar, jvar, deviation);//Reads from the protected member variables in the super class.
result = checkDeviation(deviation, keyword,ivar, jvar);
}
return result;
}
void SummaryRegressionTest::setAllowDifferentNumberOfKeywords(const bool allow)
{
this->allowDifferentNumberOfKeywords = allow;
}

View File

@@ -0,0 +1,87 @@
/*
Copyright 2016 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 SUMMARYREGRESSIONTEST_HPP
#define SUMMARYREGRESSIONTEST_HPP
#include "summaryComparator.hpp"
//! \details The class inherits from the SummaryComparator class, which takes care of all file reading. \n The RegressionTest class compares the values from the two different files and throws exceptions when the deviation is unsatisfying.
class SummaryRegressionTest: public SummaryComparator {
private:
//! \brief Gathers the correct data for comparison for a specific keyword
//! \param[in] timeVec1 The time steps of file 1.
//! \param[in] timeVec2 The time steps of file 2.
//! \param[in] keyword The keyword of interest
//! \details The function prepares a regression test by gathering the data, stroing it into two vectors, \n deciding which is to be used as a reference/basis and calling the test function.
//! \return True if check passed, false otherwise.
bool checkForKeyword(std::vector<double>& timeVec1, std::vector<double>& timeVec2, const char* keyword);
//! \brief The regression test
//! \param[in] keyword The keyword common for both the files. The vectors associated with the keyword are used for comparison.
//! \details Start test uses the private member variables, pointers of std::vector<double> type, which are set to point to the correct vectors in SummaryComparison::chooseReference(...). \n The function iterates over the referenceVev/basis and for each iteration it calculates the deviation with SummaryComparison::getDeviation(..) and stors it in a Deviation struct. \n SummaryComparison::getDeviation takes the int jvar as an reference input, and using it as an iterative index for the values which are to be compared with the basis. \n Thus, by updating the jvar variable every time a deviation is calculated, one keep track jvar and do not have to iterate over already checked values.
bool startTest(const char* keyword);
//! \brief Caluculates a deviation, throws exceptions and writes and error message.
//! \param[in] deviation Deviation struct
//! \param[in] keyword The keyword that the data that are being compared belongs to.
//! \param[in] refIndex The report step of which the deviation originates from in #referenceDataVec.
//! \param[in] checkIndex The report step of which the deviation originates from in #checkDataVec.
//! \details The function checks the values of the Deviation struct against the absolute and relative tolerance, which are private member values of the super class. \n When comparing against the relative tolerance an additional term is added, the absolute deviation has to be greater than 1e-6 for the function to throw an exception. \n When the deviations are too great, the function writes out which keyword, and at what report step the deviation is too great before optionally throwing an exception.
//! \return True if check passed, false otherwise.
bool checkDeviation(Deviation deviation, const char* keyword, int refIndex, int checkIndex);
bool isRestartFile = false; //!< Private member variable, when true the files that are being compared is a restart file vs a normal file
/// Whether or not to require that the two files have the same
/// number of keywords. Throw exception if not.
/// Default value: false (don't allow different number of keywords).
bool allowDifferentNumberOfKeywords = false;
public:
//! \brief Constructor, creates an object of RefressionTest class.
//! \param[in] basename1 Path to file1 without extension.
//! \param[in] basename1 Path to file2 without extension.
//! \param[in] relativeTol The relative tolerance which is to be used in the test.
//! \param[in] absoluteTol The absolute tolerance which is to be used in the test.
//! \details The constructor calls the constructor of the super class.
SummaryRegressionTest(const std::string& basename1,
const std::string& basename2,
double relativeTol, double absoluteTol) :
SummaryComparator(basename1, basename2, relativeTol, absoluteTol) {}
//! \details The function executes a regression test for all the keywords. If the two files do not match in amount of keywords, an exception is thrown.
void getRegressionTest();
//! \details The function executes a regression test for one specific keyword. If one or both of the files do not have the keyword, an exception is thrown.
void getRegressionTest(const char* keyword);///< Regression test for a certain keyword of the files.
//! \brief This function sets the private member variable isRestartFiles
//! \param[in] boolean Boolean value
void setIsRestartFile(bool boolean){this->isRestartFile = boolean;}
/// \brief Dynamically control whether or not to require equal
/// number of keywords (vectors) in the two result sets.
///
/// \param[in] allow Whether or not to allow different number of
/// summary keywords in the two result sets.
void setAllowDifferentNumberOfKeywords(const bool allow);
};
#endif

View File

@@ -61,6 +61,7 @@ namespace Opm { namespace RestartIO { namespace Helpers {
const std::vector<std::string>& restart_field_keys,
const std::map<std::string, size_t>& groupKeyToIndex,
const std::map<std::string, size_t>& fieldKeyToIndex,
const bool ecl_compatible_rst,
const std::size_t simStep,
const Opm::SummaryState& sumState,
const std::vector<int>& inteHead);

View File

@@ -52,6 +52,7 @@ namespace Opm { namespace RestartIO { namespace Helpers {
void captureDynamicWellData(const Opm::Schedule& sched,
const std::size_t sim_step,
const bool ecl_compatible_rst,
const Opm::data::WellRates& xw,
const Opm::SummaryState& smry);

View File

@@ -1,5 +1,4 @@
/*
Copyright 2019 Equinor ASA.
Copyright 2016, 2017 Statoil ASA.
This file is part of the Open Porous Media Project (OPM).
@@ -28,49 +27,6 @@ namespace Opm { namespace RestartIO {
class LogiHEAD
{
public:
/// Key characteristics of simulation run's PVT model.
struct PVTModel
{
/// Whether or not simulation run uses a live oil model (with
/// dissolved gas).
bool isLiveOil { false };
/// Whether or not simulation run uses a wet gas model (with
/// vaporised oil).
bool isWetGas { false };
/// Whether or not simulation run uses a constant
/// compressibility oil model (keyword PVCDO).
bool constComprOil { false };
};
/// Key characteristics of simulation model's saturation functions.
struct SatfuncFlags
{
/// Whether or not simulation run uses directionally dependent
/// relative permeability.
bool useDirectionalRelPerm { false };
/// Whether or not simulation run uses reversible relative
/// permeability functions.
bool useReversibleRelPerm { true };
/// Whether or not simulation run uses end-point scaling.
bool useEndScale { false };
/// Whether or not simulation run uses directionally dependent
/// end-point scaling.
bool useDirectionalEPS { false };
/// Whether or not simulation run uses reversible end-point
/// scaling.
bool useReversibleEPS { true };
/// Whether or not simulation run activates the alternative
/// (three-point) end-point scaling feature.
bool useAlternateEPS { false };
};
LogiHEAD();
~LogiHEAD() = default;
@@ -86,21 +42,6 @@ namespace Opm { namespace RestartIO {
const bool enableHyster
);
/// Assign PVT model characteristics.
///
/// \param[in] pvt Current run's PVT model characteristics.
///
/// \return \code *this \endcode.
LogiHEAD& pvtModel(const PVTModel& pvt);
/// Assign saturation function characteristics.
///
/// \param[in] satfunc Current run's saturation function
/// characteristics.
///
/// \return \code *this \endcode.
LogiHEAD& saturationFunction(const SatfuncFlags& satfunc);
const std::vector<bool>& data() const
{
return this->data_;

View File

@@ -43,52 +43,37 @@ namespace Opm {
namespace out {
class Summary {
public:
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const std::string& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const char* basename );
public:
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const std::string& );
Summary( const EclipseState&, const SummaryConfig&, const EclipseGrid&, const Schedule&, const char* basename );
void add_timestep(int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {});
void add_timestep(int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {});
void write();
void eval(SummaryState& summary_state,
int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells&,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values = {},
const std::map<std::pair<std::string, int>, double>& block_values = {}) const;
~Summary();
const SummaryState& get_restart_vectors() const;
void reset_cumulative_quantities(const SummaryState& rstrt);
~Summary();
private:
class keyword_handlers;
const SummaryState& get_restart_vectors() const;
void reset_cumulative_quantities(const SummaryState& rstrt);
void write() const;
private:
void internal_store(const SummaryState& summary_state, int report_step, double seconds_elapsed);
class keyword_handlers;
const EclipseGrid& grid;
out::RegionCache regionCache;
ERT::ert_unique_ptr< ecl_sum_type, ecl_sum_free > ecl_sum;
std::unique_ptr< keyword_handlers > handlers;
double prev_time_elapsed = 0;
SummaryState prev_state;
const EclipseGrid& grid;
out::RegionCache regionCache;
ERT::ert_unique_ptr< ecl_sum_type, ecl_sum_free > ecl_sum;
std::unique_ptr< keyword_handlers > handlers;
double prev_time_elapsed = 0;
SummaryState prev_state;
};
}

View File

@@ -1,48 +0,0 @@
/*
Copyright (c) 2019 Equinor 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_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP
#define OPM_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP
#include <vector>
namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems {
// This is a subset of the items in src/opm/output/eclipse/LogiHEAD.cpp .
// Promote items from that list to this in order to make them public.
enum logihead : std::vector<bool>::size_type {
IsLiveOil = 0, // Oil phase w/dissolved gas
IsWetGas = 1, // Gas phase w/vaporised oil
DirKr = 2, // Directional relative permeability
E100RevKr = 3, // Reversible rel. perm. (E100)
E100Radial = 4, // Radial model (E100)
E300Radial = 3, // Radial model (E300, others)
E300RevKr = 4, // Reversible rel. perm. (E300, others)
Hyster = 6, // Enable hysteresis
DualPoro = 14, // Enable dual porosity
EndScale = 16, // Enable end-point scaling
DirEPS = 17, // Directional end-point scaling
RevEPS = 18, // Reversible end-point scaling
AltEPS = 19, // Alternative (3-pt) end-point scaling
ConstCo = 38, // Constant oil compressibility (PVCDO)
HasMSWells = 75, // Whether or not model has MS Wells.
};
}}}} // Opm::RestartIO::Helpers::VectorItems
#endif // OPM_OUTPUT_ECLIPSE_VECTOR_LOGIHEAD_HPP

View File

@@ -64,6 +64,17 @@ namespace Opm { namespace RestartIO { namespace Helpers {
createLogiHead(const EclipseState& es);
std::vector<int> serialize_ICON(int lookup_step, // The integer index used to look up dynamic properties, e.g. the number of well.
int ncwmax, // Max number of completions per well, should be entry 17 from createInteHead.
int niconz, // Number of elements per completion in ICON, should be entry 32 from createInteHead.
const std::vector<const Well*>& sched_wells);
std::vector<double> serialize_SCON(int lookup_step, // The integer index used to look up dynamic properties, e.g. the number of well.
int ncwmax, // Max number of completions per well, should be entry 17 from createInteHead.
int nsconz, // Number of elements per completion in SCON, should be entry 33 from createInteHead.
const std::vector<const Well*>& sched_wells,
const UnitSystem& units);
}}} // Opm::RestartIO::Helpers
#endif // OPM_WRITE_RESTART_HELPERS_HPP

View File

@@ -27,7 +27,6 @@
#include <opm/parser/eclipse/Units/Dimension.hpp>
#include <opm/parser/eclipse/Utility/Typetools.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
class DeckOutput;
@@ -40,7 +39,6 @@ namespace Opm {
DeckItem( const std::string&, int, size_t size_hint = 8 );
DeckItem( const std::string&, double, size_t size_hint = 8 );
DeckItem( const std::string&, std::string, size_t size_hint = 8 );
DeckItem( const std::string&, UDAValue, size_t size_hint = 8 );
const std::string& name() const;
@@ -60,7 +58,6 @@ namespace Opm {
size_t size() const;
size_t out_size() const;
//template< typename T > T& get( size_t ) ;
template< typename T > const T& get( size_t ) const;
double getSIDouble( size_t ) const;
std::string getTrimmedString( size_t ) const;
@@ -68,15 +65,12 @@ namespace Opm {
template< typename T > const std::vector< T >& getData() const;
const std::vector< double >& getSIDoubleData() const;
void push_back( UDAValue );
void push_back( int );
void push_back( double );
void push_back( std::string );
void push_back( UDAValue, size_t );
void push_back( int, size_t );
void push_back( double, size_t );
void push_back( std::string, size_t );
void push_backDefault( UDAValue );
void push_backDefault( int );
void push_backDefault( double );
void push_backDefault( std::string );
@@ -113,10 +107,9 @@ namespace Opm {
bool operator!=(const DeckItem& other) const;
static bool to_bool(std::string string_value);
private:
mutable std::vector< double > dval;
std::vector< double > dval;
std::vector< int > ival;
std::vector< std::string > sval;
std::vector< UDAValue > uval;
type_tag type = type_tag::unknown;

View File

@@ -1,61 +0,0 @@
/*
Copyright 2019 Equinor 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 UDA_VALUE_HPP
#define UDA_VALUE_HPP
#include <stdexcept>
#include <vector>
#include <string>
#include <opm/parser/eclipse/Units/Dimension.hpp>
namespace Opm {
class UDAValue {
public:
UDAValue();
explicit UDAValue(double);
explicit UDAValue(const std::string&);
template<typename T>
T get() const;
template<typename T>
bool is() const;
void set_dim(const Dimension& dim) const;
const Dimension& get_dim() const;
bool operator==(const UDAValue& other) const;
bool operator!=(const UDAValue& other) const;
private:
bool numeric_value;
double double_value;
std::string string_value;
/* This 'mutable' modifier is a hack to avoid tampering with the overall
const-ness of the data in a deck item. */
mutable Dimension dim;
};
}
#endif

View File

@@ -89,29 +89,6 @@ class DynamicState {
std::fill_n( this->m_data.begin(), this->initial_range, initial );
}
std::vector<std::pair<std::size_t, T>> unique() {
if (this->m_data.empty())
return {};
auto& current_value = this->m_data[0];
std::size_t current_index = 0;
std::vector<std::pair<std::size_t, T>> result{{current_index, current_value}};
while (true) {
if (this->m_data[current_index] != current_value) {
current_value = this->m_data[current_index];
result.emplace_back(current_index, current_value);
}
current_index++;
if (current_index == this->m_data.size())
break;
}
return result;
}
/**
If the current value has been changed the method will
return true, otherwise it will return false.

View File

@@ -34,6 +34,7 @@ namespace Opm {
class TimeMap;
class Well;
namespace GroupInjection {
struct InjectionData {
@@ -128,7 +129,7 @@ namespace Opm {
bool hasWell(const std::string& wellName , size_t time_step) const;
const std::set< std::string >& getWells( size_t time_step ) const;
size_t numWells(size_t time_step) const;
void addWell(size_t time_step, const std::string& well_name);
void addWell(size_t time_step, const Well* well);
void delWell(size_t time_step, const std::string& wellName );
private:

View File

@@ -27,46 +27,39 @@
namespace Opm {
class GroupTree {
public:
struct group {
std::string name;
std::string parent;
public:
void update( const std::string& name);
void update( const std::string& name, const std::string& parent);
void updateSeqIndex( const std::string& name, const std::string& other_parent);
bool exists( const std::string& group ) const;
const std::string& parent( const std::string& name ) const;
std::vector< std::string > children( const std::string& parent ) const;
const std::map<std::string , size_t>& nameSeqIndMap() const;
const std::map<size_t, std::string >& seqIndNameMap() const;
bool operator==( const GroupTree& ) const;
bool operator!=( const GroupTree& ) const;
bool operator<( const group& rhs ) const;
bool operator==( const std::string& name ) const;
bool operator!=( const std::string& name ) const;
private:
struct group {
std::string name;
std::string parent;
bool operator<( const std::string& name ) const;
bool operator==( const group& rhs ) const;
bool operator!=( const group& rhs ) const;
};
bool operator<( const group& rhs ) const;
bool operator==( const std::string& name ) const;
bool operator!=( const std::string& name ) const;
void update( const std::string& name);
void update( const std::string& name, const std::string& parent);
void updateSeqIndex( const std::string& name, const std::string& other_parent);
bool exists( const std::string& group ) const;
const std::string& parent( const std::string& name ) const;
std::vector< std::string > children( const std::string& parent ) const;
const std::map<std::string , size_t>& nameSeqIndMap() const;
const std::map<size_t, std::string >& seqIndNameMap() const;
bool operator==( const GroupTree& ) const;
bool operator!=( const GroupTree& ) const;
std::vector<GroupTree::group>::const_iterator begin() const;
std::vector<GroupTree::group>::const_iterator end() const;
bool operator<( const std::string& name ) const;
bool operator==( const group& rhs ) const;
bool operator!=( const group& rhs ) const;
};
private:
std::vector< group >::iterator find( const std::string& );
friend bool operator<( const std::string&, const group& );
std::vector< group > groups = { group { "FIELD", "" } };
std::map<std::string , size_t> m_nameSeqIndMap;
std::map<size_t, std::string > m_seqIndNameMap;
std::vector< group > groups = { group { "FIELD", "" } };
friend bool operator<( const std::string&, const group& );
std::vector< group >::iterator find( const std::string& );
std::map<std::string , size_t> m_nameSeqIndMap;
std::map<size_t, std::string > m_seqIndNameMap;
};
std::ostream& operator<<(std::ostream& stream, const GroupTree& gt);
}
#endif /* GROUPTREE_HPP */

View File

@@ -34,12 +34,14 @@ namespace Opm {
public:
WellSegments() = default;
const std::string& wellName() const;
std::string wellName() const;
int numberBranch() const;
int size() const;
double depthTopSegment() const;
double lengthTopSegment() const;
double volumeTopSegment() const;
WellSegment::LengthDepthEnum lengthDepthType() const;
WellSegment::CompPressureDropEnum compPressureDrop() const;
WellSegment::MultiPhaseModelEnum multiPhaseModel() const;
@@ -54,17 +56,18 @@ namespace Opm {
const Segment& operator[](size_t idx) const;
void orderSegments();
void process(bool first_time);
void processABS();
void processINC(const bool first_time);
bool operator==( const WellSegments& ) const;
bool operator!=( const WellSegments& ) const;
private:
void processABS();
void processINC(const bool first_time);
// name of the well
std::string m_well_name;
// number of the branches
int m_number_branch;
// depth of the nodal point of the top segment
// it is taken as the BHP reference depth of the well
// BHP reference depth data from elsewhere will be ignored for multi-segmented wells
@@ -90,7 +93,6 @@ namespace Opm {
// storage index in the vector
std::map<int, int> segment_number_to_index;
};
std::ostream& operator<<( std::ostream&, const WellSegments& );
}
#endif

View File

@@ -40,7 +40,6 @@
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/Actions.hpp>
@@ -114,6 +113,7 @@ namespace Opm
size_t numWells() const;
size_t numWells(size_t timestep) const;
size_t getMaxNumConnectionsForWells(size_t timestep) const;
bool hasWell(const std::string& wellName) const;
std::vector<std::string> wellNames(const std::string& pattern, size_t timeStep, const std::vector<std::string>& matching_wells = {}) const;
@@ -121,14 +121,24 @@ namespace Opm
std::vector<std::string> wellNames(size_t timeStep) const;
std::vector<std::string> wellNames() const;
void updateWell(std::shared_ptr<Well2> well, size_t reportStep);
const Well2& getWell2(const std::string& wellName, size_t timeStep) const;
std::vector< const Well2* > getChildWells2(const std::string& group_name, size_t timeStep, GroupWellQueryMode query_mode) const;
const Well* getWell(const std::string& wellName) const;
std::vector< const Well* > getOpenWells(size_t timeStep) const;
std::vector< const Well* > getWells() const;
std::vector< const Well* > getWells(size_t timeStep) const;
std::vector< const Well* > getChildWells(const std::string& group_name, size_t timeStep, GroupWellQueryMode query_mode) const;
/*
The overload with a group name argument will return all
wells beneath that particular group; i.e.
getWells("FIELD",t);
is an inefficient way to get all the wells defined at time
't'.
*/
//std::vector< const Group& > getChildGroups(const std::string& group_name, size_t timeStep) const;
std::vector< const Group* > getChildGroups(const std::string& group_name, size_t timeStep) const;
std::vector< const Well* > getWells(const std::string& group, size_t timeStep) const;
std::vector< const Well* > getChildWells(const std::string& group_name, size_t timeStep) const;
const OilVaporizationProperties& getOilVaporizationProperties(size_t timestep) const;
const WellTestConfig& wtestConfig(size_t timestep) const;
@@ -138,7 +148,6 @@ namespace Opm
void evalAction(const SummaryState& summary_state, size_t timeStep);
const GroupTree& getGroupTree(size_t t) const;
std::vector< const Group* > getChildGroups(const std::string& group_name, size_t timeStep) const;
size_t numGroups() const;
size_t numGroups(size_t timeStep) const;
bool hasGroup(const std::string& groupName) const;
@@ -171,7 +180,6 @@ namespace Opm
TimeMap m_timeMap;
OrderedMap< std::string, Well > m_wells;
OrderedMap< std::string, Group > m_groups;
OrderedMap< std::string, DynamicState<std::shared_ptr<Well2>>> wells_static;
DynamicState< GroupTree > m_rootGroupTree;
DynamicState< OilVaporizationProperties > m_oilvaporizationproperties;
Events m_events;
@@ -193,8 +201,8 @@ namespace Opm
std::vector< Group* > getGroups(const std::string& groupNamePattern);
std::map<std::string,Events> well_events;
bool updateWellStatus( const std::string& well, size_t reportStep , WellCommon::StatusEnum status);
void addWellToGroup( Group& newGroup , const std::string& wellName , size_t timeStep);
void updateWellStatus( Well& well, size_t reportStep , WellCommon::StatusEnum status);
void addWellToGroup( Group& newGroup , Well& well , size_t timeStep);
void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid,
const Eclipse3DProperties& eclipseProperties);
bool handleGroupFromWELSPECS(const std::string& groupName, GroupTree& newTree) const;
@@ -204,6 +212,7 @@ namespace Opm
void handleWLIST(const DeckKeyword& keyword, size_t currentStep);
void handleCOMPORD(const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& compordKeyword, size_t currentStep);
void handleWELSPECS( const SCHEDULESection&, size_t, size_t );
void handleWCONProducer( const DeckKeyword& keyword, size_t currentStep, bool isPredictionMode, const ParseContext& parseContext, ErrorGuard& errors);
void handleWCONHIST( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleWCONPROD( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleWGRUPCON( const DeckKeyword& keyword, size_t currentStep);
@@ -255,7 +264,9 @@ namespace Opm
const UnitSystem& unit_system,
std::vector<std::pair<const DeckKeyword*, size_t > >& rftProperties);
void addWellEvent(const std::string& well, ScheduleEvents::Events event, size_t reportStep);
#ifdef WELL_TEST
static bool convertEclipseStringToBool(const std::string& eclipseString);
#ifdef CHECK_WELLS
bool checkWells(const ParseContext& parseContext, ErrorGuard& errors) const;
#endif
};

View File

@@ -310,11 +310,6 @@ namespace Opm {
}
enum class GroupWellQueryMode {
Immediate,
Recursive
};
}
#endif

View File

@@ -66,30 +66,20 @@ class SummaryState {
public:
typedef std::unordered_map<std::string, double>::const_iterator const_iterator;
/*
The set() function has to be retained temporarily to support updating of
cumulatives from restart files.
*/
void set(const std::string& key, double value);
bool has(const std::string& key) const;
bool has_well_var(const std::string& well, const std::string& var) const;
void update(const std::string& key, double value);
void update(const ecl::smspec_node& node, double value);
void update_well_var(const std::string& well, const std::string& var, double value);
void update_elapsed(double delta);
double get(const std::string&) const;
bool has(const std::string& key) const;
void add(const std::string& key, double value);
void add(const ecl::smspec_node& node, double value);
void add_well_var(const std::string& well, const std::string& var, double value);
bool has_well_var(const std::string& well, const std::string& var) const;
double get_well_var(const std::string& well, const std::string& var) const;
double get_elapsed() const;
std::vector<std::string> wells() const;
std::vector<std::string> wells(const std::string& var) const;
const_iterator begin() const;
const_iterator end() const;
private:
double elapsed;
std::unordered_map<std::string,double> values;
// The first key is the variable and the second key is the well.

View File

@@ -50,10 +50,11 @@ namespace Opm {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId);
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId
);
bool attachedToSegment() const;
@@ -78,16 +79,15 @@ namespace Opm {
void setComplnum(int compnum);
void scaleWellPi(double wellPi);
void updateSegment(int segment_number, double center_depth, std::size_t seqIndex);
const std::size_t& getSeqIndex() const;
const bool& getDefaultSatTabId() const;
const std::size_t& getCompSegSeqIndex() const;
void setCompSegSeqIndex(std::size_t index);
void setDefaultSatTabId(bool id);
const double& getSegDistStart() const;
const double& getSegDistEnd() const;
void setSegDistStart(const double& distStart);
void setSegDistEnd(const double& distEnd);
std::string str() const;
const std::size_t& getSeqIndex() const;
const bool& getDefaultSatTabId() const;
const std::size_t& getCompSegSeqIndex() const;
void setCompSegSeqIndex(std::size_t index);
void setDefaultSatTabId(bool id);
const double& getSegDistStart() const;
const double& getSegDistEnd() const;
void setSegDistStart(const double& distStart);
void setSegDistEnd(const double& distEnd);
bool operator==( const Connection& ) const;
bool operator!=( const Connection& ) const;
@@ -104,11 +104,11 @@ namespace Opm {
double m_skin_factor;
std::array<int,3> ijk;
std::size_t m_seqIndex;
double m_segDistStart;
double m_segDistEnd;
bool m_defaultSatTabId;
std::size_t m_compSeg_seqIndex=0;
std::size_t m_seqIndex;
double m_segDistStart;
double m_segDistEnd;
bool m_defaultSatTabId;
std::size_t m_compSeg_seqIndex=0;
// related segment number
// -1 means the completion is not related to segment

View File

@@ -96,7 +96,6 @@ namespace Opm {
void switchToInjector( size_t timeStep);
void switchToProducer( size_t timeStep);
bool setProducer(size_t timeStep, bool producer);
bool isProducer(size_t timeStep) const;
bool isInjector(size_t timeStep) const;
void addWELSPECS(const DeckRecord& deckRecord);

View File

@@ -1,169 +0,0 @@
/*
Copyright 2019 Equinor 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 WELL2_HPP
#define WELL2_HPP
#include <string>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/WellSegments.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTracerProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.hpp>
namespace Opm {
class DeckRecord;
class EclipseGrid;
class DeckKeyword;
struct WellGuideRate {
bool available;
double guide_rate;
GuideRate::GuideRatePhaseEnum guide_phase;
double scale_factor;
};
class Well2 {
public:
Well2(const std::string& wname,
const std::string& gname,
std::size_t init_step,
std::size_t insert_index,
int headI,
int headJ,
double ref_depth,
Phase phase,
WellProducer::ControlModeEnum whistctl_cmode,
WellCompletion::CompletionOrderEnum ordering);
bool isMultiSegment() const;
bool isAvailableForGroupControl() const;
double getGuideRate() const;
GuideRate::GuideRatePhaseEnum getGuideRatePhase() const;
double getGuideRateScalingFactor() const;
std::size_t firstTimeStep() const;
bool canOpen() const;
bool isProducer() const;
size_t seqIndex() const;
bool getAutomaticShutIn() const;
bool getAllowCrossFlow() const;
const std::string& name() const;
int getHeadI() const;
int getHeadJ() const;
double getRefDepth() const;
double getDrainageRadius() const;
double getEfficiencyFactor() const;
WellCompletion::CompletionOrderEnum getWellConnectionOrdering() const;
const WellProductionProperties& getProductionProperties() const;
const WellInjectionProperties& getInjectionProperties() const;
const WellEconProductionLimits& getEconLimits() const;
const WellPolymerProperties& getPolymerProperties() const;
const WellTracerProperties& getTracerProperties() const;
const WellConnections& getConnections() const;
const WellSegments& getSegments() const;
double getSolventFraction() const;
WellCommon::StatusEnum getStatus() const;
const std::string& groupName() const;
Phase getPreferredPhase() const;
/*
The getCompletions() function will return a map:
{
1 : [Connection, Connection],
2 : [Connection, Connection, Connecton],
3 : [Connection],
4 : [Connection]
}
The integer ID's correspond to the COMPLETION id given by the COMPLUMP
keyword.
*/
std::map<int, std::vector<Connection>> getCompletions() const;
bool updateAutoShutin(bool auto_shutin);
bool updateCrossFlow(bool allow_cross_flow);
bool updateHead(int I, int J);
bool updateRefDepth(double ref_dpeth);
bool updateDrainageRadius(double drainage_radius);
bool updateConnections(const std::shared_ptr<WellConnections> connections);
bool updateStatus(WellCommon::StatusEnum status);
bool updateGroup(const std::string& group);
bool updateProducer(bool is_producer);
bool updateWellGuideRate(bool available, double guide_rate, GuideRate::GuideRatePhaseEnum guide_phase, double scale_factor);
bool updateWellGuideRate(double guide_rate);
bool updateEfficiencyFactor(double efficiency_factor);
bool updateSolventFraction(double solvent_fraction);
bool updateTracer(std::shared_ptr<WellTracerProperties> tracer_properties);
bool updatePolymerProperties(std::shared_ptr<WellPolymerProperties> polymer_properties);
bool updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits);
bool updateProduction(std::shared_ptr<WellProductionProperties> production);
bool updateInjection(std::shared_ptr<WellInjectionProperties> injection);
void switchToProducer();
void switchToInjector();
bool handleWELSEGS(const DeckKeyword& keyword);
bool handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid);
bool handleWELOPEN(const DeckRecord& record, WellCompletion::StateEnum status);
bool handleCOMPLUMP(const DeckRecord& record);
bool handleWPIMULT(const DeckRecord& record);
void filterConnections(const EclipseGrid& grid);
private:
std::string wname;
std::string group_name;
std::size_t init_step;
std::size_t insert_index;
int headI;
int headJ;
double ref_depth;
Phase phase;
WellCompletion::CompletionOrderEnum ordering;
WellCommon::StatusEnum status;
double drainage_radius;
bool allow_cross_flow;
bool automatic_shutin;
bool producer;
WellGuideRate guide_rate;
double efficiency_factor;
double solvent_fraction;
std::shared_ptr<const WellEconProductionLimits> econ_limits;
std::shared_ptr<const WellPolymerProperties> polymer_properties;
std::shared_ptr<const WellTracerProperties> tracer_properties;
std::shared_ptr<WellConnections> connections; // The WellConnections object can not be const because of the filterConnections method - would be beneficial to rewrite to enable const
std::shared_ptr<const WellProductionProperties> production;
std::shared_ptr<const WellInjectionProperties> injection;
std::shared_ptr<const WellSegments> segments;
};
}
#endif

View File

@@ -28,6 +28,7 @@ namespace Opm {
class Eclipse3DProperties;
class WellConnections {
public:
WellConnections() = default;
WellConnections(int headI, int headJ);
// cppcheck-suppress noExplicitConstructor
WellConnections(const WellConnections& src, const EclipseGrid& grid);

View File

@@ -23,8 +23,6 @@
namespace Opm {
class DeckRecord;
struct WellPolymerProperties {
double m_polymerConcentration;
double m_saltConcentration;
@@ -35,9 +33,6 @@ namespace Opm {
bool operator==(const WellPolymerProperties& other) const;
bool operator!=(const WellPolymerProperties& other) const;
WellPolymerProperties();
void handleWPOLYMER(const DeckRecord& record);
void handleWPMITAB(const DeckRecord& record);
void handleWSKPTAB(const DeckRecord& record);
};
}

View File

@@ -75,7 +75,6 @@ namespace Opm {
void handleWCONHIST( const DeckRecord& record);
void handleWELTARG(WellTarget::ControlModeEnum cmode, double newValue, double siFactorG, double siFactorL, double siFactorP);
void resetDefaultBHPLimit();
void clearControls();
private:
int m_productionControls = 0;

View File

@@ -331,7 +331,6 @@ namespace Opm {
const static std::string RPT_UNKNOWN_MNEMONIC;
const static std::string SCHEDULE_WELL_ERROR;
/*
The SIMULATOR_KEYWORD_ errormodes are for the situation where the

View File

@@ -80,7 +80,6 @@ namespace Opm {
size_t numDimensions() const;
const std::string& name() const;
item_size sizeType() const;
type_tag dataType() const;
void setSizeType(item_size size_type);
std::string getDescription() const;
bool scalar() const;
@@ -109,7 +108,6 @@ namespace Opm {
double dval;
int ival;
std::string sval;
UDAValue uval;
std::vector< std::string > dimensions;
std::string m_name;

View File

@@ -26,7 +26,7 @@ namespace Opm {
class Dimension {
public:
Dimension();
Dimension() = default;
Dimension(const std::string& name, double SIfactor, double SIoffset = 0.0);
double getSIScaling() const;

View File

@@ -1,15 +1,16 @@
#ifndef OPM_TYPETOOLS_HPP
#define OPM_TYPETOOLS_HPP
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
enum class type_tag {
/* for python interop as well as queries, must be manually synchronised
* with cdeck_item.cc and opm/deck/item_type_enum.py
*/
unknown = 0,
integer = 1,
string = 2,
fdouble = 3,
uda = 4,
};
inline std::string tag_name( type_tag x ) {
@@ -17,30 +18,23 @@ inline std::string tag_name( type_tag x ) {
case type_tag::integer: return "int";
case type_tag::string: return "std::string";
case type_tag::fdouble: return "double";
case type_tag::uda: return "UDAValue";
case type_tag::unknown: return "unknown";
}
return "unknown";
}
template< typename T > type_tag get_type();
template<> inline type_tag get_type< int >() {
return type_tag::integer;
}
template<> inline type_tag get_type< double >() {
return type_tag::fdouble;
}
template<> inline type_tag get_type< std::string >() {
return type_tag::string;
}
template<> inline type_tag get_type<UDAValue>() {
return type_tag::uda;
}
}
#endif //OPM_TYPETOOLS_HPP

View File

@@ -5,7 +5,7 @@
%define tag final
Name: opm-common
Version: 2018.10
Version: 2019.04
Release: 0
Summary: Open Porous Media - common helpers and buildsystem
License: GPL-3.0

View File

@@ -178,7 +178,7 @@ namespace {
// location nwgmax +1 in the iGrp array
const auto childGroups = sched.getChildGroups(group.name(), simStep);
const auto childWells = sched.getChildWells(group.name(), simStep, Opm::GroupWellQueryMode::Immediate);
const auto childWells = sched.getChildWells(group.name(), simStep);
const auto groupMapNameIndex = currentGroupMapNameIndex(sched, simStep, inteHead);
const auto mapIndexGroup = currentGroupMapIndexGroup(sched, simStep, inteHead);
if ((childGroups.size() != 0) && (childWells.size()!=0))
@@ -369,6 +369,31 @@ namespace {
};
}
std::vector<std::string>
filter_cumulative(const bool ecl_compatible_rst,
const std::vector<std::string>& keys)
{
if (ecl_compatible_rst) {
// User wants ECLIPSE-compatible output. Write all vectors.
return keys;
}
auto ret = std::vector<std::string>{};
ret.reserve(keys.size());
for (const auto& key : keys) {
if ((key[3] == 'T') && ((key[2] == 'I') || (key[2] == 'P'))) {
// Don't write cumulative quantities in case of OPM
// extended restart files.
continue;
}
ret.push_back(key);
}
return ret;
}
// here define the dynamic group quantities to be written to the restart file
template <class XGrpArray>
void dynamicContrib(const std::vector<std::string>& restart_group_keys,
@@ -377,6 +402,7 @@ namespace {
const std::map<std::string, size_t>& fieldKeyToIndex,
const Opm::Group& group,
const Opm::SummaryState& sumState,
const bool ecl_compatible_rst,
XGrpArray& xGrp)
{
std::string groupName = group.name();
@@ -385,7 +411,7 @@ namespace {
const std::map<std::string, size_t>& keyToIndex = (groupName == "FIELD")
? fieldKeyToIndex : groupKeyToIndex;
for (const auto& key : keys) {
for (const auto key : filter_cumulative(ecl_compatible_rst,keys)) {
std::string compKey = (groupName == "FIELD")
? key : key + ":" + groupName;
@@ -491,6 +517,7 @@ captureDeclaredGroupData(const Opm::Schedule& sched,
const std::vector<std::string>& restart_field_keys,
const std::map<std::string, size_t>& groupKeyToIndex,
const std::map<std::string, size_t>& fieldKeyToIndex,
const bool ecl_compatible_rst,
const std::size_t simStep,
const Opm::SummaryState& sumState,
const std::vector<int>& inteHead)
@@ -527,14 +554,14 @@ captureDeclaredGroupData(const Opm::Schedule& sched,
// Define Dynamic Contributions to XGrp Array.
groupLoop(curGroups, [&restart_group_keys, &restart_field_keys,
&groupKeyToIndex, &fieldKeyToIndex,
&sumState, this]
ecl_compatible_rst, &sumState, this]
(const Group& group, const std::size_t groupID) -> void
{
auto xg = this->xGroup_[groupID];
XGrp::dynamicContrib(restart_group_keys, restart_field_keys,
groupKeyToIndex, fieldKeyToIndex, group,
sumState, xg);
sumState, ecl_compatible_rst, xg);
});
// Define Static Contributions to ZGrp Array.

View File

@@ -613,6 +613,7 @@ namespace {
template <class XWellArray>
void assignProducer(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -637,39 +638,47 @@ namespace {
xWell[Ix::WatCut] = get("WWCT");
xWell[Ix::GORatio] = get("WGOR");
xWell[Ix::OilPrTotal] = get("WOPT");
xWell[Ix::WatPrTotal] = get("WWPT");
xWell[Ix::GasPrTotal] = get("WGPT");
xWell[Ix::VoidPrTotal] = get("WVPT");
if (ecl_compatible_rst) {
xWell[Ix::OilPrTotal] = get("WOPT");
xWell[Ix::WatPrTotal] = get("WWPT");
xWell[Ix::GasPrTotal] = get("WGPT");
xWell[Ix::VoidPrTotal] = get("WVPT");
}
// Not fully characterised.
xWell[Ix::item37] = xWell[Ix::WatPrRate];
xWell[Ix::item38] = xWell[Ix::GasPrRate];
xWell[Ix::HistOilPrTotal] = get("WOPTH");
xWell[Ix::HistWatPrTotal] = get("WWPTH");
xWell[Ix::HistGasPrTotal] = get("WGPTH");
if (ecl_compatible_rst) {
xWell[Ix::HistOilPrTotal] = get("WOPTH");
xWell[Ix::HistWatPrTotal] = get("WWPTH");
xWell[Ix::HistGasPrTotal] = get("WGPTH");
}
}
template <class GetSummaryVector, class XWellArray>
void assignCommonInjector(GetSummaryVector& get,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
xWell[Ix::FlowBHP] = get("WBHP");
// Note: Assign both water and gas cumulatives to support
// case of well alternating between injecting water and gas.
xWell[Ix::WatInjTotal] = get("WWIT");
xWell[Ix::GasInjTotal] = get("WGIT");
xWell[Ix::HistWatInjTotal] = get("WWITH");
xWell[Ix::HistGasInjTotal] = get("WGITH");
if (ecl_compatible_rst) {
// Note: Assign both water and gas cumulatives to support
// case of well alternating between injecting water and gas.
xWell[Ix::WatInjTotal] = get("WWIT");
xWell[Ix::GasInjTotal] = get("WGIT");
xWell[Ix::HistWatInjTotal] = get("WWITH");
xWell[Ix::HistGasInjTotal] = get("WGITH");
}
}
template <class XWellArray>
void assignWaterInjector(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -681,7 +690,7 @@ namespace {
return smry.has(key) ? smry.get(key) : 0.0;
};
assignCommonInjector(get, xWell);
assignCommonInjector(get, ecl_compatible_rst, xWell);
// Injection rates reported as negative.
xWell[Ix::WatPrRate] = -get("WWIR");
@@ -696,6 +705,7 @@ namespace {
template <class XWellArray>
void assignGasInjector(const std::string& well,
const ::Opm::SummaryState& smry,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::XWell::index;
@@ -707,7 +717,7 @@ namespace {
return smry.has(key) ? smry.get(key) : 0.0;
};
assignCommonInjector(get, xWell);
assignCommonInjector(get, ecl_compatible_rst, xWell);
// Injection rates reported as negative production rates.
xWell[Ix::GasPrRate] = -get("WGIR");
@@ -731,10 +741,11 @@ namespace {
void dynamicContrib(const ::Opm::Well& well,
const ::Opm::SummaryState& smry,
const std::size_t sim_step,
const bool ecl_compatible_rst,
XWellArray& xWell)
{
if (well.isProducer(sim_step)) {
assignProducer(well.name(), smry, xWell);
assignProducer(well.name(), smry, ecl_compatible_rst, xWell);
}
else if (well.isInjector(sim_step)) {
using IType = ::Opm::WellInjector::TypeEnum;
@@ -748,16 +759,16 @@ namespace {
break;
case IType::WATER:
assignWaterInjector(well.name(), smry, xWell);
assignWaterInjector(well.name(), smry, ecl_compatible_rst, xWell);
break;
case IType::GAS:
assignGasInjector(well.name(), smry, xWell);
assignGasInjector(well.name(), smry, ecl_compatible_rst, xWell);
break;
case IType::MULTI:
assignWaterInjector(well.name(), smry, xWell);
assignGasInjector (well.name(), smry, xWell);
assignWaterInjector(well.name(), smry, ecl_compatible_rst, xWell);
assignGasInjector (well.name(), smry, ecl_compatible_rst, xWell);
break;
}
}
@@ -872,6 +883,7 @@ void
Opm::RestartIO::Helpers::AggregateWellData::
captureDynamicWellData(const Schedule& sched,
const std::size_t sim_step,
const bool ecl_compatible_rst,
const Opm::data::WellRates& xw,
const ::Opm::SummaryState& smry)
{
@@ -893,11 +905,11 @@ captureDynamicWellData(const Schedule& sched,
});
// Dynamic contributions to XWEL array.
wellLoop(wells, [this, sim_step, &smry]
wellLoop(wells, [this, sim_step, ecl_compatible_rst, &smry]
(const Well& well, const std::size_t wellID) -> void
{
auto xw = this->xWell_[wellID];
XWell::dynamicContrib(well, smry, sim_step, xw);
XWell::dynamicContrib(well, smry, sim_step, ecl_compatible_rst, xw);
});
}

View File

@@ -35,28 +35,12 @@ Opm::RestartIO::Helpers::
createLogiHead(const EclipseState& es)
{
const auto& rspec = es.runspec();
const auto& tabMgr = es.getTableManager();
const auto& phases = rspec.phases();
const auto& wsd = rspec.wellSegmentDimensions();
const auto& hystPar = rspec.hysterPar();
auto pvt = ::Opm::RestartIO::LogiHEAD::PVTModel{};
pvt.isLiveOil = phases.active(::Opm::Phase::OIL) &&
!tabMgr.getPvtoTables().empty();
pvt.isWetGas = phases.active(::Opm::Phase::GAS) &&
!tabMgr.getPvtgTables().empty();
pvt.constComprOil = phases.active(::Opm::Phase::OIL) &&
!(pvt.isLiveOil ||
tabMgr.hasTables("PVDO") ||
tabMgr.getPvcdoTable().empty());
const auto lh = LogiHEAD{}
.variousParam(false, false, wsd.maxSegmentedWells(), hystPar.active())
.pvtModel(pvt)
;
return lh.data();
}

View File

@@ -31,18 +31,15 @@
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridProperty.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/Utility/Functional.hpp>
#include <opm/output/eclipse/LogiHEAD.hpp>
#include <opm/output/eclipse/RestartIO.hpp>
#include <opm/output/eclipse/Summary.hpp>
#include <opm/output/eclipse/Tables.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <cstdlib>
#include <memory> // unique_ptr
@@ -53,7 +50,6 @@
#include <ert/ecl/EclFilename.hpp>
#include <ert/ecl/ecl_kw_magic.h>
#include <ert/ecl/ecl_kw.h>
#include <ert/ecl/ecl_init_file.h>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
@@ -96,95 +92,8 @@ void writeKeyword( ERT::FortIO& fortio ,
}
void writeKeyword(ERT::FortIO& fortio,
const std::string& keywordName,
const std::vector<bool>& data)
{
auto freeKw = [](ecl_kw_type* kw)
{
if (kw != nullptr) { ecl_kw_free(kw); }
};
using KWPtr =
std::unique_ptr<ecl_kw_type, decltype(freeKw)>;
auto kw = KWPtr {
ecl_kw_alloc(keywordName.c_str(), data.size(), ECL_BOOL),
freeKw
};
if (kw == nullptr) { return; }
const auto n = data.size();
for (auto i = 0*n; i < n; ++i) {
ecl_kw_iset_bool(kw.get(), static_cast<int>(i), data[i]);
}
ecl_kw_fwrite(kw.get(), fortio.get());
}
void writeInitFileHeader(const ::Opm::EclipseState& es,
const ::Opm::EclipseGrid& grid,
const ::Opm::Schedule& sched,
ERT::FortIO& fortio)
{
// Expected order of header vectors:
// 1. INTEHEAD
// 2. LOGIHEAD
// 3. DOUBHEAD
// INTEHEAD
{
const auto ih = ::Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, 0.0, 0, 0);
writeKeyword(fortio, "INTEHEAD", ih);
}
// LOGIHEAD
{
const auto& tabMgr = es.getTableManager();
const auto& rspec = es.runspec();
const auto& phases = rspec.phases();
const auto& wsd = rspec.wellSegmentDimensions();
auto pvt = ::Opm::RestartIO::LogiHEAD::PVTModel{};
pvt.isLiveOil = phases.active(::Opm::Phase::OIL) &&
!tabMgr.getPvtoTables().empty();
pvt.isWetGas = phases.active(::Opm::Phase::GAS) &&
!tabMgr.getPvtgTables().empty();
pvt.constComprOil = phases.active(::Opm::Phase::OIL) &&
!(pvt.isLiveOil ||
tabMgr.hasTables("PVDO") ||
tabMgr.getPvcdoTable().empty());
auto lh = ::Opm::RestartIO::LogiHEAD{}
.variousParam(false, false,
wsd.maxSegmentedWells(),
rspec.hysterPar().active());
// Sequenced after 'lh' constructor to ensure that any changes
// to live oil/wet gas elements are from this particular call.
lh.pvtModel(pvt);
writeKeyword(fortio, "LOGIHEAD", lh.data());
}
// DOUBHEAD
{
const auto dh = ::Opm::RestartIO::Helpers::
createDoubHead(es, sched, 0, 0.0, 0.0);
// DOUBHEAD must be output as double precision so we can't use
// writeKeyword() here (double -> float conversion).
ERT::EclKW<double> kw("DOUBHEAD", dh);
kw.fwrite(fortio);
}
}
class RFT {
@@ -334,14 +243,15 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
ioConfig.getFMTOUT(),
ECL_ENDIAN_FLIP );
writeInitFileHeader(this->es, this->grid, this->schedule, fortio);
// The PORV vector is a special case. This particular vector always
// holds a total of nx*ny*nz elements, and the elements are explicitly
// set to zero for inactive cells. This treatment implies that the
// active/inactive cell mapping can be inferred by reading the PORV
// vector from the result set.
// Write INIT header. Observe that the PORV vector is treated
// specially; that is because for this particulat vector we write
// a total of nx*ny*nz values, where the PORV vector has been
// explicitly set to zero for inactive cells. The convention is
// that the active/inactive cell mapping can be inferred by
// reading the PORV vector.
{
const auto& opm_data = this->es.get3DProperties().getDoubleGridProperty("PORV").getData();
auto ecl_data = opm_data;
@@ -349,6 +259,14 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
if (!this->grid.cellActive( global_index ))
ecl_data[global_index] = 0;
ecl_init_file_fwrite_header( fortio.get(),
this->grid.c_ptr(),
NULL,
units.getEclType(),
this->es.runspec( ).eclPhaseMask( ),
this->schedule.posixStartTime( ));
units.from_si( UnitSystem::measure::volume, ecl_data );
writeKeyword( fortio, "PORV" , ecl_data );
}
@@ -400,7 +318,9 @@ void EclipseIO::Impl::writeINITFile( const data::Solution& simProps, std::map<st
// Write tables
{
Tables tables( this->es.getUnits() );
tables.addPVTTables(this->es);
tables.addPVTO( this->es.getTableManager().getPvtoTables() );
tables.addPVTG( this->es.getTableManager().getPvtgTables() );
tables.addPVTW( this->es.getTableManager().getPvtwTable() );
tables.addDensity( this->es.getTableManager().getDensityTable( ) );
tables.addSatFunc(this->es);
fwrite(tables, fortio);

View File

@@ -1086,20 +1086,20 @@ namespace {
const auto xwel = wellData.xwel(wellID);
// No unit conversion here. Smry expects output units.
smry.update(key("WOPT"), xwel[VI::XWell::index::OilPrTotal]);
smry.update(key("WWPT"), xwel[VI::XWell::index::WatPrTotal]);
smry.update(key("WGPT"), xwel[VI::XWell::index::GasPrTotal]);
smry.update(key("WVPT"), xwel[VI::XWell::index::VoidPrTotal]);
smry.add(key("WOPT"), xwel[VI::XWell::index::OilPrTotal]);
smry.add(key("WWPT"), xwel[VI::XWell::index::WatPrTotal]);
smry.add(key("WGPT"), xwel[VI::XWell::index::GasPrTotal]);
smry.add(key("WVPT"), xwel[VI::XWell::index::VoidPrTotal]);
smry.update(key("WWIT"), xwel[VI::XWell::index::WatInjTotal]);
smry.update(key("WGIT"), xwel[VI::XWell::index::GasInjTotal]);
smry.add(key("WWIT"), xwel[VI::XWell::index::WatInjTotal]);
smry.add(key("WGIT"), xwel[VI::XWell::index::GasInjTotal]);
smry.update(key("WOPTH"), xwel[VI::XWell::index::HistOilPrTotal]);
smry.update(key("WWPTH"), xwel[VI::XWell::index::HistWatPrTotal]);
smry.update(key("WGPTH"), xwel[VI::XWell::index::HistGasPrTotal]);
smry.add(key("WOPTH"), xwel[VI::XWell::index::HistOilPrTotal]);
smry.add(key("WWPTH"), xwel[VI::XWell::index::HistWatPrTotal]);
smry.add(key("WGPTH"), xwel[VI::XWell::index::HistGasPrTotal]);
smry.update(key("WWITH"), xwel[VI::XWell::index::HistWatInjTotal]);
smry.update(key("WGITH"), xwel[VI::XWell::index::HistGasInjTotal]);
smry.add(key("WWITH"), xwel[VI::XWell::index::HistWatInjTotal]);
smry.add(key("WGITH"), xwel[VI::XWell::index::HistGasInjTotal]);
}
void assign_group_cumulatives(const std::string& group,
@@ -1123,19 +1123,19 @@ namespace {
const auto xgrp = groupData.xgrp(groupID);
// No unit conversion here. Smry expects output units.
smry.update(key("OPT"), xgrp[VI::XGroup::index::OilPrTotal]);
smry.update(key("WPT"), xgrp[VI::XGroup::index::WatPrTotal]);
smry.update(key("GPT"), xgrp[VI::XGroup::index::GasPrTotal]);
smry.update(key("VPT"), xgrp[VI::XGroup::index::VoidPrTotal]);
smry.add(key("OPT"), xgrp[VI::XGroup::index::OilPrTotal]);
smry.add(key("WPT"), xgrp[VI::XGroup::index::WatPrTotal]);
smry.add(key("GPT"), xgrp[VI::XGroup::index::GasPrTotal]);
smry.add(key("VPT"), xgrp[VI::XGroup::index::VoidPrTotal]);
smry.update(key("WIT"), xgrp[VI::XGroup::index::WatInjTotal]);
smry.update(key("GIT"), xgrp[VI::XGroup::index::GasInjTotal]);
smry.add(key("WIT"), xgrp[VI::XGroup::index::WatInjTotal]);
smry.add(key("GIT"), xgrp[VI::XGroup::index::GasInjTotal]);
smry.update(key("OPTH"), xgrp[VI::XGroup::index::HistOilPrTotal]);
smry.update(key("WPTH"), xgrp[VI::XGroup::index::HistWatPrTotal]);
smry.update(key("GPTH"), xgrp[VI::XGroup::index::HistGasPrTotal]);
smry.update(key("WITH"), xgrp[VI::XGroup::index::HistWatInjTotal]);
smry.update(key("GITH"), xgrp[VI::XGroup::index::HistGasInjTotal]);
smry.add(key("OPTH"), xgrp[VI::XGroup::index::HistOilPrTotal]);
smry.add(key("WPTH"), xgrp[VI::XGroup::index::HistWatPrTotal]);
smry.add(key("GPTH"), xgrp[VI::XGroup::index::HistGasPrTotal]);
smry.add(key("WITH"), xgrp[VI::XGroup::index::HistWatInjTotal]);
smry.add(key("GITH"), xgrp[VI::XGroup::index::HistGasInjTotal]);
}
Opm::SummaryState

View File

@@ -1,39 +1,16 @@
#include <opm/output/eclipse/LogiHEAD.hpp>
#include <opm/output/eclipse/VectorItems/logihead.hpp>
#include <utility>
#include <vector>
namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
enum index : std::vector<int>::size_type {
// Flag to signify live oil (dissolved gas) PVT
IsLiveOil = VI::logihead::IsLiveOil,
// Flag to signify wet gas (vaporised oil) PVT
IsWetGas = VI::logihead::IsWetGas,
// Flag to signify directional relative permeability.
DirKr = VI::logihead::DirKr,
// ECLIPSE 100 flag for reversible rel.perm.
E100RevKr = VI::logihead::E100RevKr,
// ECLIPSE 100 flag for radial geometry.
E100Radial = VI::logihead::E100Radial,
// ECLIPSE 300 flag for radial geometry.
E300Radial = VI::logihead::E300Radial,
// ECLIPSE 300 flag for reversible rel.perm.
E300RevKr = VI::logihead::E300RevKr,
lh_000 = 0 , // TRUE
lh_001 = 1 , // TRUE
lh_002 = 2 , // FALSE
lh_003 = 3 , // FALSE Flag set to FALSE for a non-radial model, TRUE for a radial model (ECLIPSE 300 and other simulators)
lh_004 = 4 , // FALSE Flag set to FALSE for a non-radial model, TRUE for a radial model (ECLIPSE 100)
lh_005 = 5 , // FALSE
// Flag to enable hysteresis
Hyster = VI::logihead::Hyster,
lh_006 = 6 , // FALSE Flag set to FALSE when no hysteresis, TRUE when hysteresis option is used
lh_007 = 7 , // FALSE
lh_008 = 8 , // FALSE
lh_009 = 9 , // FALSE
@@ -41,24 +18,12 @@ lh_010 = 10 , // FALSE
lh_011 = 11 , // FALSE
lh_012 = 12 , // FALSE
lh_013 = 13 , // FALSE
// Flag to activate dual porosity.
DualPoro = VI::logihead::DualPoro,
lh_014 = 14 , // FALSE Flag for dual porosity model
lh_015 = 15 , // FALSE
// Flag to activate saturation function end-point scaling.
EndScale = VI::logihead::EndScale,
// Flag to activate directional end-point scaling.
DirEPS = VI::logihead::DirEPS,
// Flag to activate reversible end-point scaling. Typically True.
RevEPS = VI::logihead::RevEPS, // TRUE
// Flag to activate alternative end-point scaling (3-pt option)
AltEPS = VI::logihead::AltEPS,
lh_016 = 16 , // TRUE
lh_017 = 17 , // FALSE
lh_018 = 18 , // TRUE
lh_019 = 19 , //
lh_020 = 20 , // FALSE
lh_021 = 21 , // FALSE
lh_022 = 22 , // FALSE
@@ -77,10 +42,7 @@ lh_034 = 34 , // FALSE
lh_035 = 35 , // FALSE
lh_036 = 36 , // FALSE
lh_037 = 37 , //
// Flag to signify constant oil compressibility
ConstCo = VI::logihead::ConstCo,
lh_038 = 38 , // FALSE
lh_039 = 39 , // FALSE
lh_040 = 40 , // FALSE
lh_041 = 41 , // FALSE
@@ -117,10 +79,7 @@ lh_071 = 71 , // FALSE
lh_072 = 72 , // FALSE
lh_073 = 73 , // FALSE
lh_074 = 74 , // FALSE
// TRUE if segmented well model is used
HasMSWells = VI::logihead::HasMSWells,
lh_075 = 75 , // TRUE If segmented well model is used
lh_076 = 76 , // TRUE
lh_077 = 77 , // FALSE
lh_078 = 78 , // FALSE
@@ -167,10 +126,10 @@ lh_118 = 118 , // FALSE
lh_119 = 119 , // FALSE
lh_120 = 120 , // FALSE
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
LOGIHEAD_NUMBER_OF_ITEMS // MUST be last element of enum.
LOGIHEAD_NUMBER_OF_ITEMS // MUST be last element of enum.
};
// =====================================================================
@@ -183,38 +142,25 @@ Opm::RestartIO::LogiHEAD::LogiHEAD()
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::
variousParam(const bool e300_radial,
const bool e100_radial,
const int nswlmx,
const bool enableHyster)
variousParam(const bool e300_radial, const bool e100_radial, const int nswlmx, const bool enableHyster)
{
this -> data_[E300Radial] = e300_radial;
this -> data_[E100Radial] = e100_radial;
this -> data_[Hyster] = enableHyster;
this -> data_[HasMSWells] = nswlmx >= 1; // True if MS Wells exist.
return *this;
}
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::pvtModel(const PVTModel& pvt)
{
this->data_[IsLiveOil] = pvt.isLiveOil;
this->data_[IsWetGas ] = pvt.isWetGas;
this->data_[ConstCo ] = pvt.constComprOil;
return *this;
}
Opm::RestartIO::LogiHEAD&
Opm::RestartIO::LogiHEAD::saturationFunction(const SatfuncFlags& satfunc)
{
this->data_[DirKr] = satfunc.useDirectionalRelPerm;
this->data_[E100RevKr] = satfunc.useReversibleRelPerm;
this->data_[EndScale] = satfunc.useEndScale;
this->data_[DirEPS] = satfunc.useDirectionalEPS;
this->data_[RevEPS] = satfunc.useReversibleEPS;
this->data_[AltEPS] = satfunc.useAlternateEPS;
this -> data_[lh_000] = true;
this -> data_[lh_001] = true;
this -> data_[lh_003] = e300_radial;
this -> data_[lh_004] = e100_radial;
this -> data_[lh_006] = enableHyster;
//this -> data_[lh_016] = true;
//this -> data_[lh_018] = true;
//this -> data_[lh_031] = true;
//this -> data_[lh_044] = true;
this -> data_[lh_075] = nswlmx >= 1; // True if MS Wells exist.
//this -> data_[lh_076] = true;
//this -> data_[lh_087] = true;
//this -> data_[lh_099] = true;
//this -> data_[lh_113] = true;
//this -> data_[lh_114] = true;
//this -> data_[lh_115] = true;
//this -> data_[lh_117] = true;
return *this;
}

View File

@@ -84,10 +84,10 @@ namespace {
std::vector<int>
serialize_OPM_IWEL(const data::Wells& wells,
const std::vector<std::string>& sched_wells)
const std::vector<const Well*>& sched_wells)
{
const auto getctrl = [&]( const std::string& wname ) {
const auto itr = wells.find( wname );
const auto getctrl = [&]( const Well* w ) {
const auto itr = wells.find( w->name() );
return itr == wells.end() ? 0 : itr->second.control;
};
@@ -302,6 +302,7 @@ namespace {
void writeGroup(::Opm::RestartIO::ecl_rst_file_type* rst_file,
int sim_step,
const bool ecl_compatible_rst,
const Schedule& schedule,
const Opm::SummaryState& sumState,
const std::vector<int>& ih)
@@ -319,12 +320,12 @@ namespace {
groupData.captureDeclaredGroupData(schedule,
rst_g_keys, rst_f_keys,
grpKeyToInd, fldKeyToInd,
ecl_compatible_rst,
simStep, sumState, ih);
write_kw(rst_file, "IGRP", groupData.getIGroup());
write_kw(rst_file, "SGRP", groupData.getSGroup());
write_kw(rst_file, "XGRP", groupData.getXGroup());
write_kw(rst_file, "ZGRP", serialize_ZWEL(groupData.getZGroup()));
write_kw(rst_file, "ZGRP", serialize_ZWEL(groupData.getZGroup()));
}
void writeMSWData(::Opm::RestartIO::ecl_rst_file_type* rst_file,
@@ -363,6 +364,7 @@ namespace {
auto wellData = Helpers::AggregateWellData(ih);
wellData.captureDeclaredWellData(schedule, units, sim_step, sumState, ih);
wellData.captureDynamicWellData(schedule, sim_step,
ecl_compatible_rst,
wells, sumState);
write_kw(rst_file, "IWEL", wellData.getIWell());
@@ -374,12 +376,11 @@ namespace {
if (!ecl_compatible_rst)
{
const auto sched_wells = schedule.getWells(sim_step);
const auto sched_well_names = schedule.wellNames(sim_step);
const auto opm_xwel =
serialize_OPM_XWEL(wells, sim_step, sched_wells, phases, grid);
const auto opm_iwel = serialize_OPM_IWEL(wells, sched_well_names);
const auto opm_iwel = serialize_OPM_IWEL(wells, sched_wells);
write_kw(rst_file, "OPM_IWEL", opm_iwel);
write_kw(rst_file, "OPM_XWEL", opm_xwel);
@@ -553,7 +554,8 @@ void save(const std::string& filename,
nextStepSize(value), seconds_elapsed,
schedule, grid, es);
writeGroup(rst_file.get(), sim_step, schedule, sumState, inteHD);
writeGroup(rst_file.get(), sim_step, ecl_compatible_rst,
schedule, sumState, inteHD);
// Write well and MSW data only when applicable (i.e., when present)
{

View File

@@ -85,7 +85,9 @@ namespace {
}
};
for (const auto& well_name : sched.wellNames()) {
for (const auto* well : sched.getWells()) {
const auto& well_name = well->name();
makeEntities('W', well_name);
entities.emplace_back("WBHP" , well_name);
@@ -1062,21 +1064,6 @@ static const std::unordered_map< std::string, UnitSystem::measure> block_units =
{"BWSAT" , UnitSystem::measure::identity},
{"BSGAS" , UnitSystem::measure::identity},
{"BGSAT" , UnitSystem::measure::identity},
{"BOSAT" , UnitSystem::measure::identity},
{"BWKR" , UnitSystem::measure::identity},
{"BOKR" , UnitSystem::measure::identity},
{"BKRO" , UnitSystem::measure::identity},
{"BGKR" , UnitSystem::measure::identity},
{"BKRG" , UnitSystem::measure::identity},
{"BKRW" , UnitSystem::measure::identity},
{"BWPC" , UnitSystem::measure::pressure},
{"BGPC" , UnitSystem::measure::pressure},
{"BVWAT" , UnitSystem::measure::viscosity},
{"BWVIS" , UnitSystem::measure::viscosity},
{"BVGAS" , UnitSystem::measure::viscosity},
{"BGVIS" , UnitSystem::measure::viscosity},
{"BVOIL" , UnitSystem::measure::viscosity},
{"BOVIS" , UnitSystem::measure::viscosity},
};
inline std::vector< const Well* > find_wells( const Schedule& schedule,
@@ -1099,7 +1086,7 @@ inline std::vector< const Well* > find_wells( const Schedule& schedule,
if( type == ECL_SMSPEC_GROUP_VAR ) {
if( !schedule.hasGroup( name ) ) return {};
return schedule.getChildWells( name, sim_step, GroupWellQueryMode::Recursive);
return schedule.getWells( name, sim_step );
}
if( type == ECL_SMSPEC_FIELD_VAR )
@@ -1128,39 +1115,6 @@ inline std::vector< const Well* > find_wells( const Schedule& schedule,
}
bool need_wells(ecl_smspec_var_type var_type, const std::string& keyword) {
static const std::set<std::string> region_keywords{"ROIR", "RGIR", "RWIR", "ROPR", "RGPR", "RWPR", "ROIT", "RWIT", "RGIT", "ROPT", "RGPT", "RWPT"};
if (var_type == ECL_SMSPEC_WELL_VAR)
return true;
if (var_type == ECL_SMSPEC_GROUP_VAR)
return true;
if (var_type == ECL_SMSPEC_FIELD_VAR)
return true;
if (var_type == ECL_SMSPEC_COMPLETION_VAR)
return true;
if (var_type == ECL_SMSPEC_SEGMENT_VAR)
return true;
/*
Some of the region keywords are based on summing over all the connections
which fall in the region; i.e. RGIR is the total gas injection rate in the
region and consequently the list of defined wells is required, other
region keywords like 'ROIP' do not require well information.
*/
if (var_type == ECL_SMSPEC_REGION_VAR) {
if (region_keywords.count(keyword) > 0)
return true;
}
return false;
}
bool is_udq(const std::string& keyword) {
return (keyword.size() > 1 && keyword[1] == 'U');
}
@@ -1171,15 +1125,15 @@ void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
const auto& func_table = udq.function_table();
UDQContext context(func_table, st);
std::vector<std::string> wells;
for (const auto& well_name : schedule.wellNames())
wells.push_back(well_name);
for (const auto* well : schedule.getWells())
wells.push_back(well->name());
for (const auto& assign : udq.assignments(UDQVarType::WELL_VAR)) {
auto ws = assign.eval_wells(wells);
for (const auto& well : wells) {
const auto& udq_value = ws[well];
if (udq_value)
st.update_well_var(well, ws.name(), udq_value.value());
st.add_well_var(well, ws.name(), udq_value.value());
}
}
@@ -1188,7 +1142,7 @@ void eval_udq(const Schedule& schedule, std::size_t sim_step, SummaryState& st)
for (const auto& well : wells) {
const auto& udq_value = ws[well];
if (udq_value)
st.update_well_var(well, def.keyword(), udq_value.value());
st.add_well_var(well, def.keyword(), udq_value.value());
}
}
}
@@ -1396,7 +1350,7 @@ Summary::Summary( const EclipseState& st,
for (const auto& pair : this->handlers->handlers) {
const auto * nodeptr = pair.first;
if (nodeptr->is_total())
this->prev_state.update(*nodeptr, 0);
this->prev_state.add(*nodeptr, 0);
}
}
@@ -1461,15 +1415,14 @@ well_efficiency_factors( const ecl::smspec_node* node,
return efac;
}
void Summary::eval( SummaryState& st,
int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) const {
void Summary::add_timestep( int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) {
if (secs_elapsed < this->prev_time_elapsed) {
const auto& usys = es.getUnits();
@@ -1486,7 +1439,9 @@ void Summary::eval( SummaryState& st,
};
}
auto* tstep = ecl_sum_add_tstep( this->ecl_sum.get(), report_step, secs_elapsed );
const double duration = secs_elapsed - this->prev_time_elapsed;
SummaryState st;
/* report_step is the number of the file we are about to write - i.e. for instance CASE.S$report_step
* for the data in a non-unified summary file.
@@ -1496,47 +1451,26 @@ void Summary::eval( SummaryState& st,
for( auto& f : this->handlers->handlers ) {
const int num = smspec_node_get_num( f.first );
double unit_applied_val = smspec_node_get_default( f.first );
if (need_wells(smspec_node_get_var_type(f.first ),
smspec_node_get_keyword(f.first))) {
const auto schedule_wells = find_wells( schedule, f.first, sim_step, this->regionCache );
auto eff_factors = well_efficiency_factors( f.first, schedule, schedule_wells, sim_step );
const auto schedule_wells = find_wells( schedule, f.first, sim_step, this->regionCache );
/*
It is not a bug as such if the schedule_wells list comes back
empty; it just means that at the current timestep no relevant
wells have been defined and we do not calculate a value.
*/
if (schedule_wells.size() > 0) {
auto eff_factors = well_efficiency_factors( f.first, schedule, schedule_wells, sim_step );
const auto val = f.second( { schedule_wells,
duration,
sim_step,
num,
wells,
this->regionCache,
this->grid,
eff_factors});
unit_applied_val = es.getUnits().from_si( val.unit, val.value );
}
} else {
const auto val = f.second({ {},
duration,
sim_step,
num,
{},
this->regionCache,
this->grid,
{} });
unit_applied_val = es.getUnits().from_si( val.unit, val.value );
}
const auto val = f.second( { schedule_wells,
duration,
sim_step,
num,
wells,
this->regionCache,
this->grid,
eff_factors});
double unit_applied_val = es.getUnits().from_si( val.unit, val.value );
if (smspec_node_is_total(f.first)) {
const auto* genkey = smspec_node_get_gen_key1( f.first );
unit_applied_val += this->prev_state.get(genkey);
}
st.update(*f.first, unit_applied_val);
st.add(*f.first, unit_applied_val);
}
for( const auto& value_pair : single_values ) {
@@ -1546,7 +1480,7 @@ void Summary::eval( SummaryState& st,
const auto unit = single_values_units.at( key );
double si_value = value_pair.second;
double output_value = es.getUnits().from_si(unit , si_value );
st.update(*node_pair->second, output_value);
st.add(*node_pair->second, output_value);
}
}
@@ -1561,7 +1495,7 @@ void Summary::eval( SummaryState& st,
assert (smspec_node_get_num( nodeptr ) - 1 == static_cast<int>(reg));
double si_value = value_pair.second[reg];
double output_value = es.getUnits().from_si(unit , si_value );
st.update(*nodeptr, output_value);
st.add(*nodeptr, output_value);
}
}
}
@@ -1574,60 +1508,41 @@ void Summary::eval( SummaryState& st,
const auto unit = block_units.at( key.first );
double si_value = value_pair.second;
double output_value = es.getUnits().from_si(unit , si_value );
st.update(*nodeptr, output_value);
st.add(*nodeptr, output_value);
}
}
eval_udq(schedule, sim_step, st);
}
{
const ecl_sum_type * ecl_sum = this->ecl_sum.get();
const ecl_smspec_type * smspec = ecl_sum_get_smspec(ecl_sum);
auto num_nodes = ecl_smspec_num_nodes(smspec);
for (int node_index = 0; node_index < num_nodes; node_index++) {
const auto& smspec_node = ecl_smspec_iget_node(smspec, node_index);
// The TIME node is treated specially, it is created internally in
// the ecl_sum instance when the timestep is created - and
// furthermore it is not in st SummaryState instance.
if (smspec_node.get_params_index() == ecl_smspec_get_time_index(smspec))
continue;
void Summary::internal_store(const SummaryState& st, int report_step, double secs_elapsed) {
auto* tstep = ecl_sum_add_tstep( this->ecl_sum.get(), report_step, secs_elapsed );
const ecl_sum_type * ecl_sum = this->ecl_sum.get();
const ecl_smspec_type * smspec = ecl_sum_get_smspec(ecl_sum);
auto num_nodes = ecl_smspec_num_nodes(smspec);
for (int node_index = 0; node_index < num_nodes; node_index++) {
const auto& smspec_node = ecl_smspec_iget_node(smspec, node_index);
// The TIME node is treated specially, it is created internally in
// the ecl_sum instance when the timestep is created - and
// furthermore it is not in st SummaryState instance.
if (smspec_node.get_params_index() == ecl_smspec_get_time_index(smspec))
continue;
const std::string key = smspec_node.get_gen_key1();
if (st.has(key))
ecl_sum_tstep_iset(tstep, smspec_node.get_params_index(), st.get(key));
/*
else
OpmLog::warning("Have configured summary variable " + key + " for summary output - but it has not been calculated");
*/
const std::string key = smspec_node.get_gen_key1();
if (st.has(key))
ecl_sum_tstep_iset(tstep, smspec_node.get_params_index(), st.get(key));
/*
else
OpmLog::warning("Have configured summary variable " + key + " for summary output - but it has not been calculated");
*/
}
}
}
void Summary::add_timestep( int report_step,
double secs_elapsed,
const EclipseState& es,
const Schedule& schedule,
const data::Wells& wells ,
const std::map<std::string, double>& single_values,
const std::map<std::string, std::vector<double>>& region_values,
const std::map<std::pair<std::string, int>, double>& block_values) {
SummaryState st;
this->eval(st, report_step, secs_elapsed, es, schedule, wells, single_values, region_values, block_values);
this->internal_store(st, report_step, secs_elapsed);
this->prev_state = st;
this->prev_time_elapsed = secs_elapsed;
}
void Summary::write() const {
void Summary::write() {
ecl_sum_fwrite( this->ecl_sum.get() );
}
Summary::~Summary() {}
const SummaryState& Summary::get_restart_vectors() const
@@ -1649,10 +1564,9 @@ void Summary::reset_cumulative_quantities(const SummaryState& rstrt)
// is constructed from information in a restart file--i.e., from
// the double precision restart vectors 'XGRP' and 'XWEL' during
// RestartIO::load().
this->prev_state.set(genkey, rstrt.get(genkey));
this->prev_state.add(genkey, rstrt.get(genkey));
}
}
}
}} // namespace Opm::out

View File

@@ -0,0 +1,94 @@
/*
Copyright (c) 2018 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/output/eclipse/WriteRestartHelpers.hpp>
#include <ert/ecl_well/well_const.h> // containts ICON_XXX_INDEX
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <vector>
// ----------------------------------------------------------------------------
std::vector<double>
Opm::RestartIO::Helpers::
serialize_SCON(int lookup_step,
int ncwmax,
int nsconz,
const std::vector<const Well*>& sched_wells,
const UnitSystem& units)
// ----------------------------------------------------------------------------
{
const size_t well_field_size = ncwmax * nsconz;
std::vector<double> data(sched_wells.size() * well_field_size, 0);
size_t well_offset = 0;
for (const Opm::Well* well : sched_wells) {
const auto& connections = well->getConnections( lookup_step );
size_t connection_offset = 0;
bool explicit_ctf_not_found = false;
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
data[ offset + SCON_CF_INDEX ] = units.from_si(Opm::UnitSystem::measure::transmissibility,connection.CF());
data[ offset + SCON_KH_INDEX ] = units.from_si(Opm::UnitSystem::measure::effective_Kh, connection.Kh());
connection_offset += nsconz;
}
if (explicit_ctf_not_found) {
OpmLog::warning("restart output connection data missing",
"Explicit connection transmissibility factors for well " + well->name() + " missing, writing dummy values to restart file.");
}
well_offset += well_field_size;
}
return data;
}
// ----------------------------------------------------------------------------
std::vector<int>
Opm::RestartIO::Helpers::
serialize_ICON(int lookup_step,
int ncwmax,
int niconz,
const std::vector<const Opm::Well*>& sched_wells)
// ----------------------------------------------------------------------------
{
const size_t well_field_size = ncwmax * niconz;
std::vector<int> data(sched_wells.size() * well_field_size, 0);
size_t well_offset = 0;
for (const Opm::Well* well : sched_wells) {
const auto& connections = well->getConnections( lookup_step );
size_t connection_offset = 0;
for (const auto& connection : connections) {
const size_t offset = well_offset + connection_offset;
data[ offset + ICON_IC_INDEX ] = connection.complnum();
data[ offset + ICON_I_INDEX ] = connection.getI() + 1;
data[ offset + ICON_J_INDEX ] = connection.getJ() + 1;
data[ offset + ICON_K_INDEX ] = connection.getK() + 1;
data[ offset + ICON_DIRECTION_INDEX ] = connection.dir();
data[ offset + ICON_STATUS_INDEX ] =
(connection.state() == WellCompletion::StateEnum::OPEN) ?
1 : -1000;
data[ offset + ICON_SEGMENT_INDEX ] =
connection.attachedToSegment() ?
connection.segment() : 0;
connection_offset += niconz;
}
well_offset += well_field_size;
}
return data;
}

View File

@@ -41,46 +41,27 @@ std::vector< T >& DeckItem::value_ref() {
template<>
const std::vector< int >& DeckItem::value_ref< int >() const {
if( this->type != get_type< int >() )
throw std::invalid_argument( "DekcItem::velut_ref<int> Item of wrong type." );
throw std::invalid_argument( "Item of wrong type." );
return this->ival;
}
template<>
const std::vector< double >& DeckItem::value_ref< double >() const {
if (this->type == get_type<double>())
return this->dval;
if( this->type != get_type< double >() )
throw std::invalid_argument( "Item of wrong type." );
/* Temporary return double values when user code asks for double on
something which is really a UDAValue.
*/
if( this->type == get_type< UDAValue >() ) {
this->dval.clear();
for (const auto& uv : this->uval)
this->dval.push_back(uv.get<double>());
return this->dval;
}
throw std::invalid_argument( "DeckItem::value_ref<double> Item of wrong type." );
return this->dval;
}
template<>
const std::vector< std::string >& DeckItem::value_ref< std::string >() const {
if( this->type != get_type< std::string >() )
throw std::invalid_argument( "DeckItem::value_ref<std::string> Item of wrong type." );
throw std::invalid_argument( "Item of wrong type." );
return this->sval;
}
template<>
const std::vector< UDAValue >& DeckItem::value_ref< UDAValue >() const {
if( this->type != get_type< UDAValue >() )
throw std::invalid_argument( "DeckItem::value_ref<UDAValue> Item of wrong type." );
return this->uval;
}
DeckItem::DeckItem( const std::string& nm ) : item_name( nm ) {}
DeckItem::DeckItem( const std::string& nm, int, size_t hint ) :
@@ -99,14 +80,6 @@ DeckItem::DeckItem( const std::string& nm, double, size_t hint ) :
this->defaulted.reserve( hint );
}
DeckItem::DeckItem( const std::string& nm, UDAValue , size_t hint ) :
type( get_type< UDAValue >() ),
item_name( nm )
{
this->uval.reserve( hint );
this->defaulted.reserve( hint );
}
DeckItem::DeckItem( const std::string& nm, std::string, size_t hint ) :
type( get_type< std::string >() ),
item_name( nm )
@@ -128,8 +101,7 @@ bool DeckItem::hasValue( size_t index ) const {
case type_tag::integer: return this->ival.size() > index;
case type_tag::fdouble: return this->dval.size() > index;
case type_tag::string: return this->sval.size() > index;
case type_tag::uda: return this->uval.size() > index;
default: throw std::logic_error( "DeckItem::hasValue: Type not set." );
default: throw std::logic_error( "Type not set." );
}
}
@@ -138,8 +110,7 @@ size_t DeckItem::size() const {
case type_tag::integer: return this->ival.size();
case type_tag::fdouble: return this->dval.size();
case type_tag::string: return this->sval.size();
case type_tag::uda: return this->uval.size();
default: throw std::logic_error( "DeckItem::size: Type not set." );
default: throw std::logic_error( "Type not set." );
}
}
@@ -148,16 +119,6 @@ size_t DeckItem::out_size() const {
return std::max( data_size , this->defaulted.size() );
}
/*
The non const overload is only used for the UDAValue type because we need to
mutate the UDAValue object and add dimension to it retroactively.
*/
/*template<>
UDAValue& DeckItem::get( size_t index ) {
return this->uval[index];
}
*/
template< typename T >
const T& DeckItem::get( size_t index ) const {
return this->value_ref< T >().at( index );
@@ -188,10 +149,6 @@ void DeckItem::push_back( std::string x ) {
this->push( std::move( x ) );
}
void DeckItem::push_back( UDAValue x ) {
this->push( std::move( x ) );
}
template< typename T >
void DeckItem::push( T x, size_t n ) {
auto& val = this->value_ref< T >();
@@ -212,10 +169,6 @@ void DeckItem::push_back( std::string x, size_t n ) {
this->push( std::move( x ), n );
}
void DeckItem::push_back( UDAValue x, size_t n ) {
this->push( std::move( x ), n );
}
template< typename T >
void DeckItem::push_default( T x ) {
auto& val = this->value_ref< T >();
@@ -239,10 +192,6 @@ void DeckItem::push_backDefault( std::string x ) {
this->push_default( std::move( x ) );
}
void DeckItem::push_backDefault( UDAValue x ) {
this->push_default( std::move( x ) );
}
void DeckItem::push_backDummyDefault() {
if( !this->defaulted.empty() )
@@ -289,26 +238,12 @@ const std::vector< double >& DeckItem::getSIDoubleData() const {
}
void DeckItem::push_backDimension( const Dimension& active,
const Dimension& def ) {
if (this->type == type_tag::fdouble) {
const auto& ds = this->value_ref< double >();
const bool dim_inactive = ds.empty()
|| this->defaultApplied( ds.size() - 1 );
const Dimension& def ) {
const auto& ds = this->value_ref< double >();
const bool dim_inactive = ds.empty()
|| this->defaultApplied( ds.size() - 1 );
this->dimensions.push_back( dim_inactive ? def : active );
return;
}
if (this->type == type_tag::uda) {
auto& du = this->value_ref< UDAValue >();
const bool dim_inactive = du.empty()
|| this->defaultApplied( du.size() - 1 );
this->dimensions.push_back( dim_inactive ? def : active );
return;
}
throw std::logic_error("Tried to push dimensions to an item which can not hold dimension. ");
this->dimensions.push_back( dim_inactive ? def : active );
}
type_tag DeckItem::getType() const {
@@ -339,11 +274,8 @@ void DeckItem::write(DeckOutput& stream) const {
case type_tag::string:
this->write_vector( stream, this->sval );
break;
case type_tag::uda:
this->write_vector( stream, this->uval );
break;
default:
throw std::logic_error( "DeckItem::write: Type not set." );
throw std::logic_error( "Type not set." );
}
}
@@ -473,10 +405,8 @@ bool DeckItem::to_bool(std::string string_value) {
template const int& DeckItem::get< int >( size_t ) const;
template const double& DeckItem::get< double >( size_t ) const;
template const std::string& DeckItem::get< std::string >( size_t ) const;
template const UDAValue& DeckItem::get< UDAValue >( size_t ) const;
template const std::vector< int >& DeckItem::getData< int >() const;
template const std::vector< double >& DeckItem::getData< double >() const;
template const std::vector< UDAValue >& DeckItem::getData< UDAValue >() const;
template const std::vector< std::string >& DeckItem::getData< std::string >() const;
}

View File

@@ -20,7 +20,6 @@
#include <ostream>
#include <opm/parser/eclipse/Deck/DeckOutput.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
@@ -83,14 +82,6 @@ namespace Opm {
this->os << value;
}
template <>
void DeckOutput::write_value( const UDAValue& value ) {
if (value.is<double>())
this->write_value(value.get<double>());
else
this->write_value(value.get<std::string>());
}
void DeckOutput::stash_default( ) {
this->default_count++;
}
@@ -141,5 +132,4 @@ namespace Opm {
template void DeckOutput::write( const int& value);
template void DeckOutput::write( const double& value);
template void DeckOutput::write( const std::string& value);
template void DeckOutput::write( const UDAValue& value);
}

View File

@@ -1,96 +0,0 @@
/*
Copyright 2019 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/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
UDAValue::UDAValue(double value):
numeric_value(true),
double_value(value)
{
}
UDAValue::UDAValue() :
UDAValue(0)
{}
UDAValue::UDAValue(const std::string& value):
numeric_value(false),
string_value(value)
{
}
template<>
bool UDAValue::is<double>() const {
return this->numeric_value;
}
template<>
bool UDAValue::is<std::string>() const {
return !this->numeric_value;
}
template<>
double UDAValue::get() const {
if (this->numeric_value)
return this->double_value;
throw std::invalid_argument("UDAValue does not hold a numerical value");
}
template<>
std::string UDAValue::get() const {
if (!this->numeric_value)
return this->string_value;
throw std::invalid_argument("UDAValue does not hold a string value");
}
void UDAValue::set_dim(const Dimension& dim) const {
this->dim = dim;
}
const Dimension& UDAValue::get_dim() const {
return this->dim;
}
bool UDAValue::operator==(const UDAValue& other) const {
if (this->numeric_value != other.numeric_value)
return false;
if (this->numeric_value)
return (this->double_value == other.double_value);
return this->string_value == other.string_value;
}
bool UDAValue::operator!=(const UDAValue& other) const {
return !(*this == other);
}
}

View File

@@ -291,9 +291,9 @@ namespace Opm {
return this->m_wells.at( time_step ).size();
}
void Group::addWell(size_t time_step, const std::string& well_name ) {
void Group::addWell(size_t time_step, const Well* well ) {
auto new_set = this->m_wells.at( time_step );
new_set.insert( well_name );
new_set.insert( well->name() );
this->m_wells.update( time_step, new_set );
}

View File

@@ -36,13 +36,13 @@ void GroupTree::update( const std::string& name) {
* this grouptree class is just meta data for the actual group objects (stored
* and represented elsewhere)
*/
const std::map<std::string , size_t>& GroupTree::nameSeqIndMap() const {
return m_nameSeqIndMap;
return m_nameSeqIndMap;
}
const std::map<size_t, std::string >& GroupTree::seqIndNameMap() const {
return m_seqIndNameMap;
return m_seqIndNameMap;
}
void GroupTree::update( const std::string& name, const std::string& other_parent) {
@@ -53,7 +53,7 @@ void GroupTree::update( const std::string& name, const std::string& other_parent
throw std::invalid_argument( "Parent group must have a name." );
auto root = this->find( other_parent );
if( root == this->groups.end() || root->name != other_parent )
if( root == this->groups.end() || root->name != other_parent )
this->groups.insert( root, 1, group { other_parent, "FIELD" } );
auto node = this->find( name );
@@ -77,14 +77,14 @@ void GroupTree::updateSeqIndex( const std::string& name, const std::string& othe
size_t index = this->m_nameSeqIndMap.size();
auto name_itr = this->m_nameSeqIndMap.find(name);
if (name_itr == this->m_nameSeqIndMap.end()) {
this->m_nameSeqIndMap.insert(std::make_pair(name, index));
this->m_seqIndNameMap.insert(std::make_pair(index, name));
index +=1;
this->m_nameSeqIndMap.insert(std::make_pair(name, index));
this->m_seqIndNameMap.insert(std::make_pair(index, name));
index +=1;
}
auto parent_itr = this->m_nameSeqIndMap.find(other_parent);
if (parent_itr == this->m_nameSeqIndMap.end()) {
this->m_nameSeqIndMap.insert(std::make_pair(other_parent, index));
this->m_seqIndNameMap.insert(std::make_pair(index, other_parent));
this->m_nameSeqIndMap.insert(std::make_pair(other_parent, index));
this->m_seqIndNameMap.insert(std::make_pair(index, other_parent));
}
}
@@ -99,7 +99,7 @@ const std::string& GroupTree::parent( const std::string& name ) const {
auto node = std::find( this->groups.begin(), this->groups.end(), name );
if( node == this->groups.end() )
throw std::out_of_range( "No such group: '" + name + "'." );
throw std::out_of_range( "No such parent '" + name + "'." );
return node->parent;
}
@@ -109,6 +109,11 @@ std::vector< std::string > GroupTree::children( const std::string& other_parent
throw std::out_of_range( "Node '" + other_parent + "' does not exist." );
std::vector< std::string > kids;
/* for( const auto& node : this->groups ) {
if( node.parent != other_parent ) continue;
kids.push_back( node.name );
}
*/
for( auto it = this->groups.begin(); it != this->groups.end(); it++ ) {
if( (*it).parent != other_parent ) continue;
kids.push_back( (*it).name );
@@ -162,20 +167,4 @@ std::vector< GroupTree::group >::iterator GroupTree::find( const std::string& na
name );
}
std::vector<GroupTree::group>::const_iterator GroupTree::begin() const {
return this->groups.begin();
}
std::vector<GroupTree::group>::const_iterator GroupTree::end() const {
return this->groups.end();
}
std::ostream& operator<<(std::ostream& stream, const GroupTree& gt) {
for (const auto& group_pair : gt)
stream << group_pair.parent << " -> " << group_pair.name << std::endl;
stream << std::endl << std::endl;
return stream;
}
}

View File

@@ -35,10 +35,13 @@
namespace Opm {
const std::string& WellSegments::wellName() const {
std::string WellSegments::wellName() const {
return m_well_name;
}
int WellSegments::numberBranch() const {
return m_number_branch;
}
int WellSegments::size() const {
return m_segments.size();
@@ -56,6 +59,9 @@ namespace Opm {
return m_volume_top;
}
WellSegment::LengthDepthEnum WellSegments::lengthDepthType() const {
return m_length_depth_type;
}
WellSegment::CompPressureDropEnum WellSegments::compPressureDrop() const {
return m_comp_pressure_drop;
@@ -222,16 +228,6 @@ namespace Opm {
return m_segments[segment_index];
}
void WellSegments::process(bool first_time) {
if (this->m_length_depth_type == WellSegment::ABS)
this->processABS();
else if (this->m_length_depth_type == WellSegment::INC)
this->processINC(first_time);
else
throw std::logic_error("Invalid llength/depth/type in segment data structure");
}
void WellSegments::processABS() {
const double invalid_value = Segment::invalidValue(); // meaningless value to indicate unspecified/uncompleted values
@@ -401,6 +397,7 @@ namespace Opm {
bool WellSegments::operator==( const WellSegments& rhs ) const {
return this->m_well_name == rhs.m_well_name
&& this->m_number_branch == rhs.m_number_branch
&& this->m_depth_top == rhs.m_depth_top
&& this->m_length_top == rhs.m_length_top
&& this->m_volume_top == rhs.m_volume_top
@@ -420,13 +417,4 @@ namespace Opm {
bool WellSegments::operator!=( const WellSegments& rhs ) const {
return !( *this == rhs );
}
std::ostream& operator<<( std::ostream& stream, const WellSegments& well_segments) {
return stream
<< well_segments.wellName() << " { top: {" <<
" L: " << well_segments.lengthTopSegment() <<
" D: " << well_segments.depthTopSegment() <<
" V: " << well_segments.volumeTopSegment() << " }}";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,71 +21,16 @@
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
namespace Opm{
namespace {
bool is_total(const std::string& key) {
auto sep_pos = key.find(':');
if (sep_pos == 0)
return false;
if (sep_pos == std::string::npos) {
if (key.back() == 'T')
return true;
return (key.compare(key.size() - 2, 2, "TH") == 0);
} else
return is_total(key.substr(0,sep_pos));
}
}
void SummaryState::update_elapsed(double delta) {
this->elapsed += delta;
}
double SummaryState::get_elapsed() const {
return this->elapsed;
}
void SummaryState::update(const std::string& key, double value) {
if (is_total(key))
this->values[key] += value;
else
this->values[key] = value;
}
void SummaryState::update(const ecl::smspec_node& node, double value) {
void SummaryState::add(const ecl::smspec_node& node, double value) {
if (node.get_var_type() == ECL_SMSPEC_WELL_VAR)
this->update_well_var(node.get_wgname(),
node.get_keyword(),
value);
else {
const std::string& key = node.get_gen_key1();
if (node.is_total())
this->values[key] += value;
else
this->values[key] = value;
}
this->add_well_var(node.get_wgname(),
node.get_keyword(),
value);
else
this->add(node.get_gen_key1(), value);
}
void SummaryState::update_well_var(const std::string& well, const std::string& var, double value) {
std::string key = var + ":" + well;
if (is_total(var)) {
this->values[key] += value;
this->well_values[var][well] += value;
} else {
this->values[key] = value;
this->well_values[var][well] = value;
}
this->m_wells.insert(well);
}
void SummaryState::set(const std::string& key, double value) {
void SummaryState::add(const std::string& key, double value) {
this->values[key] = value;
}
@@ -103,6 +48,12 @@ namespace {
return iter->second;
}
void SummaryState::add_well_var(const std::string& well, const std::string& var, double value) {
this->add(var + ":" + well, value);
this->well_values[var][well] = value;
this->m_wells.insert(well);
}
bool SummaryState::has_well_var(const std::string& well, const std::string& var) const {
const auto& var_iter = this->well_values.find(var);
if (var_iter == this->well_values.end())

View File

@@ -20,8 +20,6 @@
#include <algorithm>
#include <cassert>
#include <vector>
#include <sstream>
#include <iostream>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
@@ -105,15 +103,15 @@ namespace Opm {
bool Connection::attachedToSegment() const {
return (segment_number > 0);
}
const std::size_t& Connection::getSeqIndex() const {
return m_seqIndex;
}
const bool& Connection::getDefaultSatTabId() const {
return m_defaultSatTabId;
}
const std::size_t& Connection::getCompSegSeqIndex() const {
return m_compSeg_seqIndex;
}
@@ -130,15 +128,15 @@ namespace Opm {
return m_segDistEnd;
}
void Connection::setCompSegSeqIndex(std::size_t index) {
m_compSeg_seqIndex = index;
}
void Connection::setDefaultSatTabId(bool id) {
m_defaultSatTabId = id;
}
void Connection::setSegDistStart(const double& distStart) {
m_segDistStart = distStart;
}
@@ -146,7 +144,7 @@ namespace Opm {
void Connection::setSegDistEnd(const double& distEnd) {
m_segDistEnd = distEnd;
}
double Connection::depth() const {
return this->center_depth;
}
@@ -209,30 +207,8 @@ namespace Opm {
return this->wPi;
}
std::string Connection::str() const {
std::stringstream ss;
ss << "ijk: " << this->ijk[0] << "," << this->ijk[1] << "," << this->ijk[2] << std::endl;
ss << "COMPLNUM " << this->m_complnum << std::endl;
ss << "CF " << this->m_CF << std::endl;
ss << "RW " << this->m_rw << std::endl;
ss << "R0 " << this->m_r0 << std::endl;
ss << "skinf " << this->m_skin_factor << std::endl;
ss << "wPi " << this->wPi << std::endl;
ss << "kh " << this->m_Kh << std::endl;
ss << "sat_tableId " << this->sat_tableId << std::endl;
ss << "open_state " << this->open_state << std::endl;
ss << "direction " << this->direction << std::endl;
ss << "segment_nr " << this->segment_number << std::endl;
ss << "center_depth " << this->center_depth << std::endl;
ss << "seqIndex " << this->m_seqIndex << std::endl;
return ss.str();
}
bool Connection::operator==( const Connection& rhs ) const {
bool eq = this->ijk == rhs.ijk
return this->ijk == rhs.ijk
&& this->m_complnum == rhs.m_complnum
&& this->m_CF == rhs.m_CF
&& this->m_rw == rhs.m_rw
@@ -246,10 +222,6 @@ namespace Opm {
&& this->segment_number == rhs.segment_number
&& this->center_depth == rhs.center_depth
&& this->m_seqIndex == rhs.m_seqIndex;
if (!eq) {
//std::cout << this->str() << rhs.str() << std::endl;
}
return eq;
}
bool Connection::operator!=( const Connection& rhs ) const {

View File

@@ -151,7 +151,7 @@ namespace Opm {
if (isInjector(timeStep))
switchToProducer( timeStep );
this->setProducer(timeStep, true);
m_isProducer.update(timeStep , true);
return m_productionProperties.update(timeStep, newProperties);
}
@@ -167,7 +167,7 @@ namespace Opm {
if (isProducer(timeStep))
switchToInjector( timeStep );
this->setProducer(timeStep, false);
m_isProducer.update(timeStep , false);
return m_injectionProperties.update(timeStep, newProperties);
}
@@ -180,8 +180,8 @@ namespace Opm {
}
bool Well::setPolymerProperties(size_t timeStep , const WellPolymerProperties& newProperties) {
m_isProducer.update(timeStep , false);
bool update = m_polymerProperties.update(timeStep, newProperties);
this->setProducer(timeStep, false);
return update;
}
@@ -244,10 +244,6 @@ namespace Opm {
return m_status.update( timeStep , status );
}
bool Well::setProducer(size_t timeStep, bool producer) {
return this->m_isProducer.update(timeStep, producer);
}
bool Well::isProducer(size_t timeStep) const {
return bool( m_isProducer.get(timeStep) );
}
@@ -467,7 +463,14 @@ namespace Opm {
// overwrite the BHP reference depth with the one from WELSEGS keyword
const double ref_depth = new_segmentset.depthTopSegment();
m_refDepth.update( time_step, ref_depth );
new_segmentset.process(first_time);
if (new_segmentset.lengthDepthType() == WellSegment::ABS) {
new_segmentset.processABS();
} else if (new_segmentset.lengthDepthType() == WellSegment::INC) {
new_segmentset.processINC(first_time);
} else {
throw std::logic_error(" unknown length_depth_type in the new_segmentset");
}
m_segmentset.update(time_step, new_segmentset);
}
@@ -481,10 +484,7 @@ namespace Opm {
const auto headJ = this->m_headJ[ time_step ];
new_set->orderConnections( headI, headJ );
}
//This breaks test at line 824 in ScheduleTests
/*if (new_set->allConnectionsShut())
this->setStatus(time_step, WellCommon::SHUT );
*/
m_completions.update( time_step, std::shared_ptr<WellConnections>( new_set ));
}

View File

@@ -1,608 +0,0 @@
/*
Copyright 2019 Equinor 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/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/updatingConnectionsWithSegments.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
namespace Opm {
namespace {
bool defaulted(const DeckRecord& rec, const std::string& s) {
const auto& item = rec.getItem( s );
if (item.defaultApplied(0))
return true;
if (item.get<int>(0) == 0)
return true;
return false;
}
int limit(const DeckRecord& rec, const std::string&s , int shift) {
const auto& item = rec.getItem( s );
return shift + item.get<int>(0);
}
bool match_le(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (value <= limit(rec,s,shift));
}
bool match_ge(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (value >= limit(rec,s,shift));
}
bool match_eq(int value, const DeckRecord& rec, const std::string& s, int shift = 0) {
if (defaulted(rec,s))
return true;
return (limit(rec,s,shift) == value);
}
}
Well2::Well2(const std::string& wname,
const std::string& gname,
std::size_t init_step,
std::size_t insert_index,
int headI,
int headJ,
double ref_depth,
Phase phase,
WellProducer::ControlModeEnum whistctl_cmode,
WellCompletion::CompletionOrderEnum ordering):
wname(wname),
group_name(gname),
init_step(init_step),
insert_index(insert_index),
headI(headI),
headJ(headJ),
ref_depth(ref_depth),
phase(phase),
ordering(ordering),
status(WellCommon::SHUT),
drainage_radius(ParserKeywords::WELSPECS::D_RADIUS::defaultValue),
allow_cross_flow(DeckItem::to_bool(ParserKeywords::WELSPECS::CROSSFLOW::defaultValue)),
automatic_shutin( ParserKeywords::WELSPECS::CROSSFLOW::defaultValue == "SHUT"),
producer(true),
guide_rate({true, -1, GuideRate::UNDEFINED,ParserKeywords::WGRUPCON::SCALING_FACTOR::defaultValue}),
efficiency_factor(1.0),
solvent_fraction(0.0),
econ_limits(std::make_shared<WellEconProductionLimits>()),
polymer_properties(std::make_shared<WellPolymerProperties>()),
tracer_properties(std::make_shared<WellTracerProperties>()),
connections(std::make_shared<WellConnections>(headI, headJ)),
production(std::make_shared<WellProductionProperties>()),
injection(std::make_shared<WellInjectionProperties>())
{
WellProductionProperties * p = new WellProductionProperties(*this->production);
p->whistctl_cmode = whistctl_cmode;
this->production.reset( p );
}
bool Well2::updateEfficiencyFactor(double efficiency_factor) {
if (this->efficiency_factor != efficiency_factor) {
this->efficiency_factor = efficiency_factor;
return true;
}
return false;
}
bool Well2::updateWellGuideRate(double guide_rate) {
if (this->guide_rate.guide_rate != guide_rate) {
this->guide_rate.guide_rate = guide_rate;
return true;
}
return false;
}
bool Well2::updatePolymerProperties(std::shared_ptr<WellPolymerProperties> polymer_properties) {
if (*this->polymer_properties != *polymer_properties) {
this->polymer_properties = polymer_properties;
this->producer = false;
return true;
}
return false;
}
bool Well2::updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits) {
if (*this->econ_limits != *econ_limits) {
this->econ_limits = econ_limits;
return true;
}
return false;
}
void Well2::switchToProducer() {
auto p = std::make_shared<WellInjectionProperties>(this->getInjectionProperties());
p->BHPLimit = 0;
p->dropInjectionControl( Opm::WellInjector::BHP );
this->updateInjection( p );
this->updateProducer(true);
}
void Well2::switchToInjector() {
auto p = std::make_shared<WellProductionProperties>(getProductionProperties());
p->BHPLimit = 0;
p->dropProductionControl( Opm::WellProducer::BHP );
this->updateProduction( p );
this->updateProducer( false );
}
bool Well2::updateInjection(std::shared_ptr<WellInjectionProperties> injection) {
if (this->producer)
this->switchToInjector( );
if (*this->injection != *injection) {
this->injection = injection;
return true;
}
return false;
}
bool Well2::updateProduction(std::shared_ptr<WellProductionProperties> production) {
if (!this->producer)
this->switchToProducer( );
if (*this->production != *production) {
this->production = production;
return true;
}
return false;
}
bool Well2::updateTracer(std::shared_ptr<WellTracerProperties> tracer_properties) {
if (*this->tracer_properties != *tracer_properties) {
this->tracer_properties = tracer_properties;
return true;
}
return false;
}
bool Well2::updateWellGuideRate(bool available, double guide_rate, GuideRate::GuideRatePhaseEnum guide_phase, double scale_factor) {
bool update = false;
if (this->guide_rate.available != available) {
this->guide_rate.available = available;
update = true;
}
if(this->guide_rate.guide_rate != guide_rate) {
this->guide_rate.guide_rate = guide_rate;
update = true;
}
if(this->guide_rate.guide_phase != guide_phase) {
this->guide_rate.guide_phase = guide_phase;
update = true;
}
if(this->guide_rate.scale_factor != scale_factor) {
this->guide_rate.scale_factor = scale_factor;
update = true;
}
return update;
}
bool Well2::updateProducer(bool producer) {
if (this->producer != producer) {
this->producer = producer;
return true;
}
return false;
}
bool Well2::updateGroup(const std::string& group) {
if (this->group_name != group) {
this->group_name = group;
return true;
}
return false;
}
bool Well2::updateHead(int I, int J) {
bool update = false;
if (this->headI != I) {
this->headI = I;
update = true;
}
if (this->headJ != J) {
this->headJ = J;
update = true;
}
return update;
}
bool Well2::updateStatus(WellCommon::StatusEnum status) {
if (this->status != status) {
this->status = status;
return true;
}
return false;
}
bool Well2::updateRefDepth(double ref_depth) {
if (this->ref_depth != ref_depth) {
this->ref_depth = ref_depth;
return true;
}
return false;
}
bool Well2::updateDrainageRadius(double drainage_radius) {
if (this->drainage_radius != drainage_radius) {
this->drainage_radius = drainage_radius;
return true;
}
return false;
}
bool Well2::updateCrossFlow(bool allow_cross_flow) {
if (this->allow_cross_flow != allow_cross_flow) {
this->allow_cross_flow = allow_cross_flow;
return true;
}
return false;
}
bool Well2::updateAutoShutin(bool auto_shutin) {
if (this->automatic_shutin != auto_shutin) {
this->automatic_shutin = auto_shutin;
return true;
}
return false;
}
bool Well2::updateConnections(const std::shared_ptr<WellConnections> connections) {
if( this->ordering == WellCompletion::TRACK)
connections->orderConnections( this->headI, this->headJ );
if (*this->connections != *connections) {
this->connections = connections;
//if (this->connections->allConnectionsShut()) {}
// This status update breaks line 825 in ScheduleTests
//this->status = WellCommon::StatusEnum::SHUT;
return true;
}
return false;
}
bool Well2::updateSolventFraction(double solvent_fraction) {
if (this->solvent_fraction != solvent_fraction) {
this->solvent_fraction = solvent_fraction;
return true;
}
return false;
}
bool Well2::handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid) {
std::shared_ptr<WellConnections> new_connection_set( newConnectionsWithSegments(keyword, *this->connections, *this->segments , grid) );
return this->updateConnections(new_connection_set);
}
const std::string& Well2::groupName() const {
return this->group_name;
}
bool Well2::isMultiSegment() const {
if (this->segments)
return true;
return false;
}
bool Well2::isProducer() const {
return this->producer;
}
bool Well2::isAvailableForGroupControl() const {
return this->guide_rate.available;
}
double Well2::getGuideRate() const {
return this->guide_rate.guide_rate;
};
GuideRate::GuideRatePhaseEnum Well2::getGuideRatePhase() const {
return this->guide_rate.guide_phase;
};
double Well2::getGuideRateScalingFactor() const {
return this->guide_rate.scale_factor;
};
double Well2::getEfficiencyFactor() const {
return this->efficiency_factor;
}
double Well2::getSolventFraction() const {
return this->solvent_fraction;
}
std::size_t Well2::seqIndex() const {
return this->insert_index;
}
int Well2::getHeadI() const {
return this->headI;
}
int Well2::getHeadJ() const {
return this->headJ;
}
bool Well2::getAutomaticShutIn() const {
return this->automatic_shutin;
}
bool Well2::getAllowCrossFlow() const {
return this->allow_cross_flow;
}
double Well2::getRefDepth() const {
if( this->ref_depth >= 0.0 )
return this->ref_depth;
// ref depth was defaulted and we get the depth of the first completion
if( this->connections->size() == 0 ) {
throw std::invalid_argument( "No completions defined for well: "
+ name()
+ ". Can not infer reference depth" );
}
return this->connections->get(0).depth();
}
double Well2::getDrainageRadius() const {
return this->drainage_radius;
}
const std::string& Well2::name() const {
return this->wname;
}
const WellConnections& Well2::getConnections() const {
return *this->connections;
}
const WellPolymerProperties& Well2::getPolymerProperties() const {
return *this->polymer_properties;
}
const WellTracerProperties& Well2::getTracerProperties() const {
return *this->tracer_properties;
}
const WellEconProductionLimits& Well2::getEconLimits() const {
return *this->econ_limits;
}
const WellProductionProperties& Well2::getProductionProperties() const {
return *this->production;
}
const WellSegments& Well2::getSegments() const {
if (this->segments)
return *this->segments;
else
throw std::logic_error("Asked for segment information in not MSW well: " + this->name());
}
const WellInjectionProperties& Well2::getInjectionProperties() const {
return *this->injection;
}
WellCommon::StatusEnum Well2::getStatus() const {
return this->status;
}
std::map<int, std::vector<Connection>> Well2::getCompletions() const {
std::map<int, std::vector<Connection>> completions;
for (const auto& conn : *this->connections) {
auto pair = completions.find( conn.complnum() );
if (pair == completions.end())
completions[conn.complnum()] = {};
pair = completions.find(conn.complnum());
pair->second.push_back(conn);
}
return completions;
}
Phase Well2::getPreferredPhase() const {
return this->phase;
}
bool Well2::handleWELOPEN(const DeckRecord& record, WellCompletion::StateEnum status) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_eq(c.getK(), record, "K", -1)) return false;
if (!match_ge(c.complnum(), record, "C1")) return false;
if (!match_le(c.complnum(), record, "C2")) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
for (auto c : *this->connections) {
if (match(c))
c.setState( status );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleCOMPLUMP(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_ge(c.getK(), record, "K1", -1)) return false;
if (!match_le(c.getK(), record, "K2", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
const int complnum = record.getItem("N").get<int>(0);
if (complnum <= 0)
throw std::invalid_argument("Completion number must be >= 1. COMPLNUM=" + std::to_string(complnum) + "is invalid");
for (auto c : *this->connections) {
if (match(c))
c.setComplnum( complnum );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleWPIMULT(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_ge(c.complnum(), record, "FIRST")) return false;
if (!match_le(c.complnum(), record, "LAST")) return false;
if (!match_eq(c.getI() , record, "I", -1)) return false;
if (!match_eq(c.getJ() , record, "J", -1)) return false;
if (!match_eq(c.getK() , record, "K", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->headI, this->headJ);
double wellPi = record.getItem("WELLPI").get< double >(0);
for (auto c : *this->connections) {
if (match(c))
c.scaleWellPi( wellPi );
new_connections->add(c);
}
return this->updateConnections(new_connections);
}
bool Well2::handleWELSEGS(const DeckKeyword& keyword) {
if( this->segments )
throw std::logic_error("re-entering WELSEGS for a well is not supported yet!!.");
auto new_segmentset = std::make_shared<WellSegments>();
new_segmentset->loadWELSEGS(keyword);
new_segmentset->process(true);
if (new_segmentset != this->segments) {
this->segments = new_segmentset;
this->ref_depth = new_segmentset->depthTopSegment();
return true;
} else
return false;
}
void Well2::filterConnections(const EclipseGrid& grid) {
this->connections->filter(grid);
}
std::size_t Well2::firstTimeStep() const {
return this->init_step;
}
bool Well2::canOpen() const {
if (this->allow_cross_flow)
return true;
if (this->producer) {
const auto& prod = *this->production;
return (prod.WaterRate + prod.OilRate + prod.GasRate) != 0;
} else {
const auto& inj = *this->injection;
return inj.surfaceInjectionRate != 0;
}
}
WellCompletion::CompletionOrderEnum Well2::getWellConnectionOrdering() const {
return this->ordering;
}
}

View File

@@ -144,10 +144,10 @@ namespace {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
{
int conn_i = (i < 0) ? this->headI : i;
int conn_j = (j < 0) ? this->headJ : j;
@@ -167,10 +167,10 @@ namespace {
double skin_factor,
const int satTableId,
const WellCompletion::DirectionEnum direction,
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
const std::size_t seqIndex,
const double segDistStart,
const double segDistEnd,
const bool defaultSatTabId)
{
int complnum = (this->m_connections.size() + 1);
this->addConnection(i,
@@ -186,10 +186,10 @@ namespace {
skin_factor,
satTableId,
direction,
seqIndex,
segDistStart,
segDistEnd,
defaultSatTabId);
seqIndex,
segDistStart,
segDistEnd,
defaultSatTabId);
}
void WellConnections::loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const Eclipse3DProperties& eclipseProperties) {
@@ -296,47 +296,48 @@ namespace {
same_ijk );
// Only add connection for active grid cells
if (grid.cellActive(I, J, k)) {
if (prev == this->m_connections.end()) {
std::size_t noConn = this->m_connections.size();
this->addConnection(I,J,k,
grid.getCellDepth( I,J,k ),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, 0., 0., defaultSatTable);
} else {
std::size_t noConn = prev->getSeqIndex();
// The complnum value carries over; the rest of the state is fully specified by
// the current COMPDAT keyword.
int complnum = prev->complnum();
std::size_t css_ind = prev->getCompSegSeqIndex();
int conSegNo = prev->segment();
std::size_t con_SIndex = prev->getSeqIndex();
double conCDepth = prev->depth();
double conSDStart = prev->getSegDistStart();
double conSDEnd = prev->getSegDistEnd();
*prev = Connection(I,J,k,
complnum,
grid.getCellDepth(I,J,k),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, conSDStart, conSDEnd, defaultSatTable);
prev->setCompSegSeqIndex(css_ind);
prev->updateSegment(conSegNo, conCDepth, con_SIndex);
}
}
}
if (prev == this->m_connections.end()) {
std::size_t noConn = this->m_connections.size();
this->addConnection(I,J,k,
grid.getCellDepth( I,J,k ),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, 0., 0., defaultSatTable);
}
else {
std::size_t noConn = prev->getSeqIndex();
// The complnum value carries over; the rest of the state is fully specified by
// the current COMPDAT keyword.
int complnum = prev->complnum();
std::size_t css_ind = prev->getCompSegSeqIndex();
int conSegNo = prev->segment();
std::size_t con_SIndex = prev->getSeqIndex();
double conCDepth = prev->depth();
double conSDStart = prev->getSegDistStart();
double conSDEnd = prev->getSegDistEnd();
*prev = Connection(I,J,k,
complnum,
grid.getCellDepth(I,J,k),
state,
CF,
Kh,
rw,
r0,
skin_factor,
satTableId,
direction,
noConn, conSDStart, conSDEnd, defaultSatTable);
prev->setCompSegSeqIndex(css_ind);
prev->updateSegment(conSegNo, conCDepth, con_SIndex);
}
}
}
}
@@ -457,9 +458,8 @@ namespace {
}
bool WellConnections::operator==( const WellConnections& rhs ) const {
return this->size() == rhs.size() &&
this->num_removed == rhs.num_removed &&
std::equal( this->begin(), this->end(), rhs.begin() );
return this->size() == rhs.size()
&& std::equal( this->begin(), this->end(), rhs.begin() );
}
bool WellConnections::operator!=( const WellConnections& rhs ) const {

View File

@@ -17,7 +17,6 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <string>
#include <vector>
@@ -44,30 +43,6 @@ namespace Opm {
}
void WellPolymerProperties::handleWPOLYMER(const DeckRecord& record) {
this->m_polymerConcentration = record.getItem("POLYMER_CONCENTRATION").getSIDouble(0);
this->m_saltConcentration = record.getItem("SALT_CONCENTRATION").getSIDouble(0);
const auto& group_polymer_item = record.getItem("GROUP_POLYMER_CONCENTRATION");
const auto& group_salt_item = record.getItem("GROUP_SALT_CONCENTRATION");
if (!group_polymer_item.defaultApplied(0))
throw std::logic_error("Sorry explicit setting of \'GROUP_POLYMER_CONCENTRATION\' is not supported!");
if (!group_salt_item.defaultApplied(0))
throw std::logic_error("Sorry explicit setting of \'GROUP_SALT_CONCENTRATION\' is not supported!");
}
void WellPolymerProperties::handleWPMITAB(const DeckRecord& record) {
this->m_plymwinjtable = record.getItem("TABLE_NUMBER").get<int>(0);
}
void WellPolymerProperties::handleWSKPTAB(const DeckRecord& record) {
this->m_skprwattable = record.getItem("TABLE_NUMBER_WATER").get<int>(0);
this->m_skprpolytable = record.getItem("TABLE_NUMBER_POLYMER").get<int>(0);
}
bool WellPolymerProperties::operator!=(const WellPolymerProperties& other) const {
return !(*this == other);
}

View File

@@ -67,9 +67,7 @@ namespace Opm {
else
cmode = wp::ControlModeFromString( cmodeItem.getTrimmedString( 0 ) );
// clearing the existing targets/limits
clearControls();
m_productionControls = 0;
if (effectiveHistoryProductionControl(cmode)) {
this->addProductionControl( cmode );
this->controlMode = cmode;
@@ -118,7 +116,6 @@ namespace Opm {
this->init_rates(record);
for( const auto& cmode : modes ) {
if( !record.getItem( cmode.first ).defaultApplied( 0 ) ) {
@@ -202,7 +199,6 @@ namespace Opm {
}
bool WellProductionProperties::operator==(const WellProductionProperties& other) const {
return OilRate == other.OilRate
&& WaterRate == other.WaterRate
@@ -240,7 +236,6 @@ namespace Opm {
<< "THPH: " << wp.THPH << ", "
<< "VFP table: " << wp.VFPTableNumber << ", "
<< "ALQ: " << wp.ALQValue << ", "
<< "WHISTCTL: " << wp.whistctl_cmode << ", "
<< "prediction: " << wp.predictionMode << " }";
}
@@ -255,10 +250,6 @@ namespace Opm {
BHPLimit = 1. * unit::atm;
}
void WellProductionProperties::clearControls() {
m_productionControls = 0;
}
void WellProductionProperties::setBHPLimit(const double limit) {
BHPLimit = limit;
}

View File

@@ -168,8 +168,8 @@ inline void keywordW( SummaryConfig::keyword_list& list,
list.push_back( SummaryConfig::keyword_type( keyword.name(), well_name ));
}
} else
for (const auto& wname : schedule.wellNames())
list.push_back( SummaryConfig::keyword_type( keyword.name(), wname));
for (const auto* well : schedule.getWells())
list.push_back( SummaryConfig::keyword_type( keyword.name(), well->name()));
}
@@ -388,7 +388,7 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
void makeSegmentNodes(const std::size_t last_timestep,
const int segID,
const DeckKeyword& keyword,
const Well* well,
const std::vector<const Well*>& wells,
SummaryConfig::keyword_list& list)
{
// Modifies 'list' in place.
@@ -398,27 +398,30 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
list.push_back(SummaryConfig::keyword_type( keyword.name(), well, segNumber ));
};
if (! isMultiSegmentWell(last_timestep, well))
// Not an MSW. Don't create summary vectors for segments.
return;
for (const auto* well : wells) {
if (! isMultiSegmentWell(last_timestep, well)) {
// Not an MSW. Don't create summary vectors for segments.
continue;
}
const auto& wname = well->name();
if (segID < 1) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments(last_timestep, well);
const auto& wname = well->name();
if (segID < 1) {
// Segment number defaulted. Allocate a summary
// vector for each segment.
const auto nSeg = maxNumWellSegments(last_timestep, well);
for (auto segNumber = 0*nSeg;
segNumber < nSeg; ++segNumber)
for (auto segNumber = 0*nSeg;
segNumber < nSeg; ++segNumber)
{
// One-based segment number.
makeNode(wname, segNumber + 1);
}
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode(wname, segID);
}
else {
// Segment number specified. Allocate single
// summary vector for that segment number.
makeNode(wname, segID);
}
}
}
@@ -437,9 +440,8 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
const auto segID = -1;
for (const auto& well : schedule.getWells())
makeSegmentNodes(last_timestep, segID, keyword,
well, list);
makeSegmentNodes(last_timestep, segID, keyword,
schedule.getWells(), list);
}
void keywordSWithRecords(const std::size_t last_timestep,
@@ -479,9 +481,11 @@ inline void keywordMISC( SummaryConfig::keyword_list& list,
// segment number in record implies all segments.
const auto segID = record.getItem(1).defaultApplied(0)
? -1 : record.getItem(1).get<int>(0);
std::vector<const Well*> wells;
for (const auto& well_name : well_names)
makeSegmentNodes(last_timestep, segID, keyword, schedule.getWell(well_name), list);
wells.push_back( schedule.getWell(well_name) );
makeSegmentNodes(last_timestep, segID, keyword, wells, list);
}
}

View File

@@ -49,7 +49,6 @@ const std::string testHeader =
"auto unitSystem = UnitSystem::newMETRIC();\n";
const std::string sourceHeader =
"#include <opm/parser/eclipse/Deck/UDAValue.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserKeyword.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserItem.hpp>\n"
"#include <opm/parser/eclipse/Parser/ParserRecord.hpp>\n"
@@ -125,7 +124,7 @@ namespace Opm {
newSource << "}" << std::endl;
newSource << "void Parser::addDefaultKeywords() {" << std::endl
<< " Opm::ParserKeywords::addDefaultKeywords(*this);" << std::endl
<< "Opm::ParserKeywords::addDefaultKeywords(*this);" << std::endl
<< "}}" << std::endl;
return write_file( newSource, sourceFile, m_verbose, "source" );

View File

@@ -109,7 +109,6 @@ namespace Opm {
addKey(SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED, InputError::WARN);
addKey(UDQ_PARSE_ERROR, InputError::THROW_EXCEPTION);
this->addKey(SCHEDULE_WELL_ERROR, InputError::THROW_EXCEPTION);
}
void ParseContext::initEnv() {
@@ -343,5 +342,4 @@ namespace Opm {
const std::string ParseContext::SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED = "SIMULATOR_KEYWORD_ITEM_NOT_SUPPORTED";
const std::string ParseContext::UDQ_PARSE_ERROR = "UDQ_PARSE_ERROR";
const std::string ParseContext::SCHEDULE_WELL_ERROR = "SCHEDULE_WELL_ERROR";
}

View File

@@ -94,4 +94,5 @@ namespace Opm {
}
}

View File

@@ -30,7 +30,7 @@
#include <opm/parser/eclipse/Parser/ParserEnums.hpp>
#include <opm/parser/eclipse/RawDeck/RawRecord.hpp>
#include <opm/parser/eclipse/RawDeck/StarToken.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
@@ -47,11 +47,6 @@ template<> const double& default_value< double >() {
return value;
}
template<> const UDAValue& default_value< UDAValue >() {
static const UDAValue value = UDAValue(std::numeric_limits<double>::quiet_NaN());
return value;
}
template<> const std::string& default_value< std::string >() {
static const std::string value = "";
return value;
@@ -62,7 +57,7 @@ type_tag get_data_type_json( const std::string& str ) {
if( str == "DOUBLE" ) return type_tag::fdouble;
if( str == "STRING" ) return type_tag::string;
if( str == "RAW_STRING") return type_tag::string;
if( str == "UDA") return type_tag::uda;
if( str == "UDA") return type_tag::fdouble;
throw std::invalid_argument( str + " cannot be converted to enum 'tag'" );
}
@@ -93,14 +88,7 @@ std::string ParserItem::string_from_size( ParserItem::item_size sz ) {
case item_size::SINGLE: return "SINGLE";
}
throw std::logic_error( "ParserItem::string_from_size: Fatal error; should not be reachable" );
}
template<> const UDAValue& ParserItem::value_ref< UDAValue >() const {
if( this->data_type != get_type< UDAValue >() )
throw std::invalid_argument("ValueRef<UDAValue> Wrong type." );
return this->uval;
throw std::logic_error( "Fatal error; should not be reachable" );
}
template<> const int& ParserItem::value_ref< int >() const {
@@ -176,17 +164,17 @@ ParserItem::ParserItem( const Json::JsonObject& json ) :
this->setDefault( json.get_double( "default" ) );
break;
case itype::UDA:
this->setDefault( UDAValue(json.get_double( "default" )) );
break;
case itype::STRING:
case itype::RAW_STRING:
this->setDefault( json.get_string( "default" ) );
break;
case itype::UDA:
this->setDefault( json.get_double( "default" ) );
break;
default:
throw std::logic_error( "Item of unknown type: <" + json.get_string("value_type") + ">" );
throw std::logic_error( "Item of unknown type." );
}
}
@@ -217,7 +205,7 @@ void ParserItem::setInputType(ParserItem::itype input_type) {
this->setDataType( std::string() );
else if (input_type == itype::UDA)
this->setDataType( UDAValue(0) );
this->setDataType( double() );
else
throw std::invalid_argument("BUG: input type not recognized in setInputType()");
@@ -250,17 +238,22 @@ const T& ParserItem::getDefault() const {
}
bool ParserItem::hasDimension() const {
if( this->data_type != type_tag::fdouble )
return false;
return !this->dimensions.empty();
}
size_t ParserItem::numDimensions() const {
if( this->data_type != type_tag::fdouble ) return 0;
return this->dimensions.size();
}
const std::string& ParserItem::getDimension( size_t index ) const {
if( this->data_type == type_tag::fdouble || this->data_type == type_tag::uda)
return this->dimensions.at( index );
throw std::invalid_argument("Item is not double / UDA .");
if( this->data_type != type_tag::fdouble )
throw std::invalid_argument("Item is not double.");
return this->dimensions.at( index );
}
void ParserItem::push_backDimension( const std::string& dim ) {
@@ -285,10 +278,6 @@ void ParserItem::push_backDimension( const std::string& dim ) {
}
type_tag ParserItem::dataType() const {
return this->data_type;
}
ParserItem::item_size ParserItem::sizeType() const {
return m_sizeType;
@@ -304,18 +293,6 @@ type_tag ParserItem::dataType() const {
void ParserItem::setSizeType(item_size size_type) {
/*
The restriction that data type UDA can only be combined with size_type
SINGLE is due to the way units are bolted on to the Deck
datastructures after the parsing has completed. UDA values are
currently only used as scalars in well/group control and the
restriction does not have any effect. If at some stage in the future
this should change the way units are applied to the deck must be
refactored.
*/
if (this->data_type == type_tag::uda && size_type != item_size::SINGLE)
throw std::invalid_argument("Sorry - the UDA datatype can only be used with size type SINGLE");
this->m_sizeType = size_type;
}
@@ -352,12 +329,8 @@ bool ParserItem::operator==( const ParserItem& rhs ) const {
if( this->sval != rhs.sval ) return false;
break;
case type_tag::uda:
if( this->uval != rhs.uval ) return false;
break;
default:
throw std::logic_error( "Item of unknown type data_type:" + tag_name(this->data_type));
throw std::logic_error( "Item of unknown type." );
}
}
if( this->data_type != type_tag::fdouble ) return true;
@@ -442,13 +415,6 @@ std::string ParserItem::createCode(const std::string& indent) const {
stream << "double(" << as_string(this->getDefault<double>()) << ")";
break;
case type_tag::uda:
{
double double_value =this->getDefault<UDAValue>().get<double>();
stream << "UDAValue(" << as_string(double_value) << ")";
}
break;
case type_tag::string:
stream << "std::string(\"" << this->getDefault< std::string >() << "\")";
break;
@@ -459,9 +425,6 @@ std::string ParserItem::createCode(const std::string& indent) const {
stream << " );" << '\n';
}
for (size_t idim=0; idim < this->numDimensions(); idim++)
stream << indent <<"item.push_backDimension(\"" << this->getDimension( idim ) << "\");" << '\n';
if (this->m_description.size() > 0)
stream << indent << "item.setDescription(\"" << this->m_description << "\");" << '\n';
@@ -577,10 +540,8 @@ DeckItem ParserItem::scan( RawRecord& record ) const {
return scan_item< double >( *this, record );
case type_tag::string:
return scan_item< std::string >( *this, record );
case type_tag::uda:
return scan_item<UDAValue>(*this, record);
default:
throw std::logic_error( "ParserItem::scan: Fatal error; should not be reachable" );
throw std::logic_error( "Fatal error; should not be reachable" );
}
}
@@ -619,16 +580,11 @@ std::string ParserItem::inlineClassInit(const std::string& parentClass,
return std::to_string( this->getDefault< int >() );
case type_tag::fdouble:
return std::to_string( this->getDefault< double >() );
case type_tag::uda:
{
double value = this->getDefault<UDAValue>().get<double>();
return "UDAValue(" + std::to_string(value) + ")";
}
case type_tag::string:
return "\"" + this->getDefault< std::string >() + "\"";
default:
throw std::logic_error( "ParserItem::inlineClassInit: Fatal error; should not be reachable" );
throw std::logic_error( "Fatal error; should not be reachable" );
}
};
@@ -693,16 +649,13 @@ bool ParserItem::parseRaw( ) const {
template void ParserItem::setDefault( int );
template void ParserItem::setDefault( double );
template void ParserItem::setDefault( std::string );
template void ParserItem::setDefault( UDAValue );
template void ParserItem::setDataType( int );
template void ParserItem::setDataType( double );
template void ParserItem::setDataType( std::string );
template void ParserItem::setDataType( UDAValue );
template const int& ParserItem::getDefault() const;
template const double& ParserItem::getDefault() const;
template const std::string& ParserItem::getDefault() const;
template const UDAValue& ParserItem::getDefault() const;
}

View File

@@ -648,6 +648,8 @@ void set_dimensions( ParserItem& item,
{
std::string indent3 = local_indent + " ";
ss << item.createCode(indent3);
for (size_t idim=0; idim < item.numDimensions(); idim++)
ss << indent3 <<"item.push_backDimension(\"" << item.getDimension( idim ) << "\");" << '\n';
{
std::string addItemMethod = "addItem";
if (isDataKeyword())
@@ -699,7 +701,7 @@ void set_dimensions( ParserItem& item,
|| m_keywordSizeType != rhs.m_keywordSizeType
|| isDataKeyword() != rhs.isDataKeyword()
|| m_isTableCollection != rhs.m_isTableCollection )
return false;
return false;
switch( m_keywordSizeType ) {
case FIXED:

View File

@@ -95,30 +95,16 @@ namespace {
void ParserRecord::applyUnitsToDeck( Deck& deck, DeckRecord& deckRecord ) const {
for( const auto& parser_item : *this ) {
if( !parser_item.hasDimension() ) continue;
for( const auto& item : *this ) {
if( !item.hasDimension() ) continue;
auto& deckItem = deckRecord.getItem( parser_item.name() );
for (size_t idim = 0; idim < parser_item.numDimensions(); idim++) {
auto activeDimension = deck.getActiveUnitSystem().getNewDimension( parser_item.getDimension(idim) );
auto defaultDimension = deck.getDefaultUnitSystem().getNewDimension( parser_item.getDimension(idim) );
auto& deckItem = deckRecord.getItem( item.name() );
for (size_t idim = 0; idim < item.numDimensions(); idim++) {
auto activeDimension = deck.getActiveUnitSystem().getNewDimension( item.getDimension(idim) );
auto defaultDimension = deck.getDefaultUnitSystem().getNewDimension( item.getDimension(idim) );
deckItem.push_backDimension( activeDimension , defaultDimension );
}
/*
A little special casing ... the UDAValue elements in the deck must
carry their own dimension. Observe that the
ParserItem::setSizeType() method guarantees that UDA data is
scalar, i.e. this special case loop can be simpler than the
general code in the block above.
*/
if (parser_item.dataType() == type_tag::uda && deckItem.size() > 0) {
auto uda = deckItem.get<UDAValue>(0);
if (deckItem.defaultApplied(0))
uda.set_dim( deck.getDefaultUnitSystem().getNewDimension( parser_item.getDimension(0)));
else
uda.set_dim( deck.getActiveUnitSystem().getNewDimension( parser_item.getDimension(0)));
}
}
}

View File

@@ -28,7 +28,6 @@
#include <opm/parser/eclipse/RawDeck/StarToken.hpp>
#include <opm/parser/eclipse/Utility/Stringview.hpp>
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace qi = boost::spirit::qi;
@@ -106,7 +105,6 @@ namespace Opm {
throw std::invalid_argument( "Malformed floating point number '" + view + "'" );
}
template <>
std::string readValueToken< std::string >( string_view view ) {
if( view.size() == 0 || view[ 0 ] != '\'' )
@@ -118,17 +116,6 @@ namespace Opm {
return view.substr( 1, view.size() - 1 );
}
template<>
UDAValue readValueToken< UDAValue >( string_view view ) {
double n = 0;
qi::real_parser< double, fortran_double< double > > double_;
auto cursor = view.begin();
const auto ok = qi::parse( cursor, view.end(), double_, n );
if( ok && cursor == view.end() ) return UDAValue(n);
return UDAValue( readValueToken<std::string>(view) );
}
void StarToken::init_( const string_view& token ) {
// special-case the interpretation of a lone star as "1*" but do not
// allow constructs like "*123"...

View File

@@ -25,14 +25,6 @@
namespace Opm {
Dimension::Dimension()
{
this->m_name = "Unit";
this->m_SIfactor = 1.0;
this->m_SIoffset = 0.0;
}
Dimension::Dimension(const std::string& name, double SIfactor, double SIoffset)
{
for (auto iter = name.begin(); iter != name.end(); ++iter) {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More