changed: split ECL(Integration|Regression)Test classes to separate files

to unify code with the summary comparison classes
This commit is contained in:
Arne Morten Kvarving
2018-09-07 13:48:46 +02:00
parent 6e0c31bed4
commit 7a034ffd01
8 changed files with 714 additions and 584 deletions

View File

@@ -136,6 +136,8 @@ include (OpmLibMain)
if(ENABLE_ECL_INPUT)
add_library(testutil STATIC
examples/test_util/EclFilesComparator.cpp
examples/test_util/EclIntegrationTest.cpp
examples/test_util/EclRegressionTest.cpp
examples/test_util/summaryComparator.cpp
examples/test_util/summaryIntegrationTest.cpp
examples/test_util/summaryRegressionTest.cpp)

View File

@@ -298,469 +298,3 @@ double ECLFilesComparator::getCellVolume(const ecl_grid_type* ecl_grid,
}
return calculateCellVol(x,y,z);
}
void ECLRegressionTest::printResultsForKeyword(const std::string& keyword) const {
std::cout << "Deviation results for keyword " << keyword << " of type "
<< ecl_type_get_name(ecl_file_iget_named_data_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 ECLRegressionTest::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, numCells, cell, data1, data2);
HANDLE_ERROR(std::runtime_error, "Values of bool type differ.");
}
}
}
void ECLRegressionTest::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, numCells, cell, data1, data2);
HANDLE_ERROR(std::runtime_error, "Values of char type differ.");
}
}
}
void ECLRegressionTest::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, values1.size(), cell, values1[cell], values2[cell]);
HANDLE_ERROR(std::runtime_error, "Values of int type differ.");
}
}
}
void ECLRegressionTest::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 ECLRegressionTest::deviationsForCell(double val1, double val2,
const std::string& keyword,
int occurrence1, int occurrence2,
size_t kw_size, 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, kw_size, cell, val1, val2);
HANDLE_ERROR(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, kw_size, cell, val1, val2);
HANDLE_ERROR(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) {
if (analysis) {
deviations[keyword].push_back(dev);
} else {
printValuesForCell(keyword, occurrence1, occurrence2, kw_size, cell, val1, val2);
HANDLE_ERROR(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 ECLRegressionTest::gridCompare(const bool volumecheck) 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 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.");
}
if (!volumecheck) {
return;
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const bool active1 = ecl_grid_cell_active1(ecl_grid1, cell);
const bool active2 = ecl_grid_cell_active1(ecl_grid2, cell);
if (active1 != active2) {
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++;
HANDLE_ERROR(std::runtime_error, "Grid cell with one-based indices ( "
<< i << ", " << j << ", " << k << " ) is "
<< (active1 ? "active" : "inactive") << " in first grid, but "
<< (active2 ? "active" : "inactive") << " in second grid.");
}
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
HANDLE_ERROR(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with one-based indices (" << 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 << "."
<< "\nCell 1 active: " << active1
<< "\nCell 2 active: " << active2);
}
}
}
void ECLRegressionTest::results() {
if (keywords1.size() != keywords2.size()) {
std::set<std::string> keys(keywords1.begin() , keywords1.end());
for (const auto& key2: keywords2)
keys.insert( key2 );
for (const auto& key : keys)
fprintf(stderr," %8s:%3d %8s:%3d \n",key.c_str() , ecl_file_get_num_named_kw( ecl_file1 , key.c_str()),
key.c_str() , ecl_file_get_num_named_kw( ecl_file2 , key.c_str()));
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);
if (analysis) {
std::cout << deviations.size() << " keyword"
<< (deviations.size() > 1 ? "s":"") << " exhibit failures" << std::endl;
for (const auto& iter : deviations) {
std::cout << "\t" << iter.first << std::endl;
std::cout << "\t\tFails for " << iter.second.size() << " entries" << std::endl;
std::cout.precision(7);
double absErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.abs < b.abs;
})->abs;
double relErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.rel < b.rel;
})->rel;
std::cout << "\t\tLargest absolute error: "
<< std::scientific << absErr << std::endl;
std::cout << "\t\tLargest relative error: "
<< std::scientific << relErr << std::endl;
}
}
}
void ECLRegressionTest::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_type_get_type( ecl_file_iget_named_data_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 MESS"
<< ", 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 ECLIntegrationTest::setCellVolumes() {
double absTolerance = getAbsTolerance();
double relTolerance = getRelTolerance();
const unsigned int globalGridCount1 = ecl_grid_get_global_size(ecl_grid1);
const unsigned int activeGridCount1 = ecl_grid_get_active_size(ecl_grid1);
const unsigned int globalGridCount2 = ecl_grid_get_global_size(ecl_grid2);
const unsigned int activeGridCount2 = ecl_grid_get_active_size(ecl_grid2);
if (globalGridCount1 != globalGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << globalGridCount1
<< "\nCells in second file: " << globalGridCount2
<< "\nThe number of global cells differ.");
}
if (activeGridCount1 != activeGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << activeGridCount1
<< "\nCells in second file: " << activeGridCount2
<< "\nThe number of active cells differ.");
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
OPM_THROW(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with coordinate (" << i << ", " << j << ", " << k << "):"
<< "\nCell volume in first file: " << cellVolume1
<< "\nCell volume in second file: " << cellVolume2
<< "\nThe absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << "."
<< "\nThe relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".");
} // The second input case is used as reference.
cellVolumes.push_back(cellVolume2);
}
}
void ECLIntegrationTest::initialOccurrenceCompare(const std::string& keyword) {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, 0, 0);
std::vector<double> values1(numCells);
initialCellValues.resize(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, initialCellValues.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the keyword value for the first case for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedTotal = 0;
for (size_t cell = 0; cell < initialCellValues.size(); ++cell) {
weightedTotal += initialCellValues[cell]*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
}
if (weightedTotal != 0) {
double ratioValue = weightedDifference/weightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence 0:"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
void ECLIntegrationTest::occurrenceCompare(const std::string& keyword, int occurrence) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence, occurrence);
std::vector<double> values1(numCells), values2(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, values2.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the difference between the keyword value for the occurrence and the initial keyword value for each cell. The sum is weighted with respect to the cell volume of each cell.
double relativeWeightedTotal = 0;
for (size_t cell = 0; cell < values1.size(); ++cell) {
relativeWeightedTotal += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - values2[cell])*cellVolumes[cell];
}
if (relativeWeightedTotal != 0) {
double ratioValue = weightedDifference/relativeWeightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence " << occurrence << ":"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
ECLIntegrationTest::ECLIntegrationTest(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance) :
ECLFilesComparator(ECL_UNIFIED_RESTART_FILE, basename1, basename2, absTolerance, relTolerance) {
std::cout << "\nUsing cell volumes and keyword values from case " << basename2
<< " as reference." << std::endl << std::endl;
setCellVolumes();
}
bool ECLIntegrationTest::elementInWhitelist(const std::string& keyword) const {
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
return it != keywordWhitelist.end();
}
void ECLIntegrationTest::equalNumKeywords() const {
if (keywords1.size() != keywords2.size()) {
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
}
}
void ECLIntegrationTest::results() {
for (const auto& it : keywordWhitelist)
resultsForKeyword(it);
}
void ECLIntegrationTest::resultsForKeyword(const std::string& keyword) {
std::cout << "Comparing " << keyword << "...";
keywordValidForComparing(keyword);
const unsigned int occurrences1 = ecl_file_get_num_named_kw(ecl_file1, keyword.c_str());
const unsigned int occurrences2 = ecl_file_get_num_named_kw(ecl_file2, keyword.c_str());
if (occurrences1 != occurrences2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nKeyword occurrences in first file: " << occurrences1
<< "\nKeyword occurrences in second file: " << occurrences2
<< "\nThe number of occurrences differ.");
}
initialOccurrenceCompare(keyword);
for (unsigned int occurrence = 1; occurrence < occurrences1; ++occurrence) {
occurrenceCompare(keyword, occurrence);
}
std::cout << "done." << std::endl;
}

View File

@@ -133,121 +133,4 @@ class ECLFilesComparator {
static double getCellVolume(const ecl_grid_type* ecl_grid, const int globalIndex);
};
/*! \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 ECLRegressionTest: 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 kw_size, size_t cell, bool allowNegativeValues = true);
public:
//! \brief Sets up the regression test.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(file_type, basename1, basename2, absTolerance, relTolerance) {}
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrenceArg) {this->onlyLastOccurrence = onlyLastOccurrenceArg;}
//! \brief Compares grid properties of the two cases.
// gridCompare() checks if both the number of active and global cells in the two cases are the same. If they are, and volumecheck is true, all cells are looped over to calculate the cell volume deviation for the two cases. If the both the relative and absolute deviation exceeds the tolerances, an exception is thrown.
void gridCompare(const bool volumecheck) const;
//! \brief Calculates deviations for all keywords.
// This function checks if the number of keywords of the two cases are equal, and if it is, resultsForKeyword() is called for every keyword. If not, an exception is thrown.
void results();
//! \brief Calculates deviations for a specific keyword.
//! \param[in] keyword Keyword which should be compared, if this keyword is absent in one of the cases, an exception will be thrown.
//! \details This function loops through every report step and every cell and compares the values for the given keyword from the two input cases. If the absolute or relative deviation between the two values for each step exceeds both the absolute tolerance and the relative tolerance (stored in ECLFilesComparator), an exception is thrown. In addition, some keywords are marked for "disallow negative values" -- these are SGAS, SWAT and PRESSURE. An exception is thrown if a value of one of these keywords is both negative and has an absolute value larger than the absolute tolerance. If no exceptions are thrown, resultsForKeyword() uses the private member funtion printResultsForKeyword to print the average and median deviations.
void resultsForKeyword(const std::string& keyword);
};
/*! \brief A class for executing a integration test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and closes
the input cases and stores keywordnames. The three public functions
equalNumKeywords(), results() and resultsForKeyword() can be invoked
to compare griddata or keyworddata for all keywords or a given
keyword (resultsForKeyword()).
*/
class ECLIntegrationTest: public ECLFilesComparator {
private:
std::vector<double> cellVolumes; //!< Vector of cell volumes in second input case (indexed by global index)
std::vector<double> initialCellValues; //!< Keyword values for all cells at first occurrence (index by global index)
// These are the only keywords which are compared, since SWAT should be "1 - SOIL - SGAS", this keyword is omitted.
const std::vector<std::string> keywordWhitelist = {"SGAS", "SWAT", "PRESSURE"};
void setCellVolumes();
void initialOccurrenceCompare(const std::string& keyword);
void occurrenceCompare(const std::string& keyword, int occurrence) const;
public:
//! \brief Sets up the integration test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor calls the constructor of the superclass, with input filetype unified restart. See the docs for ECLFilesComparator for more information.
ECLIntegrationTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Checks if a keyword is supported for comparison.
//! \param[in] keyword Keyword to check.
bool elementInWhitelist(const std::string& keyword) const;
//! \brief Checks if the number of keywords equal in the two input cases.
//! \param[in] keyword Keyword to check.
void equalNumKeywords() const;
//! \brief Finds deviations for all supported keywords.
//! \details results() loops through all supported keywords for integration test (defined in keywordWhitelist -- this is SGAS, SWAT and PRESSURE) and calls resultsForKeyword() for each keyword.
void results();
//! \brief Finds deviations for a specific keyword.
//! \param[in] keyword Keyword to check.
/*! \details First, resultsForKeyword() checks if the keyword exits in both cases, and if the number of keyword occurrences in the two cases differ. If these tests fail, an exception is thrown. Then deviaitons are calculated as described below for each occurrence, and an exception is thrown if the relative error ratio \f$E\f$ is larger than the relative tolerance.
* Calculation:\n
* Let the keyword values for occurrence \f$n\f$ and cell \f$i\f$ be \f$p_{n,i}\f$ and \f$q_{n,i}\f$ for input case 1 and 2, respectively.
* Consider first the initial occurrence (\f$n=0\f$). The function uses the second cases as reference, and calculates the volume weighted sum of \f$q_{0,i}\f$ over all cells \f$i\f$:
* \f[ S_0 = \sum_{i} q_{0,i} v_i \f]
* where \f$v_{i}\f$ is the volume of cell \f$i\f$ in case 2. Then, the deviations between the cases for each cell are calculated:
* \f[ \Delta = \sum_{i} |p_{0,i} - q_{0,i}| v_i.\f]
* The error ratio is then \f$E = \Delta/S_0\f$.\n
* For all other occurrences \f$n\f$, the deviation value \f$\Delta\f$ is calculated the same way, but the total value \f$S\f$ is calculated relative to the initial occurrence total \f$S_0\f$:
* \f[ S = \sum_{i} |q_{n,i} - q_{0,i}| v_i. \f]
* The error ratio is \f$ E = \Delta/S\f$. */
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -0,0 +1,199 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclIntegrationTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <set>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else { \
std::cerr << message << std::endl; \
++num_errors; \
} \
}
void ECLIntegrationTest::setCellVolumes() {
double absTolerance = getAbsTolerance();
double relTolerance = getRelTolerance();
const unsigned int globalGridCount1 = ecl_grid_get_global_size(ecl_grid1);
const unsigned int activeGridCount1 = ecl_grid_get_active_size(ecl_grid1);
const unsigned int globalGridCount2 = ecl_grid_get_global_size(ecl_grid2);
const unsigned int activeGridCount2 = ecl_grid_get_active_size(ecl_grid2);
if (globalGridCount1 != globalGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << globalGridCount1
<< "\nCells in second file: " << globalGridCount2
<< "\nThe number of global cells differ.");
}
if (activeGridCount1 != activeGridCount2) {
OPM_THROW(std::runtime_error, "In grid file:"
<< "\nCells in first file: " << activeGridCount1
<< "\nCells in second file: " << activeGridCount2
<< "\nThe number of active cells differ.");
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
OPM_THROW(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with coordinate (" << i << ", " << j << ", " << k << "):"
<< "\nCell volume in first file: " << cellVolume1
<< "\nCell volume in second file: " << cellVolume2
<< "\nThe absolute deviation is " << dev.abs << ", and the tolerance limit is " << absTolerance << "."
<< "\nThe relative deviation is " << dev.rel << ", and the tolerance limit is " << relTolerance << ".");
} // The second input case is used as reference.
cellVolumes.push_back(cellVolume2);
}
}
void ECLIntegrationTest::initialOccurrenceCompare(const std::string& keyword) {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, 0, 0);
std::vector<double> values1(numCells);
initialCellValues.resize(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, initialCellValues.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the keyword value for the first case for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedTotal = 0;
for (size_t cell = 0; cell < initialCellValues.size(); ++cell) {
weightedTotal += initialCellValues[cell]*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
}
if (weightedTotal != 0) {
double ratioValue = weightedDifference/weightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence 0:"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
void ECLIntegrationTest::occurrenceCompare(const std::string& keyword, int occurrence) const {
ecl_kw_type* ecl_kw1 = nullptr;
ecl_kw_type* ecl_kw2 = nullptr;
const unsigned int numCells = getEclKeywordData(ecl_kw1, ecl_kw2, keyword, occurrence, occurrence);
std::vector<double> values1(numCells), values2(numCells);
ecl_kw_get_data_as_double(ecl_kw1, values1.data());
ecl_kw_get_data_as_double(ecl_kw2, values2.data());
// This variable sums up the difference between the keyword value for the first case and the keyword value for the second case, for each cell. The sum is weighted with respect to the cell volume of each cell.
double weightedDifference = 0;
// This variable sums up the difference between the keyword value for the occurrence and the initial keyword value for each cell. The sum is weighted with respect to the cell volume of each cell.
double relativeWeightedTotal = 0;
for (size_t cell = 0; cell < values1.size(); ++cell) {
relativeWeightedTotal += std::abs(values1[cell] - initialCellValues[cell])*cellVolumes[cell];
weightedDifference += std::abs(values1[cell] - values2[cell])*cellVolumes[cell];
}
if (relativeWeightedTotal != 0) {
double ratioValue = weightedDifference/relativeWeightedTotal;
if ((ratioValue) > getRelTolerance()) {
OPM_THROW(std::runtime_error, "\nFor keyword " << keyword << " and occurrence " << occurrence << ":"
<< "\nThe ratio of the deviation and the total value is " << ratioValue
<< ", which exceeds the relative tolerance of " << getRelTolerance() << "."
<< "\nSee the docs for more information about how the ratio is computed.");
}
}
}
ECLIntegrationTest::ECLIntegrationTest(const std::string& basename1,
const std::string& basename2,
double absTolerance, double relTolerance) :
ECLFilesComparator(ECL_UNIFIED_RESTART_FILE, basename1, basename2, absTolerance, relTolerance) {
std::cout << "\nUsing cell volumes and keyword values from case " << basename2
<< " as reference." << std::endl << std::endl;
setCellVolumes();
}
bool ECLIntegrationTest::elementInWhitelist(const std::string& keyword) const {
auto it = std::find(keywordWhitelist.begin(), keywordWhitelist.end(), keyword);
return it != keywordWhitelist.end();
}
void ECLIntegrationTest::equalNumKeywords() const {
if (keywords1.size() != keywords2.size()) {
OPM_THROW(std::runtime_error, "\nKeywords in first file: " << keywords1.size()
<< "\nKeywords in second file: " << keywords2.size()
<< "\nThe number of keywords differ.");
}
}
void ECLIntegrationTest::results() {
for (const auto& it : keywordWhitelist)
resultsForKeyword(it);
}
void ECLIntegrationTest::resultsForKeyword(const std::string& keyword) {
std::cout << "Comparing " << keyword << "...";
keywordValidForComparing(keyword);
const unsigned int occurrences1 = ecl_file_get_num_named_kw(ecl_file1, keyword.c_str());
const unsigned int occurrences2 = ecl_file_get_num_named_kw(ecl_file2, keyword.c_str());
if (occurrences1 != occurrences2) {
OPM_THROW(std::runtime_error, "For keyword " << keyword << ":"
<< "\nKeyword occurrences in first file: " << occurrences1
<< "\nKeyword occurrences in second file: " << occurrences2
<< "\nThe number of occurrences differ.");
}
initialOccurrenceCompare(keyword);
for (unsigned int occurrence = 1; occurrence < occurrences1; ++occurrence) {
occurrenceCompare(keyword, occurrence);
}
std::cout << "done." << std::endl;
}

View File

@@ -0,0 +1,78 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECLINTEGRATIONTEST_HPP
#define ECLINTEGRATIONTEST_HPP
#include "EclFilesComparator.hpp"
/*! \brief A class for executing a integration test for two ECLIPSE files.
\details This class inherits from ECLFilesComparator, which opens and closes
the input cases and stores keywordnames. The three public functions
equalNumKeywords(), results() and resultsForKeyword() can be invoked
to compare griddata or keyworddata for all keywords or a given
keyword (resultsForKeyword()).
*/
class ECLIntegrationTest: public ECLFilesComparator {
private:
std::vector<double> cellVolumes; //!< Vector of cell volumes in second input case (indexed by global index)
std::vector<double> initialCellValues; //!< Keyword values for all cells at first occurrence (index by global index)
// These are the only keywords which are compared, since SWAT should be "1 - SOIL - SGAS", this keyword is omitted.
const std::vector<std::string> keywordWhitelist = {"SGAS", "SWAT", "PRESSURE"};
void setCellVolumes();
void initialOccurrenceCompare(const std::string& keyword);
void occurrenceCompare(const std::string& keyword, int occurrence) const;
public:
//! \brief Sets up the integration test.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor calls the constructor of the superclass, with input filetype unified restart. See the docs for ECLFilesComparator for more information.
ECLIntegrationTest(const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance);
//! \brief Checks if a keyword is supported for comparison.
//! \param[in] keyword Keyword to check.
bool elementInWhitelist(const std::string& keyword) const;
//! \brief Checks if the number of keywords equal in the two input cases.
//! \param[in] keyword Keyword to check.
void equalNumKeywords() const;
//! \brief Finds deviations for all supported keywords.
//! \details results() loops through all supported keywords for integration test (defined in keywordWhitelist -- this is SGAS, SWAT and PRESSURE) and calls resultsForKeyword() for each keyword.
void results();
//! \brief Finds deviations for a specific keyword.
//! \param[in] keyword Keyword to check.
/*! \details First, resultsForKeyword() checks if the keyword exits in both cases, and if the number of keyword occurrences in the two cases differ. If these tests fail, an exception is thrown. Then deviaitons are calculated as described below for each occurrence, and an exception is thrown if the relative error ratio \f$E\f$ is larger than the relative tolerance.
* Calculation:\n
* Let the keyword values for occurrence \f$n\f$ and cell \f$i\f$ be \f$p_{n,i}\f$ and \f$q_{n,i}\f$ for input case 1 and 2, respectively.
* Consider first the initial occurrence (\f$n=0\f$). The function uses the second cases as reference, and calculates the volume weighted sum of \f$q_{0,i}\f$ over all cells \f$i\f$:
* \f[ S_0 = \sum_{i} q_{0,i} v_i \f]
* where \f$v_{i}\f$ is the volume of cell \f$i\f$ in case 2. Then, the deviations between the cases for each cell are calculated:
* \f[ \Delta = \sum_{i} |p_{0,i} - q_{0,i}| v_i.\f]
* The error ratio is then \f$E = \Delta/S_0\f$.\n
* For all other occurrences \f$n\f$, the deviation value \f$\Delta\f$ is calculated the same way, but the total value \f$S\f$ is calculated relative to the initial occurrence total \f$S_0\f$:
* \f[ S = \sum_{i} |q_{n,i} - q_{0,i}| v_i. \f]
* The error ratio is \f$ E = \Delta/S\f$. */
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -0,0 +1,349 @@
/*
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 "EclRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <set>
#include <ert/ecl/ecl_file.h>
#include <ert/ecl/ecl_grid.h>
#include <ert/ecl/ecl_type.h>
#include <ert/ecl_well/well_info.h>
// helper macro to handle error throws or not
#define HANDLE_ERROR(type, message) \
{ \
if (throwOnError) \
OPM_THROW(type, message); \
else { \
std::cerr << message << std::endl; \
++num_errors; \
} \
}
void ECLRegressionTest::printResultsForKeyword(const std::string& keyword) const {
std::cout << "Deviation results for keyword " << keyword << " of type "
<< ecl_type_get_name(ecl_file_iget_named_data_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 ECLRegressionTest::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, numCells, cell, data1, data2);
HANDLE_ERROR(std::runtime_error, "Values of bool type differ.");
}
}
}
void ECLRegressionTest::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, numCells, cell, data1, data2);
HANDLE_ERROR(std::runtime_error, "Values of char type differ.");
}
}
}
void ECLRegressionTest::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, values1.size(), cell, values1[cell], values2[cell]);
HANDLE_ERROR(std::runtime_error, "Values of int type differ.");
}
}
}
void ECLRegressionTest::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 ECLRegressionTest::deviationsForCell(double val1, double val2,
const std::string& keyword,
int occurrence1, int occurrence2,
size_t kw_size, 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, kw_size, cell, val1, val2);
HANDLE_ERROR(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, kw_size, cell, val1, val2);
HANDLE_ERROR(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) {
if (analysis) {
deviations[keyword].push_back(dev);
} else {
printValuesForCell(keyword, occurrence1, occurrence2, kw_size, cell, val1, val2);
HANDLE_ERROR(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 ECLRegressionTest::gridCompare(const bool volumecheck) 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 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.");
}
if (!volumecheck) {
return;
}
for (unsigned int cell = 0; cell < globalGridCount1; ++cell) {
const bool active1 = ecl_grid_cell_active1(ecl_grid1, cell);
const bool active2 = ecl_grid_cell_active1(ecl_grid2, cell);
if (active1 != active2) {
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++;
HANDLE_ERROR(std::runtime_error, "Grid cell with one-based indices ( "
<< i << ", " << j << ", " << k << " ) is "
<< (active1 ? "active" : "inactive") << " in first grid, but "
<< (active2 ? "active" : "inactive") << " in second grid.");
}
const double cellVolume1 = getCellVolume(ecl_grid1, cell);
const double cellVolume2 = getCellVolume(ecl_grid2, cell);
Deviation dev = calculateDeviations(cellVolume1, cellVolume2);
if (dev.abs > absTolerance && dev.rel > relTolerance) {
int i, j, k;
ecl_grid_get_ijk1(ecl_grid1, cell, &i, &j, &k);
// Coordinates from this function are zero-based, hence incrementing
i++, j++, k++;
HANDLE_ERROR(std::runtime_error, "In grid file: Deviations of cell volume exceed tolerances. "
<< "\nFor cell with one-based indices (" << 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 << "."
<< "\nCell 1 active: " << active1
<< "\nCell 2 active: " << active2);
}
}
}
void ECLRegressionTest::results() {
if (keywords1.size() != keywords2.size()) {
std::set<std::string> keys(keywords1.begin() , keywords1.end());
for (const auto& key2: keywords2)
keys.insert( key2 );
for (const auto& key : keys)
fprintf(stderr," %8s:%3d %8s:%3d \n",key.c_str() , ecl_file_get_num_named_kw( ecl_file1 , key.c_str()),
key.c_str() , ecl_file_get_num_named_kw( ecl_file2 , key.c_str()));
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);
if (analysis) {
std::cout << deviations.size() << " keyword"
<< (deviations.size() > 1 ? "s":"") << " exhibit failures" << std::endl;
for (const auto& iter : deviations) {
std::cout << "\t" << iter.first << std::endl;
std::cout << "\t\tFails for " << iter.second.size() << " entries" << std::endl;
std::cout.precision(7);
double absErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.abs < b.abs;
})->abs;
double relErr = std::max_element(iter.second.begin(), iter.second.end(),
[](const Deviation& a, const Deviation& b)
{
return a.rel < b.rel;
})->rel;
std::cout << "\t\tLargest absolute error: "
<< std::scientific << absErr << std::endl;
std::cout << "\t\tLargest relative error: "
<< std::scientific << relErr << std::endl;
}
}
}
void ECLRegressionTest::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_type_get_type( ecl_file_iget_named_data_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 MESS"
<< ", 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;
}

View File

@@ -0,0 +1,84 @@
/*
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ECLREGRESSIONTEST_HPP
#define ECLREGRESSIONTEST_HPP
#include "EclFilesComparator.hpp"
/*! \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 ECLRegressionTest: 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 kw_size, size_t cell, bool allowNegativeValues = true);
public:
//! \brief Sets up the regression test.
//! \param[in] file_type Specifies which filetype to be compared, possible inputs are UNRSTFILE, INITFILE and RFTFILE.
//! \param[in] basename1 Full path without file extension to the first case.
//! \param[in] basename2 Full path without file extension to the second case.
//! \param[in] absTolerance Tolerance for absolute deviation.
//! \param[in] relTolerance Tolerance for relative deviation.
//! \details This constructor only calls the constructor of the superclass, see the docs for ECLFilesComparator for more information.
ECLRegressionTest(int file_type, const std::string& basename1, const std::string& basename2, double absTolerance, double relTolerance):
ECLFilesComparator(file_type, basename1, basename2, absTolerance, relTolerance) {}
//! \brief Option to only compare last occurrence
void setOnlyLastOccurrence(bool onlyLastOccurrenceArg) {this->onlyLastOccurrence = onlyLastOccurrenceArg;}
//! \brief Compares grid properties of the two cases.
// gridCompare() checks if both the number of active and global cells in the two cases are the same. If they are, and volumecheck is true, all cells are looped over to calculate the cell volume deviation for the two cases. If the both the relative and absolute deviation exceeds the tolerances, an exception is thrown.
void gridCompare(const bool volumecheck) const;
//! \brief Calculates deviations for all keywords.
// This function checks if the number of keywords of the two cases are equal, and if it is, resultsForKeyword() is called for every keyword. If not, an exception is thrown.
void results();
//! \brief Calculates deviations for a specific keyword.
//! \param[in] keyword Keyword which should be compared, if this keyword is absent in one of the cases, an exception will be thrown.
//! \details This function loops through every report step and every cell and compares the values for the given keyword from the two input cases. If the absolute or relative deviation between the two values for each step exceeds both the absolute tolerance and the relative tolerance (stored in ECLFilesComparator), an exception is thrown. In addition, some keywords are marked for "disallow negative values" -- these are SGAS, SWAT and PRESSURE. An exception is thrown if a value of one of these keywords is both negative and has an absolute value larger than the absolute tolerance. If no exceptions are thrown, resultsForKeyword() uses the private member funtion printResultsForKeyword to print the average and median deviations.
void resultsForKeyword(const std::string& keyword);
};
#endif

View File

@@ -16,7 +16,8 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EclFilesComparator.hpp"
#include "EclIntegrationTest.hpp"
#include "EclRegressionTest.hpp"
#include <opm/common/ErrorMacros.hpp>
#include <ert/util/util.h>