Merge pull request #90 from akva2/restructure_test_utilities_and_add_integration_test

Restructure test utilities and add integration test
This commit is contained in:
Arne Morten Kvarving 2016-09-20 12:40:16 +02:00 committed by GitHub
commit ed9c800fa7
15 changed files with 1978 additions and 736 deletions

View File

@ -0,0 +1,264 @@
/*
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 <opm/test_util/EclFilesComparator.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) 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"
<< "-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"
<< "-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"
<< "-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"
<< "-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 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"
<< "Example 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_TYPE, &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) {
// Restart is default
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE;
// RegressionTest is default
bool integrationTest = false;
bool checkNumKeywords = false;
bool onlyLastOccurrence = false;
bool printKeywords = false;
bool printKeywordsDifference = false;
bool specificKeyword = false;
bool specificFileType = false;
char* keyword = nullptr;
char* fileTypeCstr = nullptr;
int c = 0;
while ((c = getopt(argc, argv, "hiIk:lpPt:")) != -1) {
switch (c) {
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 'l':
onlyLastOccurrence = true;
break;
case 'p':
printKeywords = true;
break;
case 'P':
printKeywordsDifference = true;
break;
case 't':
specificFileType = true;
fileTypeCstr = optarg;
break;
case '?':
if (optopt == 'k') {
std::cerr << "Option k 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;
}
}
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;
return EXIT_FAILURE;
}
std::string basename1 = argv[argOffset];
std::string basename2 = argv[argOffset + 1];
double absTolerance = strtod(argv[argOffset + 2], nullptr);
double relTolerance = strtod(argv[argOffset + 3], nullptr);
if (specificFileType) {
std::string fileTypeString(fileTypeCstr);
for (auto& c: fileTypeString) c = toupper(c);
if (fileTypeString== "UNRST") {} //Do nothing
else if (fileTypeString == "RST") {
concatenateRestart(basename1);
concatenateRestart(basename2);
}
else if (fileTypeString == "RST1") {
concatenateRestart(basename1);
}
else if (fileTypeString == "RST2") {
concatenateRestart(basename2);
}
else if (fileTypeString == "INIT") {
file_type = ECL_INIT_FILE;
}
else if (fileTypeString == "RFT") {
file_type = ECL_RFT_FILE;
}
else {
std::cerr << "Unknown ECLIPSE filetype specified with option -t. Please run compareECL -h to see manual." << std::endl;
return EXIT_FAILURE;
}
}
std::cout << "Comparing '" << basename1 << "' to '" << basename2 << "'." << std::endl;
try {
if (integrationTest) {
IntegrationTest 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 {
RegressionTest comparator(file_type, basename1, basename2, absTolerance, relTolerance);
if (printKeywords) {
comparator.printKeywords();
return 0;
}
if (printKeywordsDifference) {
comparator.printKeywordsDifference();
return 0;
}
if (onlyLastOccurrence) {
comparator.setOnlyLastOccurrence(true);
}
if (specificKeyword) {
comparator.gridCompare();
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare();
comparator.results();
}
}
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -0,0 +1,215 @@
/*
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 <opm/test_util/summaryRegressionTest.hpp>
#include <opm/test_util/summaryIntegrationTest.hpp>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <getopt.h>
void printHelp(){
std::cout << "\n\nThe program can handle both unified and non-unified summary files."<< std::endl;
std::cout <<"In the case of non-unified summary files all the files must be located in the same directory. Only the basename (full path without extension) is needed as input." << std::endl << std::endl;
std::cout << "\nThe program takes four arguments" << std::endl;
std::cout << "1) <path to file1>/<base_name>, basename without extension" << std::endl;
std::cout << "2) <path to file2>/<base_name>, basename without extension" << std::endl;
std::cout << "3) absolute tolerance" << std::endl;
std::cout << "4) relative tolerance (between 0 and 1)" << std::endl;
std::cout << "The program will only throw an exception when both the absolute and relative tolerance are exceeded." << std::endl;
std::cout << "The program is capable of performing both a regression test and an integration test, \nhowever only one type of test at a time. ";
std::cout << "By default the program will run a regression test."<< std::endl;
std::cout << "\nThe program have command line options:" << std::endl;
std::cout << "-h \t\tPrint help message." << std::endl << std::endl;
std::cout << "For the regression test: " << std::endl;
std::cout << "-r \t\tChoosing regression test (this is default)."<< std::endl;
std::cout << "-k keyword \tSpecify a specific keyword to compare, for example - k WOPR:PRODU1."<< std::endl;
std::cout << "-p \t\tWill print the keywords of the files." << std::endl;
std::cout << "-R \t\tWill allow comparison between a restarted simulation and a normal simulation. The files must end at the same time." << std::endl << std::endl;
std::cout << "For the integration test:"<< std::endl;
std::cout << "-i \t\tChoosing integration test." << std::endl;
std::cout << "-d \t\tThe program will not throw an exception when the volume error ratio exceeds the limit." << std::endl;
std::cout << "-g \t\tWill print the vector with the greatest error ratio." << std::endl;
std::cout << "-k keyword \tSpecify a specific keyword to compare, for example - k WOPR:PRODU1."<< std::endl;
std::cout << "-K \t\tWill not allow different amount of keywords in the two files. Throws an exception if the amount are different." << std::endl;
std::cout << "-m mainVar \tWill calculate the error ratio for one main variable. Valid input is WOPR, WWPR, WGPR or WBHP." << std::endl;
std::cout << "-p \t\tWill print the keywords of the files." << std::endl;
std::cout << "-P keyword \tWill print the summary vectors of a specified kewyord, for example -P WOPR:B-3H." << std::endl;
std::cout << "-s int \t\tSets the number of spikes that are allowed for each keyword, for example: -s 5." << std::endl;
std::cout << "-v \t\tFor the rate keywords WOPR, WGPR, WWPR and WBHP. Calculates the error volume of \n\t\tthe two summary files. This is printed to screen." << std::endl;
std::cout << "-V keyword \tWill calculate the error rate for a specific keyword." << std::endl << std::endl;
std::cout << "Suggested combination of command line options:"<< std::endl;
std::cout << " -i -g -m mainVariable, will print the vector which have the greatest error ratio of the main variable of interest.\n"<< std::endl;
}
//---------------------------------------------------
int main (int argc, char ** argv){
//------------------------------------------------
//Defines some constants
bool specificKeyword = false;
bool allowSpikes = false;
bool findVolumeError = false;
bool integrationTest = false;
bool regressionTest = true;
bool allowDifferentAmountOfKeywords = true;
bool printKeywords = false;
bool printSpecificKeyword = false;
bool findVectorWithGreatestErrorRatio = false;
bool oneOfTheMainVariables = false;
bool throwExceptionForTooGreatErrorRatio = true;
bool isRestartFile = false;
const char* keyword = nullptr;
const char* mainVariable = nullptr;
int c = 0;
int limit = -1;
//------------------------------------------------
//------------------------------------------------
//For setting the options selected
while ((c = getopt(argc, argv, "dghik:Km:pP:rRs:vV:")) != -1) {
switch (c) {
case 'd':
throwExceptionForTooGreatErrorRatio = false;
break;
case 'g':
findVectorWithGreatestErrorRatio = true;
throwExceptionForTooGreatErrorRatio = false;
break;
case 'h':
printHelp();
return 0;
case 'i':
integrationTest = true;
regressionTest = false;
break;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 'K':
allowDifferentAmountOfKeywords = false;
break;
case 'm':
oneOfTheMainVariables = true;
mainVariable = optarg;
break;
case 'p':
printKeywords = true;
break;
case 'P':
specificKeyword = true;
printSpecificKeyword = true;
keyword = optarg;
break;
case 'r':
integrationTest = false;
regressionTest = true;
break;
case 'R':
isRestartFile = true;
break;
case 's':
allowSpikes = true;
limit = atof(optarg);
break;
case 'v':
findVolumeError = true;
break;
case 'V':
findVolumeError = true;
specificKeyword = true;
keyword = optarg;
break;
case '?':
if (optopt == 'k' || optopt == 'm' || optopt == 's') {
std::cout << "Option requires an keyword." << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
//------------------------------------------------
int argOffset = optind;
if (argc != argOffset + 4) {
printHelp();
return EXIT_FAILURE;
}
const char * basename1 = argv[argOffset];
const char * basename2 = argv[argOffset+1];
double absoluteTolerance = strtod(argv[argOffset+2], nullptr);
double relativeTolerance = strtod(argv[argOffset+3], nullptr);
std::cout << "Comparing '" << basename1 << "' to '" << basename2 << "'." << std::endl;
try {
if(regressionTest){
RegressionTest compare(basename1,basename2, absoluteTolerance, relativeTolerance);
if(printKeywords){compare.setPrintKeywords(true);}
if(isRestartFile){compare.setIsRestartFile(true);}
if(specificKeyword){
compare.getRegressionTest(keyword);
}
else{
if(printKeywords){compare.setPrintKeywords(true);}
compare.getRegressionTest();
}
}
if(integrationTest){
IntegrationTest compare(basename1,basename2, absoluteTolerance, relativeTolerance);
if(findVectorWithGreatestErrorRatio){compare.setFindVectorWithGreatestErrorRatio(true);}
if(allowSpikes){compare.setAllowSpikes(true);}
if(oneOfTheMainVariables){
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.");
}
}
if(findVolumeError){compare.setFindVolumeError(true);}
if(limit != -1){compare.setSpikeLimit(limit);}
if(!allowDifferentAmountOfKeywords){compare.setAllowDifferentAmountOfKeywords(false);}
if(printKeywords){compare.setPrintKeywords(true);}
if(!throwExceptionForTooGreatErrorRatio){compare.setThrowExceptionForTooGreatErrorRatio(false);}
if(specificKeyword){
if(printSpecificKeyword){compare.setPrintSpecificKeyword(true);}
compare.getIntegrationTest(keyword);
return 0;
}
compare.getIntegrationTest();
}
}
catch(const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -1,102 +0,0 @@
/*
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 <opm/test_util/EclFilesComparator.hpp>
#include <iostream>
#include <string>
#include <getopt.h>
static void printHelp() {
std::cout << "compareRestart compares restartfiles and gridsizes from two simulations.\n"
<< "The program takes four arguments:\n\n"
<< "1. File number 1 (full path without extension)\n"
<< "2. File 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"
<< "-h Print help.\n"
<< "-k Specify specific keyword to compare, for example -k PRESSURE.\n"
<< "-s Print all values side by side from the specified files.\n\n";
}
//------------------------------------------------//
int main(int argc, char** argv) {
bool showValues = false;
bool specificKeyword = false;
char* keyword = nullptr;
int c = 0;
while ((c = getopt(argc, argv, "hk:s")) != -1) {
switch (c) {
case 'h':
printHelp();
return 0;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 's':
showValues = true;
break;
case '?':
if (optopt == 'k') {
std::cout << "Option k requires an argument." << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if (argc != argOffset + 4) {
printHelp();
return EXIT_FAILURE;
}
const std::string basename1 = argv[argOffset];
const std::string basename2 = argv[argOffset + 1];
const double absTolerance = atof(argv[argOffset + 2]);
const double relTolerance = atof(argv[argOffset + 3]);
try {
ECLFilesComparator comparator(INITFILE, basename1, basename2, absTolerance, relTolerance);
if (showValues) {
comparator.setShowValues(true);
}
if (specificKeyword) {
comparator.gridCompare();
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare();
comparator.results();
}
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -1,102 +0,0 @@
/*
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 <opm/test_util/EclFilesComparator.hpp>
#include <iostream>
#include <string>
#include <getopt.h>
static void printHelp() {
std::cout << "compareRestart compares restartfiles and gridsizes from two simulations.\n"
<< "The program takes four arguments:\n\n"
<< "1. File number 1 (full path without extension)\n"
<< "2. File 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"
<< "-h Print help.\n"
<< "-k Specify specific keyword to compare, for example -k PRESSURE.\n"
<< "-s Print all values side by side from the specified files.\n\n";
}
//------------------------------------------------//
int main(int argc, char** argv) {
bool showValues = false;
bool specificKeyword = false;
char* keyword = nullptr;
int c = 0;
while ((c = getopt(argc, argv, "hk:s")) != -1) {
switch (c) {
case 'h':
printHelp();
return 0;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 's':
showValues = true;
break;
case '?':
if (optopt == 'k') {
std::cout << "Option k requires an argument." << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if (argc != argOffset + 4) {
printHelp();
return EXIT_FAILURE;
}
const std::string basename1 = argv[argOffset];
const std::string basename2 = argv[argOffset + 1];
const double absTolerance = atof(argv[argOffset + 2]);
const double relTolerance = atof(argv[argOffset + 3]);
try {
ECLFilesComparator comparator(RESTARTFILE, basename1, basename2, absTolerance, relTolerance);
if (showValues) {
comparator.setShowValues(true);
}
if (specificKeyword) {
comparator.gridCompare();
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare();
comparator.results();
}
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -1,104 +0,0 @@
/*
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 <opm/test_util/EclFilesComparator.hpp>
#include <iostream>
#include <string>
#include <getopt.h>
static void printHelp() {
std::cout << "compareRestart compares restartfiles and gridsizes from two simulations.\n"
<< "The program takes four arguments:\n\n"
<< "1. File number 1 (full path without extension)\n"
<< "2. File 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"
<< "-h Print help.\n"
<< "-k Specify specific keyword to compare, for example -k PRESSURE.\n"
<< "-s Print all values side by side from the specified files.\n\n";
}
//------------------------------------------------//
int main(int argc, char** argv) {
bool showValues = false;
bool specificKeyword = false;
char* keyword = nullptr;
int c = 0;
while ((c = getopt(argc, argv, "hk:s")) != -1) {
switch (c) {
case 'h':
printHelp();
return 0;
case 'k':
specificKeyword = true;
keyword = optarg;
break;
case 's':
showValues = true;
break;
case '?':
if (optopt == 'k') {
std::cout << "Option k requires an argument." << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if (argc != argOffset + 4) {
printHelp();
return EXIT_FAILURE;
}
const std::string basename1 = argv[argOffset];
const std::string basename2 = argv[argOffset + 1];
const double absTolerance = atof(argv[argOffset + 2]);
const double relTolerance = atof(argv[argOffset + 3]);
try {
std::cout << "\nUsing absolute deviation tolerance of " << absTolerance
<< " and relative tolerance of " << relTolerance << ".\n";
ECLFilesComparator comparator(RFTFILE, basename1, basename2, absTolerance, relTolerance);
if (showValues) {
comparator.setShowValues(true);
}
if (specificKeyword) {
comparator.gridCompare();
comparator.resultsForKeyword(keyword);
}
else {
comparator.gridCompare();
comparator.results();
}
}
catch (const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -1,87 +0,0 @@
/*
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.
char* unit = getUnit(keyword);
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/test_util/summaryRegressionTest.hpp>
#include <string>
#include <getopt.h>
void printHelp(); //< Declare global function which prints help text
void printHelp(){
std::cout << "The program takes four arguments" << std::endl;
std::cout << "1) <path to file1>/<base_name>" << std::endl;
std::cout << "2) <path to file2>/<base_name>" << std::endl;
std::cout << "the basename should be without ectension." << std::endl;
std::cout << "3) relative tolerance (between 0 and 1)" << std::endl;
std::cout << "4) absolute tolerance" << std::endl;
}
//---------------------------------------------------
int main (int argc, char ** argv){
bool spesificKeyword = false;
const char* keyword = nullptr;
int c = 0;
while ((c = getopt(argc, argv, "hk:")) != -1) {
switch (c) {
case 'h':
printHelp();
return 0;
case 'k':
spesificKeyword = true;
keyword = optarg;
break;
case '?':
if (optopt == 'k') {
std::cout << "Option k requires an keyword." << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Unknown option." << std::endl;
return EXIT_FAILURE;
}
default:
return EXIT_FAILURE;
}
}
int argOffset = optind;
if (argc != argOffset + 4) {
printHelp();
return EXIT_FAILURE;
}
const char * basename1 = argv[argOffset];
const char * basename2 = argv[argOffset+1];
double absoluteTolerance = atof(argv[argOffset+2]);
double relativeTolerance = atof(argv[argOffset+3]);
try {
RegressionTest read(basename1,basename2, absoluteTolerance, relativeTolerance);
if(spesificKeyword){
read.getRegressionTest(keyword);
}
else{
read.getRegressionTest();
}
}
catch(const std::exception& e) {
std::cerr << "Program threw an exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return 0;
}

View File

@ -23,79 +23,88 @@
#include <vector>
#include <string>
/// Prototype for eclipse file struct, defined in the ERT library
struct ecl_file_struct;
struct ecl_file_struct; //!< Prototype for eclipse file struct, from ERT library.
typedef struct ecl_file_struct ecl_file_type;
/// Prototype for eclipse grid struct, defined in the ERT library
struct ecl_grid_struct;
struct ecl_grid_struct; //!< Prototype for eclipse grid struct, from ERT library.
typedef struct ecl_grid_struct ecl_grid_type;
enum eclFileEnum {RESTARTFILE, INITFILE};
struct ecl_kw_struct; //!< Prototype for eclipse keyword struct, from ERT library.
typedef struct ecl_kw_struct ecl_kw_type;
//! Deviation struct.
/*! The member variables are default initialized to -1, which is an invalid deviation value.*/
/*! \brief Deviation struct.
\details The member variables are default initialized to -1,
which is an invalid deviation value.
*/
struct Deviation {
double abs = -1;
double rel = -1;
double abs = -1; //!< Absolute deviation
double rel = -1; //!< Relative deviation
};
//! A class for handeling ECLIPSE files.
/*! ECLFilesComparator opens ECLIPSE files (.UNRST or .INIT in addition to .EGRID or .GRID) from two simulations.
* Then the functions gridCompare, results and resultsForKeyword can be invoked to compare the files from the simulations.
* Note that when either absolute or relative deviation is larger than the member variables (double) absTolerance and (double) relTolerance respectively,
* an exception is thrown. This is also the case if a value is negative and its absolute value is larger than absTolerance. */
/*! \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 {
private:
eclFileEnum fileType;
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;
double absTolerance = 0;
double relTolerance = 0;
bool showValues = false;
std::vector<std::string> keywords1, keywords2;
std::vector<double> absDeviation, relDeviation;
// Keywords which should not contain negative values, i.e. uses allowNegativeValues = false in deviationsForCell():
const std::vector<std::string> keywordWhitelist = {"SGAS", "SWAT", "PRESSURE"};
//! \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 cell, const T& value1, const T& value2) const;
bool keywordValidForComparing(const std::string& keyword) const;
void printResultsForKeyword(const std::string& keyword) const;
void deviationsForOccurence(const std::string& keyword, int occurence);
void deviationsForCell(double val1, double val2, const std::string& keyword, int occurence, int cell, bool allowNegativeValues = true);
public:
//! \brief Open ECLIPSE files and set tolerances and keywords.
//! \param[in] fileType Specifies which filetype to be compared, possible inputs are "RESTARTFILE" and "INITFILE".
//! \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(eclFileEnum fileType, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
ECLFilesComparator(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Closing the ECLIPSE files.
~ECLFilesComparator();
//! \brief Setting the member variable showValues.
//! \param[in] values Variable which is copied to #showValues
//! \details When showValues is set to true, resultsForKeyword() will print all values side by side from the two files.
void setShowValues(bool values) {this->showValues = values;}
//! \brief Print all keywords stored in #keywords1 and #keywords2
//! \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;}
//! \brief Print all keywords and their respective Eclipse type for the two input cases.
void printKeywords() const;
//! \brief Comparing grids from the to gridfiles.
//! \details This functions compares the number of active and global cells of the two grids. If the number of cells differ, an exception is thrown.
bool gridCompare() const;
//! \brief Calculating deviations for all keywords.
//! \details results() loops over all keywords, checks if they are supported for comparison, and calls resultsForKeyword() for each keyword.
void results();
//! \brief Calculating deviations for a specific keyword.
//! \param[in] keyword Keyword which should be compared.
//! \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 #absTolerance or #relTolerance respectively, or a value is both negative and has a larger absolute value than #absTolerance, an exception is thrown. The function also temporarily stores the absolute and relative deviation in #absDeviation and #relDeviation, and prints average and median values.
void resultsForKeyword(const std::string keyword);
//! \brief Print common and uncommon keywords for the two input cases.
void printKeywordsDifference() const;
//! \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.
@ -108,4 +117,121 @@ class ECLFilesComparator {
static double average(const std::vector<double>& vec);
};
/*! \brief A class for executing a regression 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 gridCompare(), results() and
resultsForKeyword() can be invoked to compare griddata
or keyworddata for all keywords or a given keyword (resultsForKeyword()).
*/
class RegressionTest: public ECLFilesComparator {
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"};
// Only compare last occurrence
bool onlyLastOccurrence = false;
// Prints results stored in absDeviation and relDeviation.
void printResultsForKeyword(const std::string& keyword) const;
// 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 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.
RegressionTest(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(file_type, basename1, basename2, absTolerance, relTolerance) {}
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrence) {this->onlyLastOccurrence = onlyLastOccurrence;}
//! \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, 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;
//! \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);
};
/*! \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 IntegrationTest: 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.
IntegrationTest(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

@ -17,7 +17,11 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUMMARYCOMPARATOR_HPP
#define SUMMARYCOMPARATOR_HPP
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <string>
@ -25,13 +29,12 @@
//! \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;
//! \brief Struct for storing the deviation between two values.
struct Deviation {
double abs = 0;//!< The absolute deviation
@ -39,12 +42,10 @@ struct Deviation {
};
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.
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
@ -54,10 +55,12 @@ class SummaryComparator {
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
std::vector<double> * referenceVec = nullptr; //!< For storing the values of each time step for the file containing the fewer time steps.
std::vector<double> * referenceDataVec = nullptr; //!< For storing the data corresponding to each time step for the file containing the fewer time steps.
std::vector<double> * checkVec = nullptr; //!< For storing the values of each time step for the file containing the more time steps.
std::vector<double> * checkDataVec = nullptr; //!< For storing the data values corresponding to each time step for the file containing the more time steps.
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
//! \brief Calculate deviation between two data values and stores it in a Deviation struct.
//! \param[in] refIndex Index in reference data
@ -69,19 +72,21 @@ class SummaryComparator {
//! \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 cointains 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(std::vector<double> &timeVec1,std::vector<double> &timeVec2);
//! \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.
//! \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.
//! \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);
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.
@ -89,7 +94,10 @@ class SummaryComparator {
//! \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(std::vector<double> &timeVec1,std::vector<double> &timeVec2,std::vector<double> &dataVec1,std::vector<double> &dataVec2);
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;}
@ -101,8 +109,20 @@ class SummaryComparator {
//! \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.
@ -111,6 +131,7 @@ class SummaryComparator {
//! \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 char* basename1, const char* basename2, double absoluteTolerance, double relativeTolerance);
//! \brief Destructor
//! \details The destructor takes care of the allocated memory in which data has been stored.
~SummaryComparator();
@ -121,20 +142,25 @@ class SummaryComparator {
//! \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);
static double median(std::vector<double> vec);
//! \brief Function which linearly interpolates a value
//! \param[in] checkValue
//! \param[in] checkValuePrev
//! \param[in] timeArray[3] Array of doubles which contains the times of interest. \n timeArray[0]: The value of the time step in which checkVec exceeds the reference time, i.e. the first j which satisfies checkVec[j] > referenceVec[i].\n timeArray[1]: The value of the time step before checkVec exceeds referenceVec[i], i.e. checkVec[j-1].\n timeArray[2]: The value of the time step at which the comparison is to be comitted, i.e. referenceVec[i].
//! \param[out] ret Returns the interpolated value.
//! \details In this case: The interpolation generates a value from the principle of linear polation.
static double interpolation(double checkValue, double checkValuePrev, double timeArray[3]);
//! \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.
static double unitStep(double value);//!< Returns a value based on the unit step principle.
static double average(std::vector<double> &vec);
//! \brief Returns a value based on the unit step principle.
static double unitStep(double value){return value;}
};
#endif

View File

@ -0,0 +1,214 @@
/*
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 <opm/test_util/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 IntegrationTest: 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);
//! \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);
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.
IntegrationTest(const char* basename1, const char* 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 allowSpikes){this->allowSpikes = allowSpikes;}
//! \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 findVolumeError){this->findVolumeError = findVolumeError;}
//! \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 oneOfTheMainVariables){this->oneOfTheMainVariables = oneOfTheMainVariables;}
//! \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

@ -17,6 +17,9 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUMMARYREGRESSIONTEST_HPP
#define SUMMARYREGRESSIONTEST_HPP
#include <opm/test_util/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.
@ -37,10 +40,12 @@ class RegressionTest: public SummaryComparator {
//! \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] reportStep The report step of which the deviation originates from.
//! \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 throwing an exception.
void checkDeviation(Deviation deviation, const char* keyword, int reportStep);
void 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
public:
//! \brief Constructor, creates an object of RefressionTest class.
//! \param[in] basename1 Path to file1 without extension.
@ -56,4 +61,10 @@ class RegressionTest: public SummaryComparator {
//! \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;}
};
#endif

View File

@ -20,173 +20,105 @@
#include <opm/common/ErrorMacros.hpp>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <cmath>
#include <numeric>
bool ECLFilesComparator::keywordValidForComparing(const std::string& keyword) const {
void ECLFilesComparator::keywordValidForComparing(const std::string& keyword) const {
auto it = std::find(keywords1.begin(), keywords1.end(), keyword);
if (it == keywords1.end()) {
std::cout << "Could not find keyword: " << keyword << std::endl;
OPM_THROW(std::runtime_error, "Keyword does not exist in first file.");
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in first file.");
}
it = find(keywords2.begin(), keywords2.end(), keyword);
if (it == keywords2.end()) {
std::cout << "Could not find keyword: " << keyword << std::endl;
OPM_THROW(std::runtime_error, "Keyword does not exist in second file.");
}
ecl_kw_type* kw = ecl_file_iget_named_kw(ecl_file1, keyword.c_str(), 0);
ecl_type_enum kw_type = ecl_kw_get_type(kw);
switch (kw_type) {
case ECL_DOUBLE_TYPE:
return true;
case ECL_FLOAT_TYPE:
return true;
case ECL_CHAR_TYPE:
std::cout << "\nKeyword " << keyword << " is of type ECL_CHAR_TYPE, "
<< "which is not supported for comparison." << std::endl;
return false;
case ECL_INT_TYPE:
std::cout << "\nKeyword " << keyword << " is of type ECL_INT_TYPE, "
<< "which is not supported for comparison." << std::endl;
return false;
case ECL_BOOL_TYPE:
std::cout << "\nKeyword " << keyword << " is of type ECL_BOOL_TYPE, "
<< "which is not supported for comparison." << std::endl;
return false;
case ECL_MESS_TYPE:
std::cout << "\nKeyword " << keyword << " is of type ECL_MESS_TYPE, "
<< "which is not supported for comparison." << std::endl;
return false;
default:
std::cout << "\nKeyword " << keyword << "has undefined type." << std::endl;
return false;
OPM_THROW(std::runtime_error, "Keyword " << keyword << " does not exist in second file.");
}
}
void ECLFilesComparator::printResultsForKeyword(const std::string& keyword) const {
const double absDeviationAverage = average(absDeviation);
const double relDeviationAverage = average(relDeviation);
std::cout << "Average absolute deviation = " << absDeviationAverage << std::endl;
std::cout << "Median absolute deviation = " << median(absDeviation) << std::endl;
std::cout << "Average relative deviation = " << relDeviationAverage << std::endl;
std::cout << "Median relative deviation = " << median(relDeviation) << "\n\n";
}
void ECLFilesComparator::deviationsForOccurence(const std::string& keyword, int occurence) {
ecl_kw_type* ecl_kw1 = ecl_file_iget_named_kw(ecl_file1, keyword.c_str(), occurence);
ecl_kw_type* ecl_kw2 = ecl_file_iget_named_kw(ecl_file2, keyword.c_str(), occurence);
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) {
std::cout << "For keyword " << keyword << " and occurence " << occurence << ":\n";
OPM_THROW(std::runtime_error, "Number of active cells are different.");
}
std::vector<double> values1(numCells1), values2(numCells2);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, values2.data());
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
for (unsigned int cell = 0; cell < values1.size(); cell++) {
deviationsForCell(values1[cell], values2[cell], keyword, occurence, cell, it == keywordWhitelist.end());
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;
}
void ECLFilesComparator::deviationsForCell(double val1, double val2, const std::string& keyword, int occurence, int cell, bool allowNegativeValues) {
template <typename T>
void ECLFilesComparator::printValuesForCell(const std::string& keyword, int occurrence1, int occurrence2, size_t cell, const T& value1, const T& value2) const {
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++;
if (showValues) {
std::cout << "Occurence = " << occurence << std::endl;
std::cout << "Grid coordinate = (" << i << ", " << j << ", " << k << ")\n";
std::cout << "(value1, value2) = (" << val1 << ", " << val2 << ")\n\n";
}
if (!allowNegativeValues) {
if (val1 < 0) {
if (std::abs(val1) > absTolerance) {
std::cout << "For keyword " << keyword << " (which is listed for positive values only), occurence " << occurence
<< ", and grid coordinate (" << i << ", " << j << ", " << k << "):\n"
<< "the vector has a value of " << val1 << ", which exceeds the absolute tolerance of " << absTolerance << ".\n";
OPM_THROW(std::runtime_error, "Negative value in first file.");
}
val1 = 0;
}
if (val2 < 0) {
if (std::abs(val2) > absTolerance) {
std::cout << "For keyword " << keyword << " (which is listed for positive values only), occurence " << occurence
<< ", and grid coordinate (" << i << ", " << j << ", " << k << "):\n"
<< "the vector has a value of " << val2 << ", which exceeds the absolute tolerance of " << absTolerance << ".\n";
OPM_THROW(std::runtime_error, "Negative value in second file.");
}
val2 = 0;
}
}
Deviation dev = calculateDeviations(val1, val2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
std::cout << "For keyword " << keyword << ", occurence " << occurence
<< ", and grid coordinate (" << i << ", " << j << ", " << k << "):\n"
<< "(first value, second value) = (" << val1 << ", " << val2 << ")\n";
std::cout << "The absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << ".\n";
std::cout << "The relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".\n";
OPM_THROW(std::runtime_error, "Absolute deviation exceeds tolerance.");
}
if (dev.abs != -1) {
absDeviation.push_back(dev.abs);
}
if (dev.rel != -1) {
relDeviation.push_back(dev.rel);
}
std::cout << std::endl
<< "Occurrence in first file = " << occurrence1 << "\n"
<< "Occurrence in second file = " << occurrence2 << "\n"
<< "Grid coordinate = (" << i << ", " << j << ", " << k << ")" << "\n"
<< "(first value, second value) = (" << value1 << ", " << value2 << ")\n\n";
}
template void ECLFilesComparator::printValuesForCell<bool> (const std::string& keyword, int occurrence1, int occurrence2, 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 cell, const int& value1, const int& value2) const;
template void ECLFilesComparator::printValuesForCell<double> (const std::string& keyword, int occurrence1, int occurrence2, 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 cell, const std::string& value1, const std::string& value2) const;
ECLFilesComparator::ECLFilesComparator(eclFileEnum eclFileType, const std::string& basename1, const std::string& basename2, double absTol, double relTol):
fileType(eclFileType), absTolerance(absTol), relTolerance(relTol) {
ECLFilesComparator::ECLFilesComparator(int file_type, const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance) :
file_type(file_type), absTolerance(absTolerance), relTolerance(relTolerance) {
std::string file1, file2;
if (fileType == RESTARTFILE) {
if (file_type == ECL_UNIFIED_RESTART_FILE) {
file1 = basename1 + ".UNRST";
file2 = basename2 + ".UNRST";
}
else if (fileType == INITFILE) {
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) {
std::cout << "Tried to open: " << file1 << std::endl;
OPM_THROW(std::runtime_error, "Error opening first file.");
OPM_THROW(std::invalid_argument, "Error opening first file: " << file1);
}
if (ecl_file2 == nullptr) {
std::cout << "Tried to open: " << file2 << std::endl;
OPM_THROW(std::runtime_error, "Error opening second file.");
OPM_THROW(std::invalid_argument, "Error opening second file: " << file2);
}
if (ecl_grid1 == nullptr) {
std::cout << "Tried to open: " << basename1 + ".EGRID" << std::endl;
OPM_THROW(std::runtime_error, "Error opening first file.");
OPM_THROW(std::invalid_argument, "Error opening first grid file: " << basename1);
}
if (ecl_grid2 == nullptr) {
std::cout << "Tried to open: " << basename2 + ".EGRID" << std::endl;
OPM_THROW(std::runtime_error, "Error opening second file.");
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);
@ -209,58 +141,40 @@ ECLFilesComparator::~ECLFilesComparator() {
void ECLFilesComparator::printKeywords() const {
std::cout << "\nKeywords for first file:\n";
for (const auto& it : keywords1) std::cout << it << std::endl;
std::cout << "\nKeywords for second file:\n";
for (const auto& it : keywords2) std::cout << it << std::endl;
std::cout << "\nKeywords in the first file:\n";
for (const auto& it : keywords1) {
std::cout << std::setw(15) << std::left << it << " of type " << ecl_util_get_type_name(ecl_file_iget_named_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_util_get_type_name(ecl_file_iget_named_type(ecl_file2, it.c_str(), 0)) << std::endl;
}
}
bool ECLFilesComparator::gridCompare() const {
const int globalGridCount1 = ecl_grid_get_global_size(ecl_grid1);
const int activeGridCount1 = ecl_grid_get_active_size(ecl_grid1);
const int globalGridCount2 = ecl_grid_get_global_size(ecl_grid2);
const int activeGridCount2 = ecl_grid_get_active_size(ecl_grid2);
if (globalGridCount1 != globalGridCount2) {
OPM_THROW(std::runtime_error, "Grid file: The total number of cells are different.");
return false;
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;
}
if (activeGridCount1 != activeGridCount2) {
OPM_THROW(std::runtime_error, "Grid file: The number of active cells are different.");
return false;
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);
}
}
return true;
}
void ECLFilesComparator::results() {
if (keywords1.size() != keywords2.size()) {
OPM_THROW(std::runtime_error, "Number of keywords are not equal.");
}
for (const auto& it : keywords1) resultsForKeyword(it);
}
void ECLFilesComparator::resultsForKeyword(const std::string keyword) {
std::cout << "\nKeyword " << keyword << ":\n\n";
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) {
std::cout << "For keyword " << keyword << ":\n";
OPM_THROW(std::runtime_error, "Number of keyword occurrences are not equal.");
}
if (!keywordValidForComparing(keyword)) {
return;
}
for (unsigned int occurence = 0; occurence < occurrences1; ++occurence) {
deviationsForOccurence(keyword, occurence);
}
printResultsForKeyword(keyword);
absDeviation.clear();
relDeviation.clear();
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;
}
@ -305,3 +219,405 @@ double ECLFilesComparator::average(const std::vector<double>& vec) {
double sum = std::accumulate(vec.begin(), vec.end(), 0.0);
return sum/vec.size();
}
void RegressionTest::printResultsForKeyword(const std::string& keyword) const {
std::cout << "Deviation results for keyword " << keyword << " of type "
<< ecl_util_get_type_name(ecl_file_iget_named_type(ecl_file1, keyword.c_str(), 0))
<< ":\n";
const double absDeviationAverage = average(absDeviation);
const double relDeviationAverage = average(relDeviation);
std::cout << "Average absolute deviation = " << absDeviationAverage << std::endl;
std::cout << "Median absolute deviation = " << median(absDeviation) << std::endl;
std::cout << "Average relative deviation = " << relDeviationAverage << std::endl;
std::cout << "Median relative deviation = " << median(relDeviation) << "\n\n";
}
void RegressionTest::boolComparisonForOccurrence(const std::string& keyword,
int occurrence1, int occurrence2) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence1, occurrence2);
for (size_t cell = 0; cell < numCells; cell++) {
bool data1 = ecl_kw_iget_bool(ecl_kw1, cell);
bool data2 = ecl_kw_iget_bool(ecl_kw2, cell);
if (data1 != data2) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, data1, data2);
OPM_THROW(std::runtime_error, "Values of bool type differ.");
}
}
}
void RegressionTest::charComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence1, occurrence2);
for (size_t cell = 0; cell < numCells; cell++) {
std::string data1(ecl_kw_iget_char_ptr(ecl_kw1, cell));
std::string data2(ecl_kw_iget_char_ptr(ecl_kw2, cell));
if (data1.compare(data2) != 0) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, data1, data2);
OPM_THROW(std::runtime_error, "Values of char type differ.");
}
}
}
void RegressionTest::intComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence1, occurrence2);
std::vector<int> values1(numCells), values2(numCells);
ecl_kw_get_memcpy_int_data(ecl_kw1, values1.data());
ecl_kw_get_memcpy_int_data(ecl_kw2, values2.data());
for (size_t cell = 0; cell < values1.size(); cell++) {
if (values1[cell] != values2[cell]) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, values1[cell], values2[cell]);
OPM_THROW(std::runtime_error, "Values of int type differ.");
}
}
}
void RegressionTest::doubleComparisonForOccurrence(const std::string& keyword, int occurrence1, int occurrence2) {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence1, occurrence2);
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());
auto it = std::find(keywordDisallowNegatives.begin(), keywordDisallowNegatives.end(), keyword);
for (size_t cell = 0; cell < values1.size(); cell++) {
deviationsForCell(values1[cell], values2[cell], keyword, occurrence1, occurrence2, cell, it == keywordDisallowNegatives.end());
}
}
void RegressionTest::deviationsForCell(double val1, double val2, const std::string& keyword, int occurrence1, int occurrence2, size_t cell, bool allowNegativeValues) {
double absTolerance = getAbsTolerance();
double relTolerance = getRelTolerance();
if (!allowNegativeValues) {
if (val1 < 0) {
if (std::abs(val1) > absTolerance) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, val1, val2);
OPM_THROW(std::runtime_error, "Negative value in first file, "
<< "which in absolute value exceeds the absolute tolerance of " << absTolerance << ".");
}
val1 = 0;
}
if (val2 < 0) {
if (std::abs(val2) > absTolerance) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, val1, val2);
OPM_THROW(std::runtime_error, "Negative value in second file, "
<< "which in absolute value exceeds the absolute tolerance of " << absTolerance << ".");
}
val2 = 0;
}
}
Deviation dev = calculateDeviations(val1, val2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
printValuesForCell(keyword, occurrence1, occurrence2, cell, val1, val2);
OPM_THROW(std::runtime_error, "Deviations exceed tolerances."
<< "\nThe absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << "."
<< "\nThe relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".");
}
if (dev.abs != -1) {
absDeviation.push_back(dev.abs);
}
if (dev.rel != -1) {
relDeviation.push_back(dev.rel);
}
}
void RegressionTest::gridCompare() const {
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 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 cells differ.");
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const double cellVolume1 = ecl_grid_get_cell_volume1(ecl_grid1, cell);
const double cellVolume2 = ecl_grid_get_cell_volume1(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 << ".");
}
}
}
void RegressionTest::results() {
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.");
}
for (const auto& it : keywords1)
resultsForKeyword(it);
}
void RegressionTest::resultsForKeyword(const std::string 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 (!onlyLastOccurrence && 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.");
}
// Assuming keyword type is constant for every occurrence:
const ecl_type_enum kw_type = ecl_file_iget_named_type(ecl_file1, keyword.c_str(), 0);
switch(kw_type) {
case ECL_DOUBLE_TYPE:
case ECL_FLOAT_TYPE:
std::cout << "Comparing " << keyword << "...";
if (onlyLastOccurrence) {
doubleComparisonForOccurrence(keyword, occurrences1 - 1, occurrences2 - 1);
}
else {
for (unsigned int occurrence = 0; occurrence < occurrences1; ++occurrence) {
doubleComparisonForOccurrence(keyword, occurrence, occurrence);
}
}
std::cout << "done." << std::endl;
printResultsForKeyword(keyword);
absDeviation.clear();
relDeviation.clear();
return;
case ECL_INT_TYPE:
std::cout << "Comparing " << keyword << "...";
if (onlyLastOccurrence) {
intComparisonForOccurrence(keyword, occurrences1 - 1, occurrences2 - 1);
}
else {
for (unsigned int occurrence = 0; occurrence < occurrences1; ++occurrence) {
intComparisonForOccurrence(keyword, occurrence, occurrence);
}
}
break;
case ECL_CHAR_TYPE:
std::cout << "Comparing " << keyword << "...";
if (onlyLastOccurrence) {
charComparisonForOccurrence(keyword, occurrences1 - 1, occurrences2 - 1);
}
else {
for (unsigned int occurrence = 0; occurrence < occurrences1; ++occurrence) {
charComparisonForOccurrence(keyword, occurrence, occurrence);
}
}
break;
case ECL_BOOL_TYPE:
std::cout << "Comparing " << keyword << "...";
if (onlyLastOccurrence) {
boolComparisonForOccurrence(keyword, occurrences1 - 1, occurrences2 - 1);
}
else {
for (unsigned int occurrence = 0; occurrence < occurrences1; ++occurrence) {
boolComparisonForOccurrence(keyword, occurrence, occurrence);
}
}
break;
case ECL_MESS_TYPE:
std::cout << "\nKeyword " << keyword << " is of type "
<< ecl_util_get_type_name(kw_type)
<< ", which is not supported in regression test." << "\n\n";
return;
default:
std::cout << "\nKeyword " << keyword << "has undefined type." << std::endl;
return;
}
std::cout << "done." << std::endl;
}
void IntegrationTest::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 = ecl_grid_get_cell_volume1(ecl_grid1, cell);
const double cellVolume2 = ecl_grid_get_cell_volume1(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 IntegrationTest::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 IntegrationTest::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.");
}
}
}
IntegrationTest::IntegrationTest(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 IntegrationTest::elementInWhitelist(const std::string& keyword) const {
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
return it != keywordWhitelist.end();
}
void IntegrationTest::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 IntegrationTest::results() {
for (const auto& it : keywordWhitelist)
resultsForKeyword(it);
}
void IntegrationTest::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

@ -50,7 +50,6 @@ SummaryComparator::SummaryComparator(const char* basename1, const char* basename
}
SummaryComparator::~SummaryComparator(){
ecl_sum_free(ecl_sum1);
ecl_sum_free(ecl_sum2);
@ -59,7 +58,6 @@ SummaryComparator::~SummaryComparator(){
}
Deviation SummaryComparator::calculateDeviations(double val1, double val2){
double absDev, relDev;
Deviation deviation;
@ -73,8 +71,8 @@ Deviation SummaryComparator::calculateDeviations(double val1, double val2){
}
void SummaryComparator::setTimeVecs(std::vector<double> &timeVec1,std::vector<double> &timeVec2){
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 ));
@ -86,8 +84,9 @@ void SummaryComparator::setTimeVecs(std::vector<double> &timeVec1,std::vector<do
}
void SummaryComparator::getDataVecs(std::vector<double> &dataVec1,std::vector<double> &dataVec2,const char* keyword){
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 )));
@ -100,9 +99,9 @@ void SummaryComparator::getDataVecs(std::vector<double> &dataVec1,std::vector<do
}
void SummaryComparator::setDataSets(std::vector<double> &timeVec1,std::vector<double> &timeVec2){
if(timeVec1.size()< timeVec2.size()){
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;
}
@ -113,8 +112,10 @@ void SummaryComparator::setDataSets(std::vector<double> &timeVec1,std::vector<do
}
void SummaryComparator::chooseReference(std::vector<double> &timeVec1,std::vector<double> &timeVec2,std::vector<double> &dataVec1,std::vector<double> &dataVec2){
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
@ -130,7 +131,6 @@ void SummaryComparator::chooseReference(std::vector<double> &timeVec1,std::vecto
}
void SummaryComparator::getDeviation(size_t refIndex, size_t &checkIndex, Deviation &dev){
if((*referenceVec)[refIndex] == (*checkVec)[checkIndex]){
dev = SummaryComparator::calculateDeviations((*referenceDataVec)[refIndex], (*checkDataVec)[checkIndex]);
@ -138,7 +138,10 @@ void SummaryComparator::getDeviation(size_t refIndex, size_t &checkIndex, Deviat
return;
}
else if((*referenceVec)[refIndex]<(*checkVec)[checkIndex]){
double value = SummaryComparator::unitStep((*checkDataVec)[checkIndex-1]);
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;
@ -153,43 +156,6 @@ void SummaryComparator::getDeviation(size_t refIndex, size_t &checkIndex, Deviat
}
double SummaryComparator::average(std::vector<double> &vec){
return std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size();
}
double SummaryComparator::interpolation(double check_value, double check_value_prev , double time_array[3]){
//does a Linear Polation
double time_check = time_array[0]; //From the check time vector
double time_check_prev = time_array[1];//From the check time vector
double time_reference = time_array[2];// The time of interest, from the reference vector
double sloap, factor, lp_value;
sloap = (check_value - check_value_prev)/double(time_check - time_check_prev);
factor = (time_reference - time_check_prev)/double(time_check - time_check_prev);
lp_value = check_value_prev + factor*sloap*(time_check - time_check_prev);
return lp_value;
}
double SummaryComparator::median(std::vector<double> vec) {
// Sorts and returns the median in a std::vector<double>
if(vec.empty()){
return 0;
}
else {
std::sort(vec.begin(), vec.end());
if(vec.size() % 2 == 0)
return (vec[vec.size()/2 - 1] + vec[vec.size()/2]) / 2;
else
return vec[vec.size()/2];
}
}
void SummaryComparator::printUnits(){
std::vector<double> timeVec1, timeVec2;
setTimeVecs(timeVec1, timeVec2); // Sets the time vectors, they are equal for all keywords (WPOR:PROD01 etc)
@ -200,13 +166,74 @@ void SummaryComparator::printUnits(){
}
//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); //Called only when the keywords are equal in the getDeviations()-function
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;
double SummaryComparator::unitStep(double value){
return value;
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,400 @@
/*
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 <opm/test_util/summaryIntegrationTest.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.h>
#include <ert/util/stringlist.h>
void IntegrationTest::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 IntegrationTest::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 IntegrationTest::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 IntegrationTest::checkDeviation(const Deviation& deviation){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel> relTol && deviation.abs > absTol){
return 1;
}
return 0;
}
void IntegrationTest::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 IntegrationTest::volumeErrorCheck(const char* keyword){
const smspec_node_type * 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.
bool hist = smspec_node_is_historical(node);
/* returns true if the keyword corresponds to a summary vector "history".
E.g. WOPRH, where the last character, 'H', indicates that it is a HISTORY vector.*/
if(hist){
return;//To make sure we do not include history vectors.
}
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 IntegrationTest::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 IntegrationTest::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 IntegrationTest::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 IntegrationTest::checkWithSpikes(const char* keyword){
int errorOccurrences = 0;
size_t jvar = 0 ;
bool spikePrev = false;
bool spikeCurrent = false;
Deviation deviation;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
int errorOccurrencesPrev = errorOccurrences;
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
IntegrationTest::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);
}
bool IntegrationTest::checkUnits(const char * keyword){
const smspec_node_type * node1 = ecl_sum_get_general_var_node (ecl_sum_fileShort ,keyword);
const smspec_node_type * node2 = ecl_sum_get_general_var_node (ecl_sum_fileLong ,keyword);
if(strcmp(smspec_node_get_unit(node1),smspec_node_get_unit(node2)) == 0){
return true;
}
return false;
}
double IntegrationTest::integrate(const std::vector<double>& timeVec,
const std::vector<double>& dataVec){
double totalSum = 0;
double width, height;
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++){
width = timeVec[i+1] - timeVec[i];
height = dataVec[i+1];
totalSum += getRectangleArea(height, width);
}
return totalSum;
}
double IntegrationTest::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::abs(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::abs(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::abs(dataVec2[j]-value);
errorSum += getRectangleArea(dev, width);
leftEdge = rightEdge;
j++;
continue;
}
}
return errorSum;
}

