Implement writing of RSM file

This commit is contained in:
Williham Williham Totland 2020-04-01 10:30:10 +02:00 committed by GitHub
parent 7c0ddd0b9d
commit ea269c7cc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 264 additions and 7 deletions

View File

@ -240,6 +240,7 @@ if(ENABLE_ECL_OUTPUT)
src/opm/io/eclipse/ERft.cpp
src/opm/io/eclipse/ERst.cpp
src/opm/io/eclipse/ESmry.cpp
src/opm/io/eclipse/ESmry_write_rsm.cpp
src/opm/io/eclipse/OutputStream.cpp
src/opm/io/eclipse/SummaryNode.cpp
src/opm/io/eclipse/rst/connection.cpp

View File

@ -19,6 +19,7 @@
#ifndef OPM_IO_ESMRY_HPP
#define OPM_IO_ESMRY_HPP
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@ -57,7 +58,11 @@ public:
const std::string& get_unit(const std::string& name) const;
const std::string& get_unit(const SummaryNode& node) const;
void write_rsm(std::ostream&) const;
void write_rsm_file(std::optional<Opm::filesystem::path> = std::nullopt) const;
private:
Opm::filesystem::path inputFileName;
int nVect, nI, nJ, nK;
void ijk_from_global_index(int glob, int &i, int &j, int &k) const;
@ -84,8 +89,16 @@ private:
std::string unpackNumber(const SummaryNode&) const;
std::string lookupKey(const SummaryNode&) const;
void write_block(std::ostream &, const std::vector<SummaryNode>&) const;
};
}} // namespace Opm::EclIO
inline std::ostream& operator<<(std::ostream& os, const Opm::EclIO::ESmry& smry) {
smry.write_rsm(os);
return os;
}
#endif // OPM_IO_ESMRY_HPP

View File

@ -21,6 +21,7 @@
#define OPM_IO_SUMMARYNODE_HPP
#include <functional>
#include <optional>
#include <string>
#include <unordered_set>
@ -65,6 +66,10 @@ struct SummaryNode {
bool is_user_defined() const;
static Category category_from_keyword(const std::string&, const std::unordered_set<std::string> &miscellaneous_keywords = {});
std::optional<std::string> display_name() const;
std::optional<std::string> display_number() const;
std::optional<std::string> display_number(number_renderer) const;
};
} // namespace Opm::EclIO

View File

