diff --git a/opm/io/eclipse/EclOutput.hpp b/opm/io/eclipse/EclOutput.hpp index 944c50909..b7f815396 100644 --- a/opm/io/eclipse/EclOutput.hpp +++ b/opm/io/eclipse/EclOutput.hpp @@ -29,6 +29,7 @@ namespace Opm { namespace EclIO { namespace OutputStream { class Restart; + class SummarySpecification; }}} namespace Opm { namespace EclIO { @@ -73,6 +74,7 @@ public: void message(const std::string& msg); friend class OutputStream::Restart; + friend class OutputStream::SummarySpecification; private: void writeBinaryHeader(const std::string& arrName, int size, eclArrType arrType); diff --git a/opm/io/eclipse/OutputStream.hpp b/opm/io/eclipse/OutputStream.hpp index 04e57379e..c153ec41e 100644 --- a/opm/io/eclipse/OutputStream.hpp +++ b/opm/io/eclipse/OutputStream.hpp @@ -22,6 +22,8 @@ #include +#include +#include #include #include #include @@ -357,6 +359,75 @@ namespace Opm { namespace EclIO { namespace OutputStream { const std::vector& data); }; + class SummarySpecification + { + public: + using StartTime = std::chrono::system_clock::time_point; + + enum class UnitConvention + { + Metric = 1, + Field = 2, + Lab = 3, + Pvt_M = 4, + }; + + struct RestartSpecification + { + std::string root; + int step; + }; + + class Parameters + { + public: + void add(const std::string& keyword, + const std::string& wgname, + const int num, + const std::string& unit); + + friend class SummarySpecification; + + private: + std::vector> keywords{}; + std::vector> wgnames{}; + std::vector nums{}; + std::vector> units{}; + }; + + explicit SummarySpecification(const ResultSet& rset, + const Formatted& fmt, + const UnitConvention uconv, + const std::array& cartDims, + const RestartSpecification& restart, + const StartTime start); + + ~SummarySpecification(); + + SummarySpecification(const SummarySpecification& rhs) = delete; + SummarySpecification(SummarySpecification&& rhs); + + SummarySpecification& operator=(const SummarySpecification& rhs) = delete; + SummarySpecification& operator=(SummarySpecification&& rhs); + + void write(const Parameters& params); + + private: + int unit_; + int restartStep_; + std::array cartDims_; + StartTime startDate_; + std::vector> restart_; + + /// Summary specification (SMSPEC) file output stream. + std::unique_ptr stream_; + + void rewindStream(); + void flushStream(); + + EclOutput& stream(); + }; + /// Derive filename corresponding to output stream of particular result /// set, with user-specified file extension. /// diff --git a/src/opm/io/eclipse/OutputStream.cpp b/src/opm/io/eclipse/OutputStream.cpp index 0eb7f2861..acc013c50 100644 --- a/src/opm/io/eclipse/OutputStream.cpp +++ b/src/opm/io/eclipse/OutputStream.cpp @@ -19,9 +19,14 @@ #include +#include + #include #include +#include +#include +#include #include #include #include @@ -31,6 +36,7 @@ #include #include #include +#include #include @@ -64,6 +70,11 @@ namespace { { return formatted ? "FRFT" : "RFT"; } + + std::string smspec(const bool formatted) + { + return formatted ? "FSMSPEC" : "SMSPEC"; + } } // namespace FileExtension namespace Open @@ -158,6 +169,20 @@ namespace { }; } } // namespace Rft + + namespace Smspec + { + std::unique_ptr + write(const std::string& filename, + const bool isFmt) + { + return std::unique_ptr { + new Opm::EclIO::EclOutput { + filename, isFmt, std::ios_base::out + } + }; + } + } // namespace Smspec } // namespace Open } // Anonymous namespace @@ -505,6 +530,232 @@ namespace Opm { namespace EclIO { namespace OutputStream { // ===================================================================== +namespace Opm { namespace EclIO { namespace OutputStream { + +namespace { + bool validUnitConvention(const SummarySpecification::UnitConvention uconv) + { + using UConv = SummarySpecification::UnitConvention; + + return (uconv == UConv::Metric) + || (uconv == UConv::Field) + || (uconv == UConv::Lab) + || (uconv == UConv::Pvt_M); + } + + int unitConvention(const SummarySpecification::UnitConvention uconv) + { + const auto unit = static_cast(uconv); + + if (! validUnitConvention(uconv)) { + throw std::invalid_argument { + "Invalid Unit Convention: " + + std::to_string(unit) + }; + } + + return unit; + } + + int makeRestartStep(const SummarySpecification::RestartSpecification& restart) + { + return (restart.step >= 0) ? restart.step : -1; + } + + std::vector> + restartRoot(const SummarySpecification::RestartSpecification& restart) + { + const auto substrLength = std::size_t{8}; + const auto maxSubstrings = std::size_t{9}; + const auto maxStringLength = maxSubstrings * substrLength; + + auto ret = std::vector>{}; + + if (restart.root.empty()) { + return ret; + } + + if (restart.root.size() > maxStringLength) { + const auto msg = "Restart root name of size " + + std::to_string(restart.root.size()) + + " exceeds " + + std::to_string(maxStringLength) + + " character limit (Ignored)"; + + Opm::OpmLog::warning(msg); + + return ret; + } + + ret.resize(maxSubstrings); + + auto remain = restart.root.size(); + + auto i = decltype(ret.size()){0}; + auto p = decltype(remain){0}; + while (remain > decltype(remain){0}) { + const auto nchar = std::min(remain, substrLength); + ret[i++] = restart.root.substr(p, nchar); + + remain -= nchar; + p += nchar; + } + + return ret; + } + + int microSeconds(const int sec) + { + using std::chrono::microseconds; + using std::chrono::seconds; + + const auto us = microseconds(seconds(sec)); + + return static_cast(us.count()); + } + + std::vector + makeStartDate(const SummarySpecification::StartTime start) + { + const auto timepoint = std::chrono::system_clock::to_time_t(start); + const auto tm = *std::gmtime(&timepoint); + + // { Day, Month, Year, Hour, Minute, Seconds } + + return { + // 1..31 1..12 + tm.tm_mday, tm.tm_mon + 1, + + tm.tm_year + 1900, + + // 0..23 0..59 + tm.tm_hour, tm.tm_min, + + // 0..59,999,999 + microSeconds(std::min(tm.tm_sec, 59)) + }; + } + + std::vector + makeDimens(const int nparam, + const std::array& cartDims, + const int istart) + { + return { nparam, cartDims[0], cartDims[1], cartDims[2], 0, istart }; + } +} // Anonymous + +}}} // namespace Opm::EclIO::OutputStream + +void +Opm::EclIO::OutputStream::SummarySpecification:: +Parameters::add(const std::string& keyword, + const std::string& wgname, + const int num, + const std::string& unit) +{ + this->keywords.emplace_back(keyword); + this->wgnames .emplace_back(wgname); + this->nums .push_back (num); + this->units .emplace_back(unit); +} + +Opm::EclIO::OutputStream::SummarySpecification:: +SummarySpecification(const ResultSet& rset, + const Formatted& fmt, + const UnitConvention uconv, + const std::array& cartDims, + const RestartSpecification& restart, + const StartTime start) + : unit_ (unitConvention(uconv)) + , restartStep_(makeRestartStep(restart)) + , cartDims_ (cartDims) + , startDate_ (start) + , restart_ (restartRoot(restart)) +{ + const auto fname = outputFileName(rset, FileExtension::smspec(fmt.set)); + + this->stream_ = Open::Smspec::write(fname, fmt.set); +} + +Opm::EclIO::OutputStream::SummarySpecification::~SummarySpecification() +{} + +Opm::EclIO::OutputStream::SummarySpecification:: +SummarySpecification(SummarySpecification && rhs) + : unit_ (rhs.unit_) + , restartStep_(rhs.restartStep_) + , cartDims_ (std::move(rhs.cartDims_)) + , startDate_ (std::move(rhs.startDate_)) + , restart_ (rhs.restart_) + , stream_ (std::move(rhs.stream_)) +{} + +Opm::EclIO::OutputStream::SummarySpecification& +Opm::EclIO::OutputStream::SummarySpecification:: +operator=(SummarySpecification&& rhs) +{ + this->unit_ = rhs.unit_; + this->restartStep_ = rhs.restartStep_; + + this->cartDims_ = std::move(rhs.cartDims_); + this->startDate_ = std::move(rhs.startDate_); + this->restart_ = rhs.restart_; + this->stream_ = std::move(rhs.stream_); + + return *this; +} + +void +Opm::EclIO::OutputStream:: +SummarySpecification::write(const Parameters& params) +{ + this->rewindStream(); + + auto& smspec = this->stream(); + + // Pretend to be ECLIPSE 100 + smspec.write("INTEHEAD", std::vector{ this->unit_, 100 }); + + if (! this->restart_.empty()) + smspec.write("RESTART", this->restart_); + + smspec.write("DIMENS", + makeDimens(static_cast(params.keywords.size()), + this->cartDims_, this->restartStep_)); + + smspec.write("KEYWORDS", params.keywords); + smspec.write("WGNAMES", params.wgnames); + smspec.write("NUMS", params.nums); + smspec.write("UNITS", params.units); + + smspec.write("STARTDAT", makeStartDate(this->startDate_)); + + this->flushStream(); +} + +void Opm::EclIO::OutputStream::SummarySpecification::rewindStream() +{ + // Benefits from EclOutput friendship + const auto position = std::ofstream::pos_type{0}; + + this->stream().ofileH.seekp(position, std::ios_base::beg); +} + +void Opm::EclIO::OutputStream::SummarySpecification::flushStream() +{ + // Benefits from EclOutput friendship + this->stream().ofileH.flush(); +} + +Opm::EclIO::EclOutput& +Opm::EclIO::OutputStream::SummarySpecification::stream() +{ + return *this->stream_; +} + +// ===================================================================== + std::string Opm::EclIO::OutputStream::outputFileName(const ResultSet& rsetDescriptor, const std::string& ext) diff --git a/tests/test_OutputStream.cpp b/tests/test_OutputStream.cpp index 23eeee292..bdd008b1b 100644 --- a/tests/test_OutputStream.cpp +++ b/tests/test_OutputStream.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include #include #include #include @@ -1730,3 +1732,692 @@ BOOST_AUTO_TEST_CASE(Formatted_Existing) } BOOST_AUTO_TEST_SUITE_END() // Class_RFT + +// ========================================================================== + +BOOST_AUTO_TEST_SUITE(Class_SummarySpecification) + +namespace { + std::time_t advance(const std::time_t tp, const double sec) + { + using namespace std::chrono; + + using TP = time_point; + using DoubSec = duration; + + const auto t = system_clock::from_time_t(tp) + + duration_cast(DoubSec(sec)); + + return system_clock::to_time_t(t); + } + + std::time_t makeUTCTime(const std::tm& timePoint) + { + auto tp = timePoint; // Mutable copy. + const auto ltime = std::mktime(&tp); + auto tmval = *std::gmtime(<ime); // Mutable. + + // offset = ltime - tmval + // == #seconds by which 'ltime' is AHEAD of tmval. + const auto offset = + std::difftime(ltime, std::mktime(&tmval)); + + // Advance 'ltime' by 'offset' so that std::gmtime(return value) will + // have the same broken-down elements as 'tp'. + return advance(ltime, offset); + } + + std::string noWGName() + { + return ":+:+:+:+"; + } + + int noNum() { return 0; } + + Opm::EclIO::OutputStream::SummarySpecification::StartTime + start(const int year, const int month, const int day, + const int hour, const int minute, const int second) + { + using std::chrono::system_clock; + + auto timepoint = std::tm {}; + + timepoint.tm_sec = second; + timepoint.tm_min = minute; + timepoint.tm_hour = hour; + timepoint.tm_mday = day; + timepoint.tm_mon = month - 1; + timepoint.tm_year = year - 1900; + + return system_clock::from_time_t(makeUTCTime(timepoint)); + } + + Opm::EclIO::OutputStream::SummarySpecification::RestartSpecification + noRestart() + { + return { "", -1 }; + } + + Opm::EclIO::OutputStream::SummarySpecification::RestartSpecification + restartedSimulation() + { + // 28 characters = 3x8 + 4 + return { "BASE-RUN-WITH-LONG-CASE-NAME", 123 }; + } + + Opm::EclIO::OutputStream::SummarySpecification::RestartSpecification + restartedSimulationTooLongBasename() + { + return { std::string(73, 'X'), 123 }; + } + + Opm::EclIO::OutputStream::SummarySpecification::Parameters + summaryParameters() + { + auto prm = Opm::EclIO::OutputStream:: + SummarySpecification::Parameters{}; + + prm.add("TIME", noWGName(), noNum(), "DAYS"); + prm.add("WBHP", "PROD01", noNum(), "BARSA"); + prm.add("GGOR", "N-PROD", noNum(), "SM3/SM3"); + prm.add("BGSAT", noWGName(), 523, ""); + + return prm; + } +} // Anonymous + +BOOST_AUTO_TEST_CASE(Unformatted_Base) +{ + using SMSpec = ::Opm::EclIO::OutputStream::SummarySpecification; + + const auto rset = RSet("CASE"); + const auto fmt = ::Opm::EclIO::OutputStream::Formatted{ false }; + const auto cartDims = std::array{ 46, 112, 22 }; // Norne dimensions + + { + using UConv = SMSpec::UnitConvention; + + // Invalid unit convention + const auto uconv = static_cast(1729); + + BOOST_CHECK_THROW(SMSpec(rset, fmt, uconv, cartDims, noRestart(), + start(2019, 10, 1, 12, 34, 56)), + std::invalid_argument); + } + + // ========================= METRIC ======================= + { + const auto uconv = SMSpec::UnitConvention::Metric; + + auto smspec = SMSpec { + rset, fmt, uconv, cartDims, noRestart(), + start(2019, 10, 1, 12, 34, 56) + }; + + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "SMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + BOOST_CHECK_MESSAGE(! smspec.hasKey("RESTART"), "SMSPEC File must NOT have 'RESTART'"); + + { + const auto vectors = smspec.getList(); + const auto expect_vectors = std::vector{ + Opm::EclIO::EclFile::EclEntry{"INTEHEAD", Opm::EclIO::eclArrType::INTE, 2}, + Opm::EclIO::EclFile::EclEntry{"DIMENS", Opm::EclIO::eclArrType::INTE, 6}, + Opm::EclIO::EclFile::EclEntry{"KEYWORDS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"WGNAMES", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"NUMS", Opm::EclIO::eclArrType::INTE, 4}, + Opm::EclIO::EclFile::EclEntry{"UNITS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"STARTDAT", Opm::EclIO::eclArrType::INTE, 6}, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(vectors.begin(), vectors.end(), + expect_vectors.begin(), + expect_vectors.end()); + } + + smspec.loadData(); + + { + const auto& Ih = smspec.get("INTEHEAD"); + const auto expect = std::vector{ 1, 100 }; + BOOST_CHECK_EQUAL_COLLECTIONS(Ih.begin(), Ih.end(), + expect.begin(), + expect.end()); + } + + { + const auto& D = smspec.get("DIMENS"); + const auto expect = std::vector { + 4, 46, 112, 22, 0, -1 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(D.begin(), D.end(), + expect.begin(), expect.end()); + } + + { + const auto& K = smspec.get("KEYWORDS"); + const auto expect = std::vector { + "TIME", "WBHP", "GGOR", "BGSAT" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(K.begin(), K.end(), + expect.begin(), expect.end()); + } + + { + const auto& W = smspec.get("WGNAMES"); + const auto expect = std::vector { + ":+:+:+:+", "PROD01", "N-PROD", ":+:+:+:+" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(W.begin(), W.end(), + expect.begin(), expect.end()); + } + + { + const auto& N = smspec.get("NUMS"); + const auto expect = std::vector { 0, 0, 0, 523 }; + + BOOST_CHECK_EQUAL_COLLECTIONS(N.begin(), N.end(), + expect.begin(), expect.end()); + } + + { + const auto& U = smspec.get("UNITS"); + const auto expect = std::vector { + "DAYS", "BARSA", "SM3/SM3", "" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(U.begin(), U.end(), + expect.begin(), expect.end()); + } + + { + const auto& S = smspec.get("STARTDAT"); + const auto expect = std::vector { + 1, 10, 2019, 12, 34, + 56 * 1000 * 1000 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(S.begin(), S.end(), + expect.begin(), expect.end()); + } + } + + // ========================= FIELD ======================= + { + const auto uconv = SMSpec::UnitConvention::Field; + + auto smspec = SMSpec { + rset, fmt, uconv, cartDims, noRestart(), + start(1970, 1, 1, 0, 0, 0) + }; + + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "SMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + { + const auto vectors = smspec.getList(); + const auto expect_vectors = std::vector{ + Opm::EclIO::EclFile::EclEntry{"INTEHEAD", Opm::EclIO::eclArrType::INTE, 2}, + Opm::EclIO::EclFile::EclEntry{"DIMENS", Opm::EclIO::eclArrType::INTE, 6}, + Opm::EclIO::EclFile::EclEntry{"KEYWORDS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"WGNAMES", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"NUMS", Opm::EclIO::eclArrType::INTE, 4}, + Opm::EclIO::EclFile::EclEntry{"UNITS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"STARTDAT", Opm::EclIO::eclArrType::INTE, 6}, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(vectors.begin(), vectors.end(), + expect_vectors.begin(), + expect_vectors.end()); + } + + smspec.loadData(); + + { + const auto& Ih = smspec.get("INTEHEAD"); + const auto expect = std::vector{ 2, 100 }; + BOOST_CHECK_EQUAL_COLLECTIONS(Ih.begin(), Ih.end(), + expect.begin(), + expect.end()); + } + + { + const auto& D = smspec.get("DIMENS"); + const auto expect = std::vector { + 4, 46, 112, 22, 0, -1 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(D.begin(), D.end(), + expect.begin(), expect.end()); + } + + { + const auto& K = smspec.get("KEYWORDS"); + const auto expect = std::vector { + "TIME", "WBHP", "GGOR", "BGSAT" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(K.begin(), K.end(), + expect.begin(), expect.end()); + } + + { + const auto& W = smspec.get("WGNAMES"); + const auto expect = std::vector { + ":+:+:+:+", "PROD01", "N-PROD", ":+:+:+:+" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(W.begin(), W.end(), + expect.begin(), expect.end()); + } + + { + const auto& N = smspec.get("NUMS"); + const auto expect = std::vector { 0, 0, 0, 523 }; + + BOOST_CHECK_EQUAL_COLLECTIONS(N.begin(), N.end(), + expect.begin(), expect.end()); + } + + { + const auto& U = smspec.get("UNITS"); + const auto expect = std::vector { + // (!) (!) + "DAYS", "BARSA", "SM3/SM3", "" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(U.begin(), U.end(), + expect.begin(), expect.end()); + } + + { + const auto& S = smspec.get("STARTDAT"); + const auto expect = std::vector { + 1, 1, 1970, 0, 0, 0 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(S.begin(), S.end(), + expect.begin(), expect.end()); + } + } + + // ========================= LAB ======================= + { + const auto uconv = SMSpec::UnitConvention::Lab; + + auto smspec = SMSpec { + rset, fmt, uconv, cartDims, noRestart(), + start(2018, 12, 24, 17, 0, 0) + }; + + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "SMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + { + const auto vectors = smspec.getList(); + const auto expect_vectors = std::vector{ + Opm::EclIO::EclFile::EclEntry{"INTEHEAD", Opm::EclIO::eclArrType::INTE, 2}, + Opm::EclIO::EclFile::EclEntry{"DIMENS", Opm::EclIO::eclArrType::INTE, 6}, + Opm::EclIO::EclFile::EclEntry{"KEYWORDS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"WGNAMES", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"NUMS", Opm::EclIO::eclArrType::INTE, 4}, + Opm::EclIO::EclFile::EclEntry{"UNITS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"STARTDAT", Opm::EclIO::eclArrType::INTE, 6}, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(vectors.begin(), vectors.end(), + expect_vectors.begin(), + expect_vectors.end()); + } + + smspec.loadData(); + + { + const auto& Ih = smspec.get("INTEHEAD"); + const auto expect = std::vector{ 3, 100 }; + BOOST_CHECK_EQUAL_COLLECTIONS(Ih.begin(), Ih.end(), + expect.begin(), + expect.end()); + } + + { + const auto& D = smspec.get("DIMENS"); + const auto expect = std::vector { + 4, 46, 112, 22, 0, -1 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(D.begin(), D.end(), + expect.begin(), expect.end()); + } + + { + const auto& K = smspec.get("KEYWORDS"); + const auto expect = std::vector { + "TIME", "WBHP", "GGOR", "BGSAT" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(K.begin(), K.end(), + expect.begin(), expect.end()); + } + + { + const auto& W = smspec.get("WGNAMES"); + const auto expect = std::vector { + ":+:+:+:+", "PROD01", "N-PROD", ":+:+:+:+" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(W.begin(), W.end(), + expect.begin(), expect.end()); + } + + { + const auto& N = smspec.get("NUMS"); + const auto expect = std::vector { 0, 0, 0, 523 }; + + BOOST_CHECK_EQUAL_COLLECTIONS(N.begin(), N.end(), + expect.begin(), expect.end()); + } + + { + const auto& U = smspec.get("UNITS"); + const auto expect = std::vector { + // (!) (!) + "DAYS", "BARSA", "SM3/SM3", "" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(U.begin(), U.end(), + expect.begin(), expect.end()); + } + + { + const auto& S = smspec.get("STARTDAT"); + const auto expect = std::vector { + 24, 12, 2018, 17, 0, 0 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(S.begin(), S.end(), + expect.begin(), expect.end()); + } + } + + // ========================= PVT-M ======================= + { + const auto uconv = SMSpec::UnitConvention::Pvt_M; + + auto smspec = SMSpec { + rset, fmt, uconv, cartDims, noRestart(), + start(1983, 1, 1, 1, 2, 3) + }; + + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "SMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + { + const auto vectors = smspec.getList(); + const auto expect_vectors = std::vector{ + Opm::EclIO::EclFile::EclEntry{"INTEHEAD", Opm::EclIO::eclArrType::INTE, 2}, + Opm::EclIO::EclFile::EclEntry{"DIMENS", Opm::EclIO::eclArrType::INTE, 6}, + Opm::EclIO::EclFile::EclEntry{"KEYWORDS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"WGNAMES", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"NUMS", Opm::EclIO::eclArrType::INTE, 4}, + Opm::EclIO::EclFile::EclEntry{"UNITS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"STARTDAT", Opm::EclIO::eclArrType::INTE, 6}, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(vectors.begin(), vectors.end(), + expect_vectors.begin(), + expect_vectors.end()); + } + + smspec.loadData(); + + { + const auto& Ih = smspec.get("INTEHEAD"); + const auto expect = std::vector{ 4, 100 }; + BOOST_CHECK_EQUAL_COLLECTIONS(Ih.begin(), Ih.end(), + expect.begin(), + expect.end()); + } + + { + const auto& D = smspec.get("DIMENS"); + const auto expect = std::vector { + 4, 46, 112, 22, 0, -1 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(D.begin(), D.end(), + expect.begin(), expect.end()); + } + + { + const auto& K = smspec.get("KEYWORDS"); + const auto expect = std::vector { + "TIME", "WBHP", "GGOR", "BGSAT" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(K.begin(), K.end(), + expect.begin(), expect.end()); + } + + { + const auto& W = smspec.get("WGNAMES"); + const auto expect = std::vector { + ":+:+:+:+", "PROD01", "N-PROD", ":+:+:+:+" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(W.begin(), W.end(), + expect.begin(), expect.end()); + } + + { + const auto& N = smspec.get("NUMS"); + const auto expect = std::vector { 0, 0, 0, 523 }; + + BOOST_CHECK_EQUAL_COLLECTIONS(N.begin(), N.end(), + expect.begin(), expect.end()); + } + + { + const auto& U = smspec.get("UNITS"); + const auto expect = std::vector { + // (!) (!) + "DAYS", "BARSA", "SM3/SM3", "" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(U.begin(), U.end(), + expect.begin(), expect.end()); + } + + { + const auto& S = smspec.get("STARTDAT"); + const auto expect = std::vector { + 1, 1, 1983, 1, 2, 3 * 1000 * 1000 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(S.begin(), S.end(), + expect.begin(), expect.end()); + } + } +} + +BOOST_AUTO_TEST_CASE(Formatted_Restarted) +{ + using SMSpec = ::Opm::EclIO::OutputStream::SummarySpecification; + + const auto rset = RSet("CASE"); + const auto fmt = ::Opm::EclIO::OutputStream::Formatted{ true }; + const auto cartDims = std::array{ 46, 112, 22 }; // Norne dimensions + + // === Restart root name too long ========================= + { + using UConv = SMSpec::UnitConvention; + + auto smspec = SMSpec { + rset, fmt, UConv::Pvt_M, cartDims, + restartedSimulationTooLongBasename(), + start(2019, 10, 1, 12, 34, 56) + }; + + // Should *NOT* write RESTART vector (name too long). + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "FSMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + BOOST_CHECK_MESSAGE(! smspec.hasKey("RESTART"), + "SMSPEC file must NOT have RESTART " + "data if root name is too long"); + } + + // ========================= METRIC ======================= + { + const auto uconv = SMSpec::UnitConvention::Metric; + + auto smspec = SMSpec { + rset, fmt, uconv, cartDims, restartedSimulation(), + start(2019, 10, 1, 12, 34, 56) + }; + + smspec.write(summaryParameters()); + } + + { + const auto fname = ::Opm::EclIO::OutputStream:: + outputFileName(rset, "FSMSPEC"); + + auto smspec = ::Opm::EclIO::EclFile{fname}; + + { + const auto vectors = smspec.getList(); + const auto expect_vectors = std::vector{ + Opm::EclIO::EclFile::EclEntry{"INTEHEAD", Opm::EclIO::eclArrType::INTE, 2}, + Opm::EclIO::EclFile::EclEntry{"RESTART", Opm::EclIO::eclArrType::CHAR, 9}, + Opm::EclIO::EclFile::EclEntry{"DIMENS", Opm::EclIO::eclArrType::INTE, 6}, + Opm::EclIO::EclFile::EclEntry{"KEYWORDS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"WGNAMES", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"NUMS", Opm::EclIO::eclArrType::INTE, 4}, + Opm::EclIO::EclFile::EclEntry{"UNITS", Opm::EclIO::eclArrType::CHAR, 4}, + Opm::EclIO::EclFile::EclEntry{"STARTDAT", Opm::EclIO::eclArrType::INTE, 6}, + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(vectors.begin(), vectors.end(), + expect_vectors.begin(), + expect_vectors.end()); + } + + smspec.loadData(); + + { + const auto& Ih = smspec.get("INTEHEAD"); + const auto expect = std::vector{ 1, 100 }; + BOOST_CHECK_EQUAL_COLLECTIONS(Ih.begin(), Ih.end(), + expect.begin(), + expect.end()); + } + + { + const auto& R = smspec.get("RESTART"); + const auto expect = std::vector { + "BASE-RUN", "-WITH-LO", "NG-CASE-", // 0 .. 2 + "NAME" , "" , "" , // 3 .. 5 + "" , "" , "" , // 6 .. 8 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(R.begin(), R.end(), + expect.begin(), expect.end()); + } + + { + const auto& D = smspec.get("DIMENS"); + const auto expect = std::vector { + 4, 46, 112, 22, 0, 123 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(D.begin(), D.end(), + expect.begin(), expect.end()); + } + + { + const auto& K = smspec.get("KEYWORDS"); + const auto expect = std::vector { + "TIME", "WBHP", "GGOR", "BGSAT" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(K.begin(), K.end(), + expect.begin(), expect.end()); + } + + { + const auto& W = smspec.get("WGNAMES"); + const auto expect = std::vector { + ":+:+:+:+", "PROD01", "N-PROD", ":+:+:+:+" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(W.begin(), W.end(), + expect.begin(), expect.end()); + } + + { + const auto& N = smspec.get("NUMS"); + const auto expect = std::vector { 0, 0, 0, 523 }; + + BOOST_CHECK_EQUAL_COLLECTIONS(N.begin(), N.end(), + expect.begin(), expect.end()); + } + + { + const auto& U = smspec.get("UNITS"); + const auto expect = std::vector { + "DAYS", "BARSA", "SM3/SM3", "" + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(U.begin(), U.end(), + expect.begin(), expect.end()); + } + + { + const auto& S = smspec.get("STARTDAT"); + const auto expect = std::vector { + 1, 10, 2019, 12, 34, + 56 * 1000 * 1000 + }; + + BOOST_CHECK_EQUAL_COLLECTIONS(S.begin(), S.end(), + expect.begin(), expect.end()); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() // Class_SummarySpecification