View File

@ -20,6 +20,7 @@
#include <opm/common/ErrorMacros.hpp>
#include <ert/ecl/ecl_sum.h>
#include <ert/util/stringlist.h>
#include <string>
void RegressionTest::getRegressionTest(){
std::vector<double> timeVec1, timeVec2;
@ -28,25 +29,32 @@ void RegressionTest::getRegressionTest(){
std::vector<double> dataVec1, dataVec2, absDevVec, relDevVec;
int ivar = 0;
if(stringlist_get_size(keysShort) != stringlist_get_size(keysLong)){
OPM_THROW(std::invalid_argument, "Different ammont of keywords in the two summary files.");
OPM_THROW(std::runtime_error, "Different ammont 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".
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;
}
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::invalid_argument, "No match on keyword");
OPM_THROW(std::runtime_error, "No match on keyword");
}
}
ivar++;
}
std::cout << "Regression test succeeded." << std::endl;
}
@ -55,28 +63,33 @@ void RegressionTest::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;
}
checkForKeyword(timeVec1, timeVec2, keyword);
std::cout << "Regression test succeeded." << std::endl;
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::invalid_argument, "Input keyword from user does not exist in/is not common for the two summary files.");
OPM_THROW(std::runtime_error, "Input keyword from user does not exist in/is not common for the two summary files.");
}
void RegressionTest::checkDeviation(Deviation deviation, const char* keyword, int reportStep){
void RegressionTest::checkDeviation(Deviation deviation, const char* keyword, int refIndex, int checkIndex){
double absTol = getAbsTolerance();
double relTol = getRelTolerance();
if (deviation.rel > relTol && deviation.abs > absTol){
std::cout << "For keyword " << keyword << " at reportstep " << reportStep <<std::endl;
std::cout << "(days, reference value) and (days, check value) = (" << (*referenceVec)[reportStep] << ", " << (*referenceDataVec)[reportStep]
<< ") and (" << (*checkVec)[reportStep] << ", " << (*checkDataVec)[reportStep] << ")\n";
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;
OPM_THROW(std::invalid_argument, "Deviation exceed the limit.");
OPM_THROW(std::runtime_error, "Deviation exceed the limit.");
}
}
@ -96,6 +109,6 @@ void RegressionTest::startTest(const char* keyword){
Deviation deviation;
for (size_t ivar = 0; ivar < referenceVec->size(); ivar++){
getDeviation(ivar, jvar, deviation);//Reads from the protected member variables in the super class.
checkDeviation(deviation, keyword,ivar);
checkDeviation(deviation, keyword,ivar, jvar);
}
}

View File

@ -1,24 +1,25 @@
/*
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/>.
*/
+ 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 "config.h"
#include <opm/test_util/summaryComparator.hpp>
#include <opm/test_util/summaryIntegrationTest.hpp>
#if HAVE_DYNAMIC_BOOST_TEST
@ -40,35 +41,59 @@ BOOST_AUTO_TEST_CASE(deviation){
}
BOOST_AUTO_TEST_CASE(median){
std::vector<double> vec = {8.6, 0.6 ,0 , 3.0, 7.2};
BOOST_CHECK_EQUAL(3,SummaryComparator::median(vec));
vec.pop_back();
BOOST_CHECK_EQUAL(1.8, SummaryComparator::median(vec));
BOOST_AUTO_TEST_CASE(area) {
double width1 = 0;
double width2 = 2;
double width3 = 10;
}
double area1 = IntegrationTest::getRectangleArea(1,width2);
double area2 = IntegrationTest::getRectangleArea(10,width1);
double area3 = IntegrationTest::getRectangleArea(4,width3);
BOOST_AUTO_TEST_CASE(interpolation){
double timeArray[3] = {6,1,2};
double linearCheckValues[2] = {6,11};
double linearCheckValues_2[2] = {2,12};
double constCheckValues[2] = {3,3};
double linearLP = SummaryComparator::interpolation(linearCheckValues[1],linearCheckValues[0], timeArray);
double constLP = SummaryComparator::interpolation(constCheckValues[1], constCheckValues[0], timeArray);
double linearLP_2 = SummaryComparator::interpolation(linearCheckValues_2[1], linearCheckValues_2[0], timeArray);
BOOST_CHECK_EQUAL(linearLP,7);
BOOST_CHECK_EQUAL(constLP,3);
BOOST_CHECK_EQUAL(linearLP_2,4);
BOOST_CHECK_EQUAL(area1,2);
BOOST_CHECK_EQUAL(area2,0);
BOOST_CHECK_EQUAL(area3,40);
}
BOOST_AUTO_TEST_CASE(average) {
BOOST_AUTO_TEST_CASE(operatorOverload) {
WellProductionVolume volumeA;
WellProductionVolume volumeB;
volumeA.total = 2;
volumeA.error = 2;
volumeB.total = 3;
volumeB.error = 1;
volumeA += volumeB;
std::vector<double> vec = {1,2,3,4,5,6};
const double tol = 1.0e-14;
double avg = SummaryComparator::average(vec);
BOOST_CHECK_CLOSE(avg, 21.0/6, tol);
BOOST_CHECK_EQUAL(volumeA.total,5);
BOOST_CHECK_EQUAL(volumeA.error,3);
}
BOOST_AUTO_TEST_CASE(integration) {
std::vector<double> data1 = {2,2,2,2,2,2};
std::vector<double> time1 = {0,1,2,3,4,5};
double val1 = IntegrationTest::integrate(time1, data1);
std::vector<double> data2 = {3, 3, 4, 3, 2, 1, 0, 4};
std::vector<double> time2 = {0, 0.5, 1, 2, 3, 3.5, 4.5, 5};
std::vector<double> data3 = {0, 0, 0, 0, 0, 0, 0, 0};
std::vector<double> data4 = {0, 1, 4, 1, 2, 4, 0, 2, 5, 6, 3};
std::vector<double> time4 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10};
std::vector<double> data5 = {0, 4, 3, 2, 5, 6, 3, 1, 0, 4, 2, 1};
std::vector<double> time5 = {0, 0.5, 1, 2.5, 3, 4, 4.5, 6, 7.5,8, 9.5 ,10};
double val2 = IntegrationTest::integrateError(time1, data1, time2, data2);
double val3 = IntegrationTest::integrateError(time1, data1, time2, data3);
double val4 = IntegrationTest::integrateError(time1, data1, time1, data1);
double val5 = IntegrationTest::integrateError(time4, data4, time5, data5);
BOOST_CHECK_EQUAL(val1,10);
BOOST_CHECK_EQUAL(val2,6);
BOOST_CHECK_EQUAL(val3,10);
BOOST_CHECK_EQUAL(val4,0);
BOOST_CHECK_EQUAL(val5,24.5);
}