Implement writing of RSM file
This commit is contained in:
parent
7c0ddd0b9d
commit
ea269c7cc7
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
194
src/opm/io/eclipse/ESmry_write_rsm.cpp
Normal file
194
src/opm/io/eclipse/ESmry_write_rsm.cpp
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -281,6 +281,8 @@ RSVD
|
||||
SUMMARY
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
RUNSUM
|
||||
|
||||
-- 1a) Oil rate vs time
|
||||
FOPR
|
||||
-- Field Oil Production Rate
|
||||
|
@ -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 ));
|
||||
|
||||
{
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user