@ -52,10 +52,10 @@
namespace Opm { namespace EclIO {
ESmry::ESmry(const std::string &filename, bool loadBaseRunData) :
inputFileName { filename },
summaryNodes { }
{
Opm::filesystem::path inputFileName(filename);
Opm::filesystem::path rootName = inputFileName.parent_path() / inputFileName.stem();
// if root name (without any extension) given as first argument in constructor, binary will then be assumed

View File

@ -0,0 +1,194 @@
/*
Copyright 2020 Equinor ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/io/eclipse/ESmry.hpp>
#include <cmath>
#include <algorithm>
#include <fstream>
#include <functional>
#include <iomanip>
#include <list>
#include <ostream>
#include <regex>
#include <string>
#include <opm/common/ErrorMacros.hpp>
namespace {
constexpr std::size_t column_width { 8 } ;
constexpr std::size_t column_space { 5 } ;
constexpr std::size_t column_count { 10 } ;
constexpr std::size_t total_column { column_width + column_space } ;
constexpr std::size_t total_width { total_column * column_count } ;
const std::string block_separator_line { } ;
// the fact that the dashed header line has 127 rather than 130 dashes has no provenance
const std::string divider_line { std::string(total_width - 3, '-') } ;
const std::string block_header_line(const std::string& run_name, const std::string& comment) {
return "SUMMARY OF RUN " + run_name + " OPM FLOW " + comment;
}
void write_line(std::ostream& os, const std::string& line, char prefix = ' ') {
os << prefix << std::setw(total_width) << std::left << line << '\n';
}
void print_text_element(std::ostream& os, const std::string& element) {
os << std::setw(8) << std::left << element << std::setw(5) << "";
}
void print_float_element(std::ostream& os, float element) {
static const std::regex integer_regex { "\\.0*$" };
auto element_string = std::to_string(element);
if (element_string.size() > 8) {
element_string = element_string.substr(0, 8);
}
element_string = std::regex_replace(element_string, integer_regex, "");
os << std::setw(8) << std::right << element_string << std::setw(5) << "";
}
void write_header_columns(std::ostream& os, const std::vector<Opm::EclIO::SummaryNode>& vectors, std::function<void(std::ostream&, const Opm::EclIO::SummaryNode&)> print_element, char prefix = ' ') {
os << prefix;
for (const auto& vector : vectors) {
print_element(os, vector);
}
os << '\n';
}
void write_data_row(std::ostream& os, const std::vector<std::pair<std::vector<float>, int>>& data, std::size_t index, char prefix = ' ') {
os << prefix;
for (const auto& vector : data) {
print_float_element(os, vector.first[index] * std::pow(10.0, -vector.second));
}
os << '\n';
}
void write_scale_columns(std::ostream& os, const std::vector<std::pair<std::vector<float>, int>> data, char prefix = ' ') {
os << prefix;
for (const auto& vector : data) {
const auto scale_factor { vector.second } ;
if (scale_factor) {
print_text_element(os, "*10**" + std::to_string(scale_factor));
} else {
print_text_element(os, "");
}
}
os << '\n';
}
}
namespace Opm::EclIO {
void ESmry::write_block(std::ostream& os, const std::vector<SummaryNode>& vectors) const {
write_line(os, block_separator_line, '1');
write_line(os, divider_line);
write_line(os, block_header_line(inputFileName.stem(), "VERSION 1910 ANYTHING CAN GO HERE: USER, MACHINE ETC."));
write_line(os, divider_line);
std::vector<std::pair<std::vector<float>, int>> data;
bool has_scale_factors { false } ;
for (const auto& vector : vectors) {
const auto& vector_data { get(vector) } ;
auto max = *std::max_element(vector_data.begin(), vector_data.end());
int scale_factor { std::max(0, 3 * static_cast<int>(std::floor(( std::log10(max) - 4 ) / 3 ))) } ;
if (scale_factor) {
has_scale_factors = true;
}
data.emplace_back(vector_data, scale_factor);
}
std::size_t rows { data[0].first.size() };
write_header_columns(os, vectors, [](std::ostream& os, const SummaryNode& node) { print_text_element(os, node.keyword); });
write_header_columns(os, vectors, [this](std::ostream& os, const SummaryNode& node) { print_text_element(os, this->get_unit(node)); });
if (has_scale_factors) {
write_scale_columns(os, data);
}
write_header_columns(os, vectors, [](std::ostream& os, const SummaryNode& node) { print_text_element(os, node.display_name().value_or("")); });
write_header_columns(os, vectors, [](std::ostream& os, const SummaryNode& node) { print_text_element(os, node.display_number().value_or("")); });
write_line(os, divider_line);
for (std::size_t i { 0 } ; i < rows; i++) {
write_data_row(os, data, i);
}
os << std::flush;
}
void ESmry::write_rsm(std::ostream& os) const {
SummaryNode date_vector;
std::vector<SummaryNode> data_vectors;
std::remove_copy_if(summaryNodes.begin(), summaryNodes.end(), std::back_inserter(data_vectors), [&date_vector](const SummaryNode& node){
if (node.keyword == "TIME" || node.keyword == "DATE") {
date_vector = node;
return true;
} else {
return false;
}
});
std::vector<std::list<SummaryNode>> data_vector_blocks;
constexpr std::size_t data_column_count { column_count - 1 } ;
for (std::size_t i { 0 } ; i < data_vectors.size(); i += data_column_count) {
auto last = std::min(data_vectors.size(), i + data_column_count);
data_vector_blocks.emplace_back(data_vectors.begin() + i, data_vectors.begin() + last);
data_vector_blocks.back().push_front(date_vector);
}
for (const auto& data_vector_block : data_vector_blocks) {
write_block(os, { data_vector_block.begin(), data_vector_block.end() });
}
}
void ESmry::write_rsm_file(std::optional<Opm::filesystem::path> filename) const {
Opm::filesystem::path summary_file_name { filename.value_or(inputFileName) } ;
summary_file_name.replace_extension("RSM");
std::ofstream rsm_file { summary_file_name } ;
if (!rsm_file.is_open()) {
OPM_THROW(std::runtime_error, "Could not open file " + std::string(summary_file_name));
}
write_rsm(rsm_file);
rsm_file.close();
}
} // namespace Opm::EclIO

View File

@ -74,11 +74,11 @@ std::string default_number_renderer(const Opm::EclIO::SummaryNode& node) {
std::string Opm::EclIO::SummaryNode::unique_key(number_renderer render_number) const {
std::vector<std::string> key_parts { keyword } ;
if (use_name(category))
key_parts.emplace_back(wgname);
if (auto opt = display_name())
key_parts.emplace_back(opt.value());
if (use_number(category))
key_parts.emplace_back(render_number(*this));
if (auto opt = display_number(render_number))
key_parts.emplace_back(opt.value());
auto compose_key = [](std::string& key, const std::string& key_part) -> std::string {
constexpr auto delimiter { ':' } ;
@ -152,3 +152,23 @@ Opm::EclIO::SummaryNode::Category Opm::EclIO::SummaryNode::category_from_keyword
default: return Category::Miscellaneous;
}
}
std::optional<std::string> Opm::EclIO::SummaryNode::display_name() const {
if (use_name(category)) {
return wgname;
} else {
return std::nullopt;
}
}
std::optional<std::string> Opm::EclIO::SummaryNode::display_number() const {
return display_number(default_number_renderer);
}
std::optional<std::string> Opm::EclIO::SummaryNode::display_number(number_renderer render_number) const {
if (use_number(category)) {
return render_number(*this);
} else {
return std::nullopt;
}
}

View File

@ -42,6 +42,7 @@
#include <opm/output/eclipse/WriteInit.hpp>
#include <opm/output/eclipse/WriteRFT.hpp>
#include <opm/io/eclipse/ESmry.hpp>
#include <opm/io/eclipse/OutputStream.hpp>
#include <algorithm>
@ -105,6 +106,7 @@ class EclipseIO::Impl {
const Schedule& schedule;
std::string outputDir;
std::string baseName;
SummaryConfig summaryConfig;
out::Summary summary;
bool output_enabled;
};
@ -118,7 +120,8 @@ EclipseIO::Impl::Impl( const EclipseState& eclipseState,
, schedule( schedule_ )
, outputDir( eclipseState.getIOConfig().getOutputDir() )
, baseName( uppercase( eclipseState.getIOConfig().getBaseName() ) )
, summary( eclipseState, summary_config, grid , schedule )
, summaryConfig( summary_config )
, summary( eclipseState, summaryConfig, grid , schedule )
, output_enabled( eclipseState.getIOConfig().getOutputEnabled() )
{}
@ -208,6 +211,14 @@ void EclipseIO::writeTimeStep(const SummaryState& st,
this->impl->summary.write();
}
bool final_step { report_step == static_cast<int>(this->impl->schedule.size()) - 1 };
if (final_step && this->impl->summaryConfig.createRunSummary()) {
Opm::filesystem::path outputDir { this->impl->outputDir } ;
Opm::filesystem::path outputFile { outputDir / this->impl->baseName } ;
EclIO::ESmry(outputFile).write_rsm_file();
}
/*
Current implementation will not write restart files for substep,
but there is an unsupported option to the RPTSCHED keyword which

View File

@ -281,6 +281,8 @@ RSVD
SUMMARY
-- -------------------------------------------------------------------------
RUNSUM
-- 1a) Oil rate vs time
FOPR
-- Field Oil Production Rate

View File

@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(RUN) {
msim.run(schedule, io, false);
for (const auto& fname : {"SPE1CASE1.INIT", "SPE1CASE1.UNRST", "SPE1CASE1.EGRID", "SPE1CASE1.SMSPEC", "SPE1CASE1.UNSMRY"})
for (const auto& fname : {"SPE1CASE1.INIT", "SPE1CASE1.UNRST", "SPE1CASE1.EGRID", "SPE1CASE1.SMSPEC", "SPE1CASE1.UNSMRY", "SPE1CASE1.RSM"})
BOOST_CHECK( is_file( fname ));
{

View File

@ -19,6 +19,7 @@
#include "config.h"
#include <opm/io/eclipse/ESmry.hpp>
#include <opm/common/utility/FileSystem.hpp>
#define BOOST_TEST_MODULE Test EclIO
#include <boost/test/unit_test.hpp>
@ -352,6 +353,16 @@ BOOST_AUTO_TEST_CASE(TestESmry_4) {
}
namespace fs = Opm::filesystem;
BOOST_AUTO_TEST_CASE(TestCreateRSM) {
ESmry smry1("SPE1CASE1.SMSPEC");
smry1.write_rsm_file();
BOOST_CHECK(fs::exists("SPE1CASE1.RSM"));
smry1.write_rsm_file("TEST.RSM");
BOOST_CHECK(fs::exists("TEST.RSM"));
}
BOOST_AUTO_TEST_CASE(TestUnits) {
ESmry smry("SPE1CASE1.SMSPEC");