Merge pull request #2684 from joakim-hove/file-deck
rst_deck: application to create a deck prepared for restart
This commit is contained in:
commit
5a3eca0fe5
@ -42,11 +42,13 @@ set(OPM_PROJECT_EXTRA_CODE_INTREE "#ENABLE_ECL_INPUT is needed by opm-common-pre
|
||||
if(ENABLE_ECL_OUTPUT)
|
||||
set(OPM_PROJECT_EXTRA_CODE_INSTALLED "${OPM_PROJECT_EXTRA_CODE_INSTALLED}
|
||||
set(COMPARE_ECL_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/compareECL)
|
||||
set(OPM_PACK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/opmpack)")
|
||||
set(OPM_PACK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/opmpack)
|
||||
set(RST_DECK_COMMAND ${CMAKE_INSTALL_PREFIX}/bin${${name}_VER_DIR}/rst_deck)")
|
||||
|
||||
set(OPM_PROJECT_EXTRA_CODE_INTREE "${OPM_PROJECT_EXTRA_CODE_INTREE}
|
||||
set(COMPARE_ECL_COMMAND ${PROJECT_BINARY_DIR}/bin/compareECL)
|
||||
set(OPM_PACK_COMMAND ${PROJECT_BINARY_DIR}/bin/opmpack)")
|
||||
set(OPM_PACK_COMMAND ${PROJECT_BINARY_DIR}/bin/opmpack)
|
||||
set(RST_DECK_COMMAND ${PROJECT_BINARY_DIR}/bin/rst_deck)")
|
||||
endif()
|
||||
|
||||
# project information is in dune.module. Read this file and set variables.
|
||||
|
@ -46,6 +46,8 @@ if(ENABLE_ECL_INPUT)
|
||||
src/opm/io/eclipse/SummaryNode.cpp
|
||||
src/opm/json/JsonObject.cpp
|
||||
src/opm/parser/eclipse/Deck/Deck.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckTree.cpp
|
||||
src/opm/parser/eclipse/Deck/FileDeck.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckItem.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckValue.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckKeyword.cpp
|
||||
@ -563,6 +565,7 @@ if(ENABLE_ECL_INPUT)
|
||||
examples/opmi.cpp
|
||||
examples/opmpack.cpp
|
||||
examples/opmhash.cpp
|
||||
examples/rst_deck.cpp
|
||||
examples/wellgraph.cpp
|
||||
examples/make_ext_smry.cpp
|
||||
)
|
||||
@ -577,6 +580,7 @@ if(ENABLE_ECL_INPUT)
|
||||
examples/opmi.cpp
|
||||
examples/opmpack.cpp
|
||||
examples/opmhash.cpp
|
||||
examples/rst_deck.cpp
|
||||
examples/make_esmry.cpp
|
||||
)
|
||||
endif()
|
||||
@ -851,7 +855,9 @@ if(ENABLE_ECL_INPUT)
|
||||
opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQFunctionTable.hpp
|
||||
opm/parser/eclipse/Deck/DeckItem.hpp
|
||||
opm/parser/eclipse/Deck/Deck.hpp
|
||||
opm/parser/eclipse/Deck/FileDeck.hpp
|
||||
opm/parser/eclipse/Deck/DeckSection.hpp
|
||||
opm/parser/eclipse/Deck/DeckTree.hpp
|
||||
opm/parser/eclipse/Deck/DeckOutput.hpp
|
||||
opm/parser/eclipse/Deck/DeckValue.hpp
|
||||
opm/parser/eclipse/Deck/DeckKeyword.hpp
|
||||
|
@ -69,6 +69,11 @@ opm_add_test( rst_msw
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
TEST_ARGS tests/MSW.DATA tests/MSW_RESTART.DATA )
|
||||
|
||||
add_test( NAME rst_deck_test
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tests/rst_test_driver.sh ${PROJECT_BINARY_DIR}/bin/rst_deck ${PROJECT_BINARY_DIR}/bin/opmhash
|
||||
${PROJECT_SOURCE_DIR}/tests/SPE1CASE2_INCLUDE.DATA)
|
||||
|
||||
|
||||
# opm-tests dependent tests
|
||||
if(HAVE_OPM_TESTS)
|
||||
opm_add_test(parse_write ONLY_COMPILE
|
||||
@ -113,8 +118,17 @@ if(HAVE_OPM_TESTS)
|
||||
opm_add_test("SPE9_CP_GROUP2" NO_COMPILE EXE_NAME parse_write TEST_ARGS "${OPM_TESTS_ROOT}/spe9group/SPE9_CP_GROUP.DATA")
|
||||
set_property(TEST NORNE_ATW2013
|
||||
PROPERTY ENVIRONMENT "OPM_ERRORS_IGNORE=PARSE_RANDOM_SLASH")
|
||||
|
||||
add_test( NAME rst_deck_test_norne
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tests/rst_test_driver.sh ${CMAKE_BINARY_DIR}/bin/rst_deck ${CMAKE_BINARY_DIR}/bin/opmhash
|
||||
${OPM_TESTS_ROOT}/norne/NORNE_ATW2013.DATA)
|
||||
|
||||
set_property(TEST rst_deck_test_norne
|
||||
PROPERTY ENVIRONMENT "OPM_ERRORS_IGNORE=PARSE_RANDOM_SLASH")
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
# JSON tests
|
||||
opm_add_test(jsonTests
|
||||
SOURCES tests/json/jsonTests.cpp
|
||||
|
@ -1,6 +1,7 @@
|
||||
set(genkw_SOURCES src/opm/json/JsonObject.cpp
|
||||
src/opm/parser/eclipse/Parser/createDefaultKeywordList.cpp
|
||||
src/opm/parser/eclipse/Deck/UDAValue.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckTree.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckValue.cpp
|
||||
src/opm/parser/eclipse/Deck/Deck.cpp
|
||||
src/opm/parser/eclipse/Deck/DeckItem.cpp
|
||||
|
7
docs/man1/rst_deck.1
Normal file
7
docs/man1/rst_deck.1
Normal file
@ -0,0 +1,7 @@
|
||||
.TH RST_DECK
|
||||
.SH NAME
|
||||
rst_deck \- Create a version of deck ready for restart
|
||||
|
||||
|
||||
|
||||
|
297
examples/rst_deck.cpp
Normal file
297
examples/rst_deck.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
Copyright 2021 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 <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <getopt.h>
|
||||
#include <fmt/format.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/DeckValue.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/I.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/P.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/R.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParserKeywords/S.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
|
||||
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
|
||||
#include <opm/parser/eclipse/Parser/InputErrorAction.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/FileDeck.hpp>
|
||||
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
const std::unordered_set<std::string> remove_from_solution = {"EQUIL", "PRESSURE", "SWAT", "SGAS"};
|
||||
const std::unordered_set<std::string> keep_in_solution = {"ABC"};
|
||||
|
||||
void print_help_and_exit(const std::optional<std::string> error_msg = {}) {
|
||||
|
||||
if (error_msg.has_value()) {
|
||||
std::cerr << "Error:" << std::endl;
|
||||
std::cerr << error_msg.value() << std::endl;
|
||||
std::cerr << "------------------------------------------------------" << std::endl;
|
||||
}
|
||||
|
||||
std::string keep_keywords;
|
||||
for (const auto& kw : keep_in_solution)
|
||||
keep_keywords += kw + " ";
|
||||
|
||||
const std::string help_text = fmt::format(R"(
|
||||
|
||||
The rst_deck program will load a simulation deck and parameters for a restart
|
||||
and reformat the deck to become a restart deck. Before the updated deck is
|
||||
output the program will update the SOLUTION and SCHEDULE sections. All keywords
|
||||
from the SOLUTION section will be cleared out(1) and a RESTART keyword will be
|
||||
inserted. In the SCHEDULE section the program can either remove all keywords up
|
||||
until the restart date, or alternatively insert SKIPREST immediately following
|
||||
the SCHEDULE keyword(2).
|
||||
|
||||
When creating the updated restart deck the program can either link to unmodified
|
||||
include files with INCLUDE statements, create a copy of deck structure in an
|
||||
alternative location or create one large file with all keywords in the same
|
||||
file. Apart from the alterations to support restart the output deck will be
|
||||
equivalent to the input deck, but formatting is not retained and comments have
|
||||
been stripped away.
|
||||
|
||||
Arguments:
|
||||
|
||||
1. The data file we are starting with.
|
||||
|
||||
2. The basename of the restart file - with an optional path prefix and a :N to
|
||||
restart from step N(3). A restart step value of 0 is interpreted as a dry run
|
||||
- a deck which has not been set up for restart will be written out.
|
||||
|
||||
3. Basename of the restart deck we create, can optionally contain a path prefix;
|
||||
the path will be created if it does not already exist. This argument is
|
||||
optional, if it is not provided the program will dump a restart deck on
|
||||
stdout. If the argument corresponds to an existing directory the restart case
|
||||
will get the same name as the base case.
|
||||
|
||||
Options:
|
||||
|
||||
-s: Manipulate the SCHEDULE section by inserting a SKIPREST keyword immediately
|
||||
following the SCHEDULE keyword. If the -s option is not used the SCHEDULE
|
||||
section will be modified by removing all keywords until we reach the restart
|
||||
date. NB: Currently the -s option is required
|
||||
|
||||
-m: [share|inline|copy] The restart deck can reuse the unmodified include files
|
||||
from the base case, this is mode 'share' and is the default. With mode
|
||||
'inline' the restart deck will be one long file and with mode 'copy' the
|
||||
file structure of the base case will be retained. The default if no -m
|
||||
option is given is the 'share' mode.
|
||||
|
||||
In the case of 'share' and 'copy' the correct path to include files will be
|
||||
negotiated based on the path given to the output case in the third argument.
|
||||
If the restart deck is passed to stdout the include files will be resolved
|
||||
based on output in cwd.
|
||||
|
||||
Example:
|
||||
|
||||
rst_deck /path/to/history/HISTORY.DATA rst/HISTORY:30 /path/to/rst/RESTART -s
|
||||
|
||||
1: The program has a compiled list of keywords which will be retained in the
|
||||
SOLUTION section. The current value of that list is: {}
|
||||
|
||||
2: Current version of the program *only* supports the SKIPREST option, and the
|
||||
-s option is required.
|
||||
|
||||
3: The second argument is treated purely as a string and inserted verbatim into
|
||||
the updated restart deck. In a future version we might interpret the second
|
||||
argument as a file path and check the content and also do filesystem
|
||||
manipulations from it.
|
||||
|
||||
)", keep_keywords);
|
||||
|
||||
std::cerr << help_text << std::endl;
|
||||
if (error_msg.has_value())
|
||||
std::exit(EXIT_FAILURE);
|
||||
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct options {
|
||||
std::string input_deck;
|
||||
std::pair<std::string, int> restart;
|
||||
std::optional<std::string> target;
|
||||
|
||||
Opm::FileDeck::OutputMode mode{Opm::FileDeck::OutputMode::SHARE};
|
||||
bool skiprest{false};
|
||||
};
|
||||
|
||||
|
||||
Opm::FileDeck load_deck(const options& opt) {
|
||||
Opm::ParseContext parseContext(Opm::InputError::WARN);
|
||||
Opm::ErrorGuard errors;
|
||||
Opm::Parser parser;
|
||||
|
||||
/* Use the same default ParseContext as flow. */
|
||||
parseContext.update(Opm::ParseContext::PARSE_RANDOM_SLASH, Opm::InputError::IGNORE);
|
||||
parseContext.update(Opm::ParseContext::PARSE_MISSING_DIMS_KEYWORD, Opm::InputError::WARN);
|
||||
parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_WELL, Opm::InputError::WARN);
|
||||
parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_GROUP, Opm::InputError::WARN);
|
||||
auto deck = parser.parseFile(opt.input_deck, parseContext, errors);
|
||||
return Opm::FileDeck{ deck };
|
||||
}
|
||||
|
||||
|
||||
Opm::FileDeck::OutputMode mode(const std::string& mode_arg) {
|
||||
if (mode_arg == "inline")
|
||||
return Opm::FileDeck::OutputMode::INLINE;
|
||||
|
||||
if (mode_arg == "share")
|
||||
return Opm::FileDeck::OutputMode::SHARE;
|
||||
|
||||
if (mode_arg == "copy")
|
||||
return Opm::FileDeck::OutputMode::COPY;
|
||||
|
||||
print_help_and_exit(fmt::format("Mode argument: \'{}\' not recognized. Valid options are inline|share|copy", mode_arg));
|
||||
return Opm::FileDeck::OutputMode::INLINE;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::size_t> split_restart(const std::string& restart_base) {
|
||||
auto sep_pos = restart_base.rfind(':');
|
||||
if (sep_pos == std::string::npos)
|
||||
print_help_and_exit(fmt::format("Expected restart argument on the form: BASE:NUMBER - e.g. HISTORY:60"));
|
||||
|
||||
return std::make_pair(restart_base.substr(0, sep_pos), std::stoi(restart_base.substr(sep_pos + 1)));
|
||||
}
|
||||
|
||||
|
||||
options load_options(int argc, char **argv) {
|
||||
options opt;
|
||||
while (true) {
|
||||
int c;
|
||||
c = getopt(argc, argv, "hm:s");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case 'm':
|
||||
opt.mode = mode(optarg);
|
||||
break;
|
||||
case 's':
|
||||
opt.skiprest = true;
|
||||
break;
|
||||
case 'h':
|
||||
print_help_and_exit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto arg_offset = optind;
|
||||
if (arg_offset >= argc)
|
||||
print_help_and_exit();
|
||||
|
||||
opt.input_deck = argv[arg_offset];
|
||||
opt.restart = split_restart(argv[arg_offset + 1]);
|
||||
if ((argc - arg_offset) >= 3) {
|
||||
opt.target = argv[arg_offset + 2];
|
||||
if (opt.mode == Opm::FileDeck::OutputMode::COPY) {
|
||||
auto target = fs::path(opt.target.value()).parent_path();
|
||||
if (fs::exists(target)) {
|
||||
auto input = fs::path(opt.input_deck).parent_path();
|
||||
if (fs::equivalent(target, input))
|
||||
opt.mode = Opm::FileDeck::OutputMode::SHARE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (opt.mode == Opm::FileDeck::OutputMode::COPY)
|
||||
print_help_and_exit("When writing output to stdout you must use inline|share mode");
|
||||
}
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
|
||||
void update_solution(const options& opt, Opm::FileDeck& file_deck)
|
||||
{
|
||||
if (opt.restart.second == 0)
|
||||
return;
|
||||
|
||||
const auto solution = file_deck.find("SOLUTION");
|
||||
if (!solution.has_value())
|
||||
print_help_and_exit(fmt::format("Could not find SOLUTION section in input deck: {}", opt.input_deck));
|
||||
|
||||
const auto summary = file_deck.find("SUMMARY");
|
||||
if (!summary.has_value())
|
||||
print_help_and_exit(fmt::format("Could not find SUMMARY section in input deck: {}", opt.input_deck));
|
||||
|
||||
auto index = solution.value();
|
||||
{
|
||||
while (true) {
|
||||
const auto& keyword = file_deck[++index];
|
||||
if (keep_in_solution.count(keyword.name()) == 0)
|
||||
file_deck.erase(index);
|
||||
|
||||
if (index == summary)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Opm::UnitSystem units;
|
||||
std::vector<Opm::DeckValue> values{ Opm::DeckValue{opt.restart.first}, Opm::DeckValue{opt.restart.second} };
|
||||
Opm::DeckKeyword restart(Opm::ParserKeywords::RESTART(), std::vector<std::vector<Opm::DeckValue>>{ values }, units, units);
|
||||
|
||||
auto schedule = file_deck.find("SCHEDULE");
|
||||
file_deck.insert(++schedule.value(), restart);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void update_schedule(const options& opt, Opm::FileDeck& file_deck)
|
||||
{
|
||||
if (opt.restart.second == 0)
|
||||
return;
|
||||
|
||||
const auto schedule = file_deck.find("SCHEDULE");
|
||||
if (opt.skiprest) {
|
||||
Opm::DeckKeyword skiprest( Opm::ParserKeywords::SKIPREST{} );
|
||||
|
||||
auto index = schedule.value();
|
||||
file_deck.insert(++index, skiprest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
auto options = load_options(argc, argv);
|
||||
auto file_deck = load_deck(options);
|
||||
update_solution(options, file_deck);
|
||||
update_schedule(options, file_deck);
|
||||
if (!options.target.has_value())
|
||||
file_deck.dump_stdout(fs::current_path(), options.mode);
|
||||
else {
|
||||
std::string target = options.target.value();
|
||||
if (fs::is_directory(target))
|
||||
file_deck.dump( fs::absolute(target), fs::path(options.input_deck).filename(), options.mode );
|
||||
else {
|
||||
auto target_path = fs::path( fs::absolute(options.target.value()) );
|
||||
file_deck.dump( fs::absolute(target_path.parent_path()), target_path.filename(), options.mode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/DeckTree.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
|
||||
@ -153,6 +154,8 @@ namespace Opm {
|
||||
std::string getDataFile() const;
|
||||
void setDataFile(const std::string& dataFile);
|
||||
std::string makeDeckPath(const std::string& path) const;
|
||||
DeckTree& tree();
|
||||
DeckTree tree() const;
|
||||
|
||||
std::size_t size() const;
|
||||
bool empty() const;
|
||||
@ -183,6 +186,7 @@ namespace Opm {
|
||||
|
||||
std::optional<std::string> m_dataFile;
|
||||
std::string input_path;
|
||||
DeckTree file_tree;
|
||||
mutable std::size_t unit_system_access_count = 0;
|
||||
};
|
||||
}
|
||||
|
71
opm/parser/eclipse/Deck/DeckTree.hpp
Normal file
71
opm/parser/eclipse/Deck/DeckTree.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2021 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 DECK_TREE_HPP
|
||||
#define DECK_TREE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
/*
|
||||
The purpose of the DeckTree class is to maintain a minimal relationship
|
||||
between the include files in the deck; the sole purpose of this class is to
|
||||
support writing of decks with the keywords in the correct files.
|
||||
*/
|
||||
|
||||
class DeckTree {
|
||||
public:
|
||||
DeckTree() = default;
|
||||
DeckTree(const std::string&);
|
||||
|
||||
const std::string& parent(const std::string& fname) const;
|
||||
bool includes(const std::string& parent_file, const std::string& include_file) const;
|
||||
void add_include(std::string parent_file, std::string include_file);
|
||||
void add_root(const std::string& fname);
|
||||
bool has_include(const std::string& fname) const;
|
||||
const std::string& root() const;
|
||||
|
||||
private:
|
||||
class TreeNode {
|
||||
public:
|
||||
explicit TreeNode(const std::string& fn);
|
||||
TreeNode(const std::string& pn, const std::string& fn);
|
||||
void add_include(const std::string& include_file);
|
||||
bool includes(const std::string& include_file) const;
|
||||
|
||||
std::string fname;
|
||||
std::optional<std::string> parent;
|
||||
std::unordered_set<std::string> include_files;
|
||||
};
|
||||
|
||||
std::string add_node(const std::string& fname);
|
||||
|
||||
std::optional<std::string> root_file;
|
||||
std::unordered_map<std::string, TreeNode> nodes;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
#endif /* DECKRECORD_HPP */
|
||||
|
155
opm/parser/eclipse/Deck/FileDeck.hpp
Normal file
155
opm/parser/eclipse/Deck/FileDeck.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2021 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 FILE_DECK_HPP
|
||||
#define FILE_DECK_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/parser/eclipse/Deck/FileDeck.hpp>
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
namespace Opm {
|
||||
class Deck;
|
||||
|
||||
class FileDeck {
|
||||
public:
|
||||
|
||||
enum class OutputMode {
|
||||
INLINE = 1,
|
||||
SHARE = 2,
|
||||
COPY = 3
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Index {
|
||||
std::size_t file_index;
|
||||
std::size_t keyword_index;
|
||||
|
||||
Index(std::size_t file_index_arg, std::size_t keyword_index_arg, const FileDeck* deck_arg)
|
||||
: file_index(file_index_arg)
|
||||
, keyword_index(keyword_index_arg)
|
||||
, deck(deck_arg)
|
||||
{}
|
||||
|
||||
Index& operator--();
|
||||
Index operator--(int);
|
||||
Index& operator++();
|
||||
Index operator++(int);
|
||||
bool operator==(const Index& other) const;
|
||||
bool operator!=(const Index& other) const;
|
||||
|
||||
|
||||
private:
|
||||
const FileDeck * deck;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Block {
|
||||
public:
|
||||
explicit Block(const std::string& filename);
|
||||
std::size_t size() const;
|
||||
void load(const Deck& deck, std::size_t deck_index);
|
||||
std::optional<std::size_t> find(const std::string& keyword) const;
|
||||
bool empty() const;
|
||||
void erase(const FileDeck::Index& index);
|
||||
void insert(std::size_t keyword_index, const DeckKeyword& keyword);
|
||||
void dump(DeckOutput& out) const;
|
||||
|
||||
private:
|
||||
std::string fname;
|
||||
std::vector<DeckKeyword> keywords;
|
||||
|
||||
friend FileDeck;
|
||||
};
|
||||
|
||||
|
||||
explicit FileDeck(const Deck& deck);
|
||||
std::optional<Index> find(const std::string& keyword) const;
|
||||
void erase(const Index& index);
|
||||
void erase(const Index& begin, const Index& end);
|
||||
void insert(const Index& index, const DeckKeyword& keyword);
|
||||
|
||||
void dump_stdout(const std::string& output_dir, OutputMode mode) const;
|
||||
void dump(const std::string& dir, const std::string& fname, OutputMode mode) const;
|
||||
const DeckKeyword& operator[](const Index& index) const;
|
||||
const Index start() const;
|
||||
const Index stop() const;
|
||||
|
||||
private:
|
||||
std::vector<Block> blocks;
|
||||
std::string input_directory;
|
||||
std::unordered_set<std::string> modified_files;
|
||||
DeckTree deck_tree;
|
||||
|
||||
struct DumpContext {
|
||||
std::unordered_map<std::string, std::ofstream> stream_map;
|
||||
std::unordered_map<std::string, std::string> file_map;
|
||||
|
||||
bool has_file(const std::string& fname) const {
|
||||
return this->file_map.count(fname) > 0;
|
||||
}
|
||||
|
||||
std::optional<std::ofstream *> get_stream(const std::string& deck_name) {
|
||||
auto name_iter = this->file_map.find(deck_name);
|
||||
if (name_iter == this->file_map.end())
|
||||
return {};
|
||||
|
||||
return &this->stream_map.at(name_iter->second);
|
||||
}
|
||||
|
||||
|
||||
std::ofstream& open_file(const std::string& deck_name, const fs::path& output_file)
|
||||
{
|
||||
if (this->stream_map.count(output_file.string()) == 0) {
|
||||
this->file_map.insert(std::make_pair( deck_name, output_file.string() ));
|
||||
|
||||
if (!fs::is_directory(output_file.parent_path()))
|
||||
fs::create_directories(output_file.parent_path());
|
||||
|
||||
std::ofstream stream{output_file};
|
||||
if (!stream)
|
||||
throw std::logic_error(fmt::format("Opening {} for writing failed", output_file.string()));
|
||||
this->stream_map.insert(std::make_pair(output_file.string(), std::move(stream)));
|
||||
}
|
||||
return this->stream_map.at(output_file.string());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void dump(std::ostream& os) const;
|
||||
void dump_shared(std::ostream& stream, const std::string& output_dir) const;
|
||||
void dump_inline() const;
|
||||
std::string dump_block(const Block& block, const std::string& dir, const std::optional<std::string>& fname, DumpContext& context) const;
|
||||
void include_block(const std::string& source_file, const std::string& target_file, const std::string& dir, DumpContext& context) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -19,12 +19,15 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckOutput.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
bool DeckView::hasKeyword( const DeckKeyword& keyword ) const {
|
||||
@ -156,7 +159,8 @@ namespace Opm {
|
||||
keywordList( d.keywordList ),
|
||||
defaultUnits( d.defaultUnits ),
|
||||
m_dataFile( d.m_dataFile ),
|
||||
input_path( d.input_path )
|
||||
input_path( d.input_path ),
|
||||
file_tree( d.file_tree )
|
||||
{
|
||||
this->init(this->keywordList.begin(), this->keywordList.end());
|
||||
if (d.activeUnits)
|
||||
@ -169,7 +173,8 @@ namespace Opm {
|
||||
keywordList( std::move(d.keywordList) ),
|
||||
defaultUnits( d.defaultUnits ),
|
||||
m_dataFile( d.m_dataFile ),
|
||||
input_path( d.input_path )
|
||||
input_path( d.input_path ),
|
||||
file_tree( std::move(d.file_tree) )
|
||||
{
|
||||
this->init(this->keywordList.begin(), this->keywordList.end());
|
||||
if (d.activeUnits)
|
||||
@ -280,8 +285,20 @@ namespace Opm {
|
||||
this->input_path = "";
|
||||
else
|
||||
this->input_path = dataFile.substr(0, slash_pos);
|
||||
|
||||
this->file_tree.add_root(dataFile);
|
||||
}
|
||||
|
||||
DeckTree& Deck::tree() {
|
||||
return this->file_tree;
|
||||
}
|
||||
|
||||
DeckTree Deck::tree() const {
|
||||
return this->file_tree;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Deck::iterator Deck::begin() {
|
||||
return this->keywordList.begin();
|
||||
}
|
||||
|
99
src/opm/parser/eclipse/Deck/DeckTree.cpp
Normal file
99
src/opm/parser/eclipse/Deck/DeckTree.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2021 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.
|
||||
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckTree.hpp>
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
DeckTree::TreeNode::TreeNode(const std::string& fn)
|
||||
: fname(fn)
|
||||
{}
|
||||
|
||||
DeckTree::TreeNode::TreeNode(const std::string& pn, const std::string& fn)
|
||||
: fname(fn)
|
||||
, parent(pn)
|
||||
{}
|
||||
|
||||
void DeckTree::TreeNode::add_include(const std::string& include_file) {
|
||||
this->include_files.emplace(include_file);
|
||||
}
|
||||
|
||||
bool DeckTree::TreeNode::includes(const std::string& include_file) const {
|
||||
return this->include_files.count(include_file) > 0;
|
||||
}
|
||||
|
||||
|
||||
std::string DeckTree::add_node(const std::string& fname) {
|
||||
auto abs_path = fs::canonical(fname);
|
||||
this->nodes.emplace( abs_path, TreeNode(abs_path) );
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
|
||||
DeckTree::DeckTree(const std::string& fname)
|
||||
{
|
||||
this->add_root(fname);
|
||||
}
|
||||
|
||||
void DeckTree::add_root(const std::string& fname)
|
||||
{
|
||||
if (this->root_file.has_value())
|
||||
throw std::logic_error("Root already assigned");
|
||||
|
||||
this->root_file = this->add_node(fname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool DeckTree::includes(const std::string& parent_file, const std::string& include_file) const {
|
||||
if (!this->root_file.has_value())
|
||||
return false;
|
||||
|
||||
const auto& parent_node = this->nodes.at(fs::canonical(parent_file));
|
||||
return parent_node.includes(fs::canonical(include_file));
|
||||
}
|
||||
|
||||
const std::string& DeckTree::parent(const std::string& fname) const {
|
||||
const auto& node = this->nodes.at(fs::canonical(fname));
|
||||
const auto& parent_node = this->nodes.at( node.parent.value() );
|
||||
return parent_node.fname;
|
||||
}
|
||||
|
||||
const std::string& DeckTree::root() const {
|
||||
return this->root_file.value();
|
||||
}
|
||||
|
||||
void DeckTree::add_include(std::string parent_file, std::string include_file) {
|
||||
if (!this->root_file.has_value())
|
||||
return;
|
||||
|
||||
parent_file = fs::canonical(parent_file);
|
||||
include_file = fs::canonical(include_file);
|
||||
this->nodes.emplace(include_file, TreeNode(parent_file, include_file));
|
||||
auto& parent_node = this->nodes.at(parent_file);
|
||||
parent_node.add_include( include_file );
|
||||
}
|
||||
|
||||
bool DeckTree::has_include(const std::string& fname) const {
|
||||
const auto& node = this->nodes.at(fname);
|
||||
return !node.include_files.empty();
|
||||
}
|
||||
|
||||
}
|
350
src/opm/parser/eclipse/Deck/FileDeck.cpp
Normal file
350
src/opm/parser/eclipse/Deck/FileDeck.cpp
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
Copyright 2021 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.
|
||||
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckOutput.hpp>
|
||||
#include <opm/parser/eclipse/Deck/FileDeck.hpp>
|
||||
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void INCLUDE(std::ostream& stream , const std::string& fname)
|
||||
{
|
||||
auto include_string = fmt::format(R"(
|
||||
INCLUDE
|
||||
'{}' /
|
||||
)", fname);
|
||||
stream << include_string;
|
||||
}
|
||||
|
||||
void touch_file(const fs::path& file) {
|
||||
if (!fs::exists(file)) {
|
||||
const auto& parent_path = file.parent_path();
|
||||
if (!fs::is_directory(parent_path))
|
||||
fs::create_directories(parent_path);
|
||||
std::ofstream{file};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDeck::Index& FileDeck::Index::operator--() {
|
||||
if (this->keyword_index > 0)
|
||||
this->keyword_index--;
|
||||
else {
|
||||
if (this->file_index == 0)
|
||||
throw std::logic_error("Going beyond start of container");
|
||||
|
||||
this->file_index--;
|
||||
this->keyword_index = this->deck->blocks[this->file_index].size() - 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileDeck::Index FileDeck::Index::operator--(int) {
|
||||
auto current = *this;
|
||||
this->operator--();
|
||||
return current;
|
||||
}
|
||||
|
||||
FileDeck::Index& FileDeck::Index::operator++() {
|
||||
if (this->deck->blocks.empty())
|
||||
throw std::logic_error("Trying to iterate empty container");
|
||||
|
||||
const auto& block = this->deck->blocks[this->file_index];
|
||||
if (this->keyword_index < block.size() - 1)
|
||||
this->keyword_index++;
|
||||
else {
|
||||
this->file_index++;
|
||||
this->keyword_index = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileDeck::Index FileDeck::Index::operator++(int) {
|
||||
auto current = *this;
|
||||
this->operator++();
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
bool FileDeck::Index::operator==(const Index& other) const {
|
||||
return this->file_index == other.file_index &&
|
||||
this->keyword_index == other.keyword_index;
|
||||
}
|
||||
|
||||
bool FileDeck::Index::operator!=(const Index& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FileDeck::FileDeck(const Deck& deck)
|
||||
: input_directory(deck.getInputPath())
|
||||
, deck_tree(deck.tree())
|
||||
{
|
||||
if (deck.empty())
|
||||
return;
|
||||
|
||||
std::size_t deck_index = 0;
|
||||
while (true) {
|
||||
const auto& current_file = deck[deck_index].location().filename;
|
||||
|
||||
FileDeck::Block block(current_file);
|
||||
block.load(deck, deck_index);
|
||||
deck_index += block.size();
|
||||
this->blocks.push_back(std::move(block));
|
||||
|
||||
if (deck_index >= deck.size())
|
||||
break;
|
||||
}
|
||||
|
||||
this->modified_files.insert(this->blocks[0].fname);
|
||||
}
|
||||
|
||||
const DeckKeyword& FileDeck::operator[](const Index& index) const {
|
||||
const auto& file_block = this->blocks.at(index.file_index);
|
||||
return file_block.keywords.at(index.keyword_index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FileDeck::FileDeck::Block::load(const Deck& deck, std::size_t deck_index) {
|
||||
const auto& current_file = deck[deck_index].location().filename;
|
||||
while (true) {
|
||||
this->keywords.push_back(deck[deck_index]);
|
||||
deck_index += 1;
|
||||
|
||||
if (deck_index >= deck.size())
|
||||
break;
|
||||
|
||||
if (deck[deck_index].location().filename != current_file)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t FileDeck::FileDeck::Block::size() const {
|
||||
return this->keywords.size();
|
||||
}
|
||||
|
||||
std::optional<std::size_t> FileDeck::Block::find(const std::string& keyword) const {
|
||||
auto iter = std::find_if(this->keywords.begin(), this->keywords.end(), [&keyword](const DeckKeyword& kw) { return kw.name() == keyword;});
|
||||
if (iter == this->keywords.end())
|
||||
return {};
|
||||
|
||||
return std::distance(this->keywords.begin(), iter);
|
||||
}
|
||||
|
||||
void FileDeck::erase(const FileDeck::Index& index) {
|
||||
auto& block = this->blocks.at(index.file_index);
|
||||
this->modified_files.insert(block.fname);
|
||||
block.erase(index);
|
||||
}
|
||||
|
||||
void FileDeck::erase(const Index& begin, const Index& end) {
|
||||
auto current = end;
|
||||
while (current != begin) {
|
||||
current--;
|
||||
this->erase(current);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileDeck::Block::empty() const {
|
||||
return this->keywords.empty();
|
||||
}
|
||||
|
||||
void FileDeck::Block::erase(const FileDeck::Index& index) {
|
||||
if (index.keyword_index >= this->keywords.size())
|
||||
throw std::logic_error("Invalid keyword index in block");
|
||||
|
||||
this->keywords.erase(this->keywords.begin() + index.keyword_index);
|
||||
}
|
||||
|
||||
void FileDeck::Block::insert(std::size_t keyword_index, const DeckKeyword& keyword) {
|
||||
this->keywords.insert(this->keywords.begin() + keyword_index, keyword);
|
||||
}
|
||||
|
||||
void FileDeck::Block::dump(DeckOutput& out) const {
|
||||
for (const auto& kw : this->keywords) {
|
||||
kw.write( out );
|
||||
out.write_string( out.fmt.keyword_sep );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FileDeck::FileDeck::Block::Block(const std::string& filename)
|
||||
: fname(fs::canonical(filename))
|
||||
{}
|
||||
|
||||
std::optional<FileDeck::Index> FileDeck::find(const std::string& keyword) const {
|
||||
std::size_t file_index = 0;
|
||||
|
||||
while (true) {
|
||||
if (file_index >= this->blocks.size())
|
||||
break;
|
||||
|
||||
const auto& file_block = this->blocks[file_index];
|
||||
const auto& block_index = file_block.find(keyword);
|
||||
if (block_index.has_value())
|
||||
return std::make_optional<FileDeck::Index>(file_index, block_index.value(), this);
|
||||
|
||||
file_index++;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void FileDeck::insert(const Index& index, const DeckKeyword& keyword)
|
||||
{
|
||||
auto& block = this->blocks.at(index.file_index);
|
||||
block.insert(index.keyword_index, keyword);
|
||||
|
||||
this->modified_files.insert(block.fname);
|
||||
}
|
||||
|
||||
|
||||
const FileDeck::Index FileDeck::start() const {
|
||||
return FileDeck::Index{0,0, this};
|
||||
}
|
||||
|
||||
const FileDeck::Index FileDeck::stop() const {
|
||||
return FileDeck::Index{this->blocks.size(), 0 , this};
|
||||
}
|
||||
|
||||
void FileDeck::dump(std::ostream& os) const {
|
||||
DeckOutput out( os , 10 );
|
||||
for (const auto& block : this->blocks)
|
||||
block.dump(out);
|
||||
}
|
||||
|
||||
|
||||
void FileDeck::dump_inline() const {
|
||||
this->dump(std::cout);
|
||||
}
|
||||
|
||||
|
||||
std::string FileDeck::dump_block(const FileDeck::Block& block, const std::string& output_dir, const std::optional<std::string>& data_file , FileDeck::DumpContext& context) const {
|
||||
const auto& deck_name = block.fname;
|
||||
auto old_stream = context.get_stream(deck_name);
|
||||
if (old_stream.has_value()) {
|
||||
DeckOutput out(*old_stream.value(), 10);
|
||||
block.dump( out );
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
fs::path output_file;
|
||||
if (data_file.has_value())
|
||||
output_file = fs::path(output_dir) / data_file.value();
|
||||
else {
|
||||
// Should ideally use fs::relative()
|
||||
auto rel_path = fs::proximate(block.fname, this->input_directory);
|
||||
output_file = output_dir / rel_path;
|
||||
}
|
||||
|
||||
touch_file(output_file);
|
||||
output_file = fs::canonical(output_file);
|
||||
|
||||
auto& stream = context.open_file(deck_name, output_file);
|
||||
DeckOutput out(stream, 10);
|
||||
block.dump( out );
|
||||
return output_file.string();
|
||||
}
|
||||
|
||||
|
||||
void FileDeck::include_block(const std::string& input_file, const std::string& output_file, const std::string& output_dir, FileDeck::DumpContext& context) const {
|
||||
auto current_file = input_file;
|
||||
while (true) {
|
||||
const auto& parent = this->deck_tree.parent(current_file);
|
||||
auto stream = context.get_stream(parent);
|
||||
if (stream.has_value()) {
|
||||
// Should ideally use fs::relative()
|
||||
std::string include_file = fs::proximate(output_file, output_dir);
|
||||
INCLUDE(*stream.value(), include_file);
|
||||
break;
|
||||
}
|
||||
current_file = parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FileDeck::dump(const std::string& output_dir, const std::string& fname, OutputMode mode) const {
|
||||
if (!fs::is_directory(output_dir))
|
||||
fs::create_directories(output_dir);
|
||||
|
||||
auto output_cwd = fs::path(output_dir);
|
||||
if (mode == OutputMode::INLINE) {
|
||||
std::ofstream os(output_cwd / fname);
|
||||
this->dump(os);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (mode == OutputMode::COPY) {
|
||||
DumpContext context;
|
||||
this->dump_block(this->blocks[0], output_dir, fname, context);
|
||||
|
||||
for (std::size_t block_index = 1; block_index < this->blocks.size(); block_index++) {
|
||||
const auto& block = this->blocks[block_index];
|
||||
const auto& include_file = this->dump_block(block, output_dir, {}, context);
|
||||
if (block.fname != this->deck_tree.root())
|
||||
this->include_block(block.fname, include_file, output_dir, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mode == OutputMode::SHARE) {
|
||||
std::ofstream stream{output_cwd / fname};
|
||||
this->dump_shared(stream, output_dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FileDeck::dump_shared(std::ostream& stream, const std::string& output_dir) const {
|
||||
for (std::size_t block_index = 0; block_index < this->blocks.size(); block_index++) {
|
||||
const auto& block = this->blocks[block_index];
|
||||
if (block_index == 0 || this->modified_files.count(block.fname) > 0 || this->deck_tree.has_include(block.fname)) {
|
||||
DeckOutput out(stream, 10);
|
||||
block.dump( out );
|
||||
} else {
|
||||
// Should ideally use fs::relative()
|
||||
std::string include_file = fs::proximate(block.fname, output_dir);
|
||||
if (include_file.find(block.fname) == std::string::npos)
|
||||
INCLUDE(stream, include_file);
|
||||
else
|
||||
INCLUDE(stream, block.fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FileDeck::dump_stdout(const std::string& output_dir, OutputMode mode) const {
|
||||
if (mode == OutputMode::COPY)
|
||||
throw std::logic_error("dump to stdout can not be combined outputmode COPY");
|
||||
|
||||
if (mode == OutputMode::INLINE)
|
||||
this->dump_inline();
|
||||
else if (mode == OutputMode::SHARE)
|
||||
this->dump_shared(std::cout, output_dir);
|
||||
}
|
||||
}
|
@ -1011,8 +1011,11 @@ bool parseState( ParserState& parserState, const Parser& parser ) {
|
||||
std::string includeFileAsString = readValueToken<std::string>(firstRecord.getItem(0));
|
||||
const auto& includeFile = parserState.getIncludeFilePath( includeFileAsString );
|
||||
|
||||
if (includeFile.has_value())
|
||||
if (includeFile.has_value()) {
|
||||
auto& deck_tree = parserState.deck.tree();
|
||||
deck_tree.add_include(filesystem::absolute(parserState.current_path()), includeFile.value() );
|
||||
parserState.loadFile( includeFile.value() );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
418
tests/SPE1CASE2_INCLUDE.DATA
Normal file
418
tests/SPE1CASE2_INCLUDE.DATA
Normal file
@ -0,0 +1,418 @@
|
||||
-- This reservoir simulation deck is made available under the Open Database
|
||||
-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in
|
||||
-- individual contents of the database are licensed under the Database Contents
|
||||
-- License: http://opendatacommons.org/licenses/dbcl/1.0/
|
||||
|
||||
-- Copyright (C) 2015 Statoil
|
||||
|
||||
-- This simulation is based on the data given in
|
||||
-- 'Comparison of Solutions to a Three-Dimensional
|
||||
-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh,
|
||||
-- Journal of Petroleum Technology, January 1981
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
------------------------ SPE1 - CASE 2 ------------------------------------
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
RUNSPEC
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
TITLE
|
||||
SPE1 - CASE 2
|
||||
|
||||
DIMENS
|
||||
10 10 3 /
|
||||
|
||||
-- The number of equilibration regions is inferred from the EQLDIMS
|
||||
-- keyword.
|
||||
EQLDIMS
|
||||
/
|
||||
|
||||
-- The number of PVTW tables is inferred from the TABDIMS keyword;
|
||||
-- when no data is included in the keyword the default values are used.
|
||||
TABDIMS
|
||||
/
|
||||
|
||||
|
||||
OIL
|
||||
GAS
|
||||
WATER
|
||||
DISGAS
|
||||
-- As seen from figure 4 in Odeh, GOR is increasing with time,
|
||||
-- which means that dissolved gas is present
|
||||
|
||||
|
||||
FIELD
|
||||
|
||||
START
|
||||
1 'JAN' 2015 /
|
||||
|
||||
WELLDIMS
|
||||
-- Item 1: maximum number of wells in the model
|
||||
-- - there are two wells in the problem; injector and producer
|
||||
-- Item 2: maximum number of grid blocks connected to any one well
|
||||
-- - must be one as the wells are located at specific grid blocks
|
||||
-- Item 3: maximum number of groups in the model
|
||||
-- - we are dealing with only one 'group'
|
||||
-- Item 4: maximum number of wells in any one group
|
||||
-- - there must be two wells in a group as there are two wells in total
|
||||
2 1 1 2 /
|
||||
|
||||
UNIFOUT
|
||||
|
||||
GRID
|
||||
-- The INIT keyword is used to request an .INIT file. The .INIT file
|
||||
-- is written before the simulation actually starts, and contains grid
|
||||
-- properties and saturation tables as inferred from the input
|
||||
-- deck. There are no other keywords which can be used to configure
|
||||
-- exactly what is written to the .INIT file.
|
||||
INIT
|
||||
|
||||
-- -------------------------------------------------------------------------
|
||||
INCLUDE
|
||||
'include/grid.grdecl' /
|
||||
|
||||
TOPS
|
||||
-- The depth of the top of each grid block
|
||||
100*8325 /
|
||||
|
||||
INCLUDE
|
||||
'include/props.grdecl' /
|
||||
|
||||
|
||||
PROPS
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
PVTW
|
||||
-- Item 1: pressure reference (psia)
|
||||
-- Item 2: water FVF (rb per bbl or rb per stb)
|
||||
-- Item 3: water compressibility (psi^{-1})
|
||||
-- Item 4: water viscosity (cp)
|
||||
-- Item 5: water 'viscosibility' (psi^{-1})
|
||||
|
||||
-- Using values from Norne:
|
||||
-- In METRIC units:
|
||||
-- 277.0 1.038 4.67E-5 0.318 0.0 /
|
||||
-- In FIELD units:
|
||||
4017.55 1.038 3.22E-6 0.318 0.0 /
|
||||
|
||||
ROCK
|
||||
-- Item 1: reference pressure (psia)
|
||||
-- Item 2: rock compressibility (psi^{-1})
|
||||
|
||||
-- Using values from table 1 in Odeh:
|
||||
14.7 3E-6 /
|
||||
|
||||
SWOF
|
||||
-- Column 1: water saturation
|
||||
-- - this has been set to (almost) equally spaced values from 0.12 to 1
|
||||
-- Column 2: water relative permeability
|
||||
-- - generated from the Corey-type approx. formula
|
||||
-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12
|
||||
-- Column 3: oil relative permeability when only oil and water are present
|
||||
-- - we will use the same values as in column 3 in SGOF.
|
||||
-- This is not really correct, but since only the first
|
||||
-- two values are of importance, this does not really matter
|
||||
-- Column 4: corresponding water-oil capillary pressure (psi)
|
||||
|
||||
0.12 0 1 0
|
||||
0.18 4.64876033057851E-008 1 0
|
||||
0.24 0.000000186 0.997 0
|
||||
0.3 4.18388429752066E-007 0.98 0
|
||||
0.36 7.43801652892562E-007 0.7 0
|
||||
0.42 1.16219008264463E-006 0.35 0
|
||||
0.48 1.67355371900826E-006 0.2 0
|
||||
0.54 2.27789256198347E-006 0.09 0
|
||||
0.6 2.97520661157025E-006 0.021 0
|
||||
0.66 3.7654958677686E-006 0.01 0
|
||||
0.72 4.64876033057851E-006 0.001 0
|
||||
0.78 0.000005625 0.0001 0
|
||||
0.84 6.69421487603306E-006 0 0
|
||||
0.91 8.05914256198347E-006 0 0
|
||||
1 0.00001 0 0 /
|
||||
|
||||
|
||||
SGOF
|
||||
-- Column 1: gas saturation
|
||||
-- Column 2: gas relative permeability
|
||||
-- Column 3: oil relative permeability when oil, gas and connate water are present
|
||||
-- Column 4: oil-gas capillary pressure (psi)
|
||||
-- - stated to be zero in Odeh's paper
|
||||
|
||||
-- Values in column 1-3 are taken from table 3 in Odeh's paper:
|
||||
0 0 1 0
|
||||
0.001 0 1 0
|
||||
0.02 0 0.997 0
|
||||
0.05 0.005 0.980 0
|
||||
0.12 0.025 0.700 0
|
||||
0.2 0.075 0.350 0
|
||||
0.25 0.125 0.200 0
|
||||
0.3 0.190 0.090 0
|
||||
0.4 0.410 0.021 0
|
||||
0.45 0.60 0.010 0
|
||||
0.5 0.72 0.001 0
|
||||
0.6 0.87 0.0001 0
|
||||
0.7 0.94 0.000 0
|
||||
0.85 0.98 0.000 0
|
||||
0.88 0.984 0.000 0 /
|
||||
--1.00 1.0 0.000 0 /
|
||||
-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF
|
||||
-- must not be greater than 1, but Eclipse still runs
|
||||
-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88
|
||||
-- The corresponding krg value was estimated by assuming linear rel. between
|
||||
-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given)
|
||||
|
||||
DENSITY
|
||||
-- Density (lb per ft³) at surface cond. of
|
||||
-- oil, water and gas, respectively (in that order)
|
||||
|
||||
-- Using values from Norne:
|
||||
-- In METRIC units:
|
||||
-- 859.5 1033.0 0.854 /
|
||||
-- In FIELD units:
|
||||
53.66 64.49 0.0533 /
|
||||
|
||||
PVDG
|
||||
-- Column 1: gas phase pressure (psia)
|
||||
-- Column 2: gas formation volume factor (rb per Mscf)
|
||||
-- - in Odeh's paper the units are said to be given in rb per bbl,
|
||||
-- but this is assumed to be a mistake: FVF-values in Odeh's paper
|
||||
-- are given in rb per scf, not rb per bbl. This will be in
|
||||
-- agreement with conventions
|
||||
-- Column 3: gas viscosity (cP)
|
||||
|
||||
-- Using values from lower right table in Odeh's table 2:
|
||||
14.700 166.666 0.008000
|
||||
264.70 12.0930 0.009600
|
||||
514.70 6.27400 0.011200
|
||||
1014.7 3.19700 0.014000
|
||||
2014.7 1.61400 0.018900
|
||||
2514.7 1.29400 0.020800
|
||||
3014.7 1.08000 0.022800
|
||||
4014.7 0.81100 0.026800
|
||||
5014.7 0.64900 0.030900
|
||||
9014.7 0.38600 0.047000 /
|
||||
|
||||
PVTO
|
||||
-- Column 1: dissolved gas-oil ratio (Mscf per stb)
|
||||
-- Column 2: bubble point pressure (psia)
|
||||
-- Column 3: oil FVF for saturated oil (rb per stb)
|
||||
-- Column 4: oil viscosity for saturated oil (cP)
|
||||
|
||||
-- Use values from top left table in Odeh's table 2:
|
||||
0.0010 14.7 1.0620 1.0400 /
|
||||
0.0905 264.7 1.1500 0.9750 /
|
||||
0.1800 514.7 1.2070 0.9100 /
|
||||
0.3710 1014.7 1.2950 0.8300 /
|
||||
0.6360 2014.7 1.4350 0.6950 /
|
||||
0.7750 2514.7 1.5000 0.6410 /
|
||||
0.9300 3014.7 1.5650 0.5940 /
|
||||
1.2700 4014.7 1.6950 0.5100
|
||||
9014.7 1.5790 0.7400 /
|
||||
1.6180 5014.7 1.8270 0.4490
|
||||
9014.7 1.7370 0.6310 /
|
||||
-- It is required to enter data for undersaturated oil for the highest GOR
|
||||
-- (i.e. the last row) in the PVTO table.
|
||||
-- In order to fulfill this requirement, values for oil FVF and viscosity
|
||||
-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated:
|
||||
-- It has been assumed that there is a linear relation between the GOR
|
||||
-- and the FVF when keeping the pressure constant at 9014.7psia.
|
||||
-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984
|
||||
-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil,
|
||||
-- so it is possible to use the assumption described above.
|
||||
-- An equivalent approximation for the viscosity has been used.
|
||||
/
|
||||
|
||||
SOLUTION
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
EQUIL
|
||||
-- Item 1: datum depth (ft)
|
||||
-- Item 2: pressure at datum depth (psia)
|
||||
-- - Odeh's table 1 says that initial reservoir pressure is
|
||||
-- 4800 psi at 8400ft, which explains choice of item 1 and 2
|
||||
-- Item 3: depth of water-oil contact (ft)
|
||||
-- - chosen to be directly under the reservoir
|
||||
-- Item 4: oil-water capillary pressure at the water oil contact (psi)
|
||||
-- - given to be 0 in Odeh's paper
|
||||
-- Item 5: depth of gas-oil contact (ft)
|
||||
-- - chosen to be directly above the reservoir
|
||||
-- Item 6: gas-oil capillary pressure at gas-oil contact (psi)
|
||||
-- - given to be 0 in Odeh's paper
|
||||
-- Item 7: RSVD-table
|
||||
-- Item 8: RVVD-table
|
||||
-- Item 9: Set to 0 as this is the only value supported by OPM
|
||||
|
||||
-- Item #: 1 2 3 4 5 6 7 8 9
|
||||
8400 4800 8450 0 8300 0 1 0 0 /
|
||||
|
||||
RSVD
|
||||
-- Dissolved GOR is initially constant with depth through the reservoir.
|
||||
-- The reason is that the initial reservoir pressure given is higher
|
||||
---than the bubble point presssure of 4014.7psia, meaning that there is no
|
||||
-- free gas initially present.
|
||||
8300 1.270
|
||||
8450 1.270 /
|
||||
|
||||
SUMMARY
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
-- 1a) Oil rate vs time
|
||||
FOPR
|
||||
-- Field Oil Production Rate
|
||||
|
||||
-- 1b) GOR vs time
|
||||
WGOR
|
||||
-- Well Gas-Oil Ratio
|
||||
'PROD'
|
||||
/
|
||||
-- Using FGOR instead of WGOR:PROD results in the same graph
|
||||
FGOR
|
||||
|
||||
-- 2a) Pressures of the cell where the injector and producer are located
|
||||
BPR
|
||||
1 1 1 /
|
||||
10 10 3 /
|
||||
/
|
||||
|
||||
-- 2b) Gas saturation at grid points given in Odeh's paper
|
||||
BGSAT
|
||||
1 1 1 /
|
||||
1 1 2 /
|
||||
1 1 3 /
|
||||
10 1 1 /
|
||||
10 1 2 /
|
||||
10 1 3 /
|
||||
10 10 1 /
|
||||
10 10 2 /
|
||||
10 10 3 /
|
||||
/
|
||||
|
||||
-- In order to compare Eclipse with Flow:
|
||||
WBHP
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WGIR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WGIT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WGPR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WGPT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WOIR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WOIT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WOPR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WOPT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WWIR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WWIT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WWPR
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
WWPT
|
||||
'INJ'
|
||||
'PROD'
|
||||
/
|
||||
SCHEDULE
|
||||
-- -------------------------------------------------------------------------
|
||||
RPTSCHED
|
||||
'PRES' 'SGAS' 'RS' 'WELLS' /
|
||||
|
||||
RPTRST
|
||||
'BASIC=1' /
|
||||
|
||||
|
||||
-- If no resolution (i.e. case 1), the two following lines must be added:
|
||||
--DRSDT
|
||||
-- 0 /
|
||||
-- Since this is Case 2, the two lines above have been commented out.
|
||||
-- if DRSDT is set to 0, GOR cannot rise and free gas does not
|
||||
-- dissolve in undersaturated oil -> constant bubble point pressure
|
||||
|
||||
WELSPECS
|
||||
-- Item #: 1 2 3 4 5 6
|
||||
'PROD' 'G1' 10 10 8400 'OIL' /
|
||||
'INJ' 'G1' 1 1 8335 'GAS' /
|
||||
/
|
||||
-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2
|
||||
-- Note that the depth at the midpoint of the well grid blocks
|
||||
-- has been used as reference depth for bottom hole pressure in item 5
|
||||
|
||||
COMPDAT
|
||||
-- Item #: 1 2 3 4 5 6 7 8 9
|
||||
'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
|
||||
'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 /
|
||||
/
|
||||
-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2
|
||||
-- Item 9 is the well bore internal diameter,
|
||||
-- the radius is given to be 0.25ft in Odeh's paper
|
||||
|
||||
|
||||
WCONPROD
|
||||
-- Item #:1 2 3 4 5 9
|
||||
'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
|
||||
/
|
||||
-- It is stated in Odeh's paper that the maximum oil prod. rate
|
||||
-- is 20 000stb per day which explains the choice of value in item 4.
|
||||
-- The items > 4 are defaulted with the exception of item 9,
|
||||
-- the BHP lower limit, which is given to be 1000psia in Odeh's paper
|
||||
|
||||
WCONINJE
|
||||
-- Item #:1 2 3 4 5 6 7
|
||||
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
|
||||
/
|
||||
-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day
|
||||
-- BHP upper limit (item 7) should not be exceeding the highest
|
||||
-- pressure in the PVT table=9014.7psia (default is 100 000psia)
|
||||
|
||||
TSTEP
|
||||
--Advance the simulater once a month for TEN years:
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 /
|
||||
|
||||
DATES
|
||||
31 'DEC' 2019 /
|
||||
/
|
||||
|
||||
TSTEP
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31
|
||||
31 28 31 30 31 30 31 31 30 31 30 31 /
|
||||
|
||||
--Advance the simulator once a year for TEN years:
|
||||
--10*365 /
|
||||
|
||||
END
|
2
tests/include/dx.grdecl
Normal file
2
tests/include/dx.grdecl
Normal file
@ -0,0 +1,2 @@
|
||||
DX
|
||||
300*1000 /
|
2
tests/include/dy.grdecl
Normal file
2
tests/include/dy.grdecl
Normal file
@ -0,0 +1,2 @@
|
||||
DY
|
||||
300*1000 /
|
2
tests/include/dz.grdecl
Normal file
2
tests/include/dz.grdecl
Normal file
@ -0,0 +1,2 @@
|
||||
DZ
|
||||
100*20 100*30 100*50 /
|
8
tests/include/grid.grdecl
Normal file
8
tests/include/grid.grdecl
Normal file
@ -0,0 +1,8 @@
|
||||
INCLUDE
|
||||
'include/dx.grdecl' /
|
||||
|
||||
INCLUDE
|
||||
'include/dy.grdecl' /
|
||||
|
||||
INCLUDE
|
||||
'include/dz.grdecl' /
|
3
tests/include/poro.grdecl
Normal file
3
tests/include/poro.grdecl
Normal file
@ -0,0 +1,3 @@
|
||||
PORO
|
||||
300*0.3 /
|
||||
|
12
tests/include/props.grdecl
Normal file
12
tests/include/props.grdecl
Normal file
@ -0,0 +1,12 @@
|
||||
PERMX
|
||||
100*500 100*50 100*200 /
|
||||
|
||||
PERMY
|
||||
100*500 100*50 100*200 /
|
||||
|
||||
PERMZ
|
||||
100*500 100*50 100*200 /
|
||||
|
||||
INCLUDE
|
||||
'include/poro.grdecl' /
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckTree.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckOutput.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
|
||||
@ -149,13 +150,6 @@ BOOST_AUTO_TEST_CASE(set_and_get_data_file) {
|
||||
BOOST_CHECK_EQUAL("", deck.getInputPath());
|
||||
BOOST_CHECK_EQUAL("some/path", deck.makeDeckPath("some/path"));
|
||||
BOOST_CHECK_EQUAL("/abs/path", deck.makeDeckPath("/abs/path"));
|
||||
|
||||
std::string file("/path/to/file.DATA");
|
||||
deck.setDataFile( file );
|
||||
BOOST_CHECK_EQUAL(file, deck.getDataFile());
|
||||
BOOST_CHECK_EQUAL("/path/to", deck.getInputPath());
|
||||
BOOST_CHECK_EQUAL("/path/to/some/path", deck.makeDeckPath("some/path"));
|
||||
BOOST_CHECK_EQUAL("/abs/path", deck.makeDeckPath("/abs/path"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DummyDefaultsString) {
|
||||
@ -686,3 +680,5 @@ BOOST_AUTO_TEST_CASE(STRING_TO_BOOL) {
|
||||
BOOST_CHECK_THROW(DeckItem::to_bool("YE"), std::invalid_argument);
|
||||
BOOST_CHECK_THROW(DeckItem::to_bool("YE"), std::invalid_argument);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,11 +23,16 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp>
|
||||
|
||||
#include "tests/WorkArea.cpp"
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
const std::string& deckStr = R"(
|
||||
@ -258,12 +263,25 @@ GRIDFILE
|
||||
BOOST_CHECK( !ioConfig.getWriteEGRIDFile() );
|
||||
}
|
||||
|
||||
void touch_file(const fs::path& file) {
|
||||
if (!fs::exists(file)) {
|
||||
if (file.has_parent_path()) {
|
||||
const auto& parent_path = file.parent_path();
|
||||
if (!fs::is_directory(parent_path))
|
||||
fs::create_directories(parent_path);
|
||||
}
|
||||
std::ofstream os{file};
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(OutputPaths) {
|
||||
WorkArea wa;
|
||||
|
||||
IOConfig config1( "" );
|
||||
BOOST_CHECK_EQUAL("", config1.getBaseName() );
|
||||
|
||||
Deck deck2;
|
||||
touch_file("testString.DATA");
|
||||
deck2.setDataFile( "testString.DATA" );
|
||||
IOConfig config2( deck2 );
|
||||
std::string output_dir2 = ".";
|
||||
@ -273,7 +291,8 @@ BOOST_AUTO_TEST_CASE(OutputPaths) {
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
Deck deck3;
|
||||
deck3.setDataFile( "/path/to/testString.DATA" );
|
||||
touch_file("path/to/testString.DATA");
|
||||
deck3.setDataFile( "path/to/testString.DATA" );
|
||||
IOConfig config3( deck3 );
|
||||
std::string output_dir3 = "/path/to";
|
||||
config3.setOutputDir( output_dir3 );
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/common/utility/TimeService.hpp>
|
||||
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
||||
#include <opm/common/utility/FileSystem.hpp>
|
||||
#include <opm/parser/eclipse/Python/Python.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
@ -33,7 +36,9 @@
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Action/State.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Deck/DeckValue.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/Deck/FileDeck.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/io/eclipse/rst/state.hpp>
|
||||
@ -44,6 +49,9 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = Opm::filesystem;
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
@ -204,3 +212,111 @@ BOOST_AUTO_TEST_CASE(LoadActionRestartSim) {
|
||||
Action::State action_state;
|
||||
action_state.load_rst(rst_actions, rst_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TestFileDeck)
|
||||
{
|
||||
Parser parser;
|
||||
auto python = std::make_shared<Python>();
|
||||
auto deck = parser.parseFile("UDQ_WCONPROD.DATA");
|
||||
FileDeck fd(deck);
|
||||
|
||||
{
|
||||
auto index = fd.start();
|
||||
for (const auto& kw : deck) {
|
||||
BOOST_CHECK(kw == fd[index]);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto index = fd.stop();
|
||||
for (std::size_t deck_index = deck.size(); deck_index > 0; deck_index--)
|
||||
BOOST_CHECK(deck[deck_index - 1] == fd[--index]);
|
||||
}
|
||||
|
||||
|
||||
const auto& index1 = fd.find("RADIAL");
|
||||
BOOST_CHECK( !index1.has_value() );
|
||||
|
||||
|
||||
const auto& index2 = fd.find("DIMENS");
|
||||
BOOST_CHECK( index2.has_value());
|
||||
|
||||
BOOST_CHECK_EQUAL( index2.value().file_index , 0);
|
||||
BOOST_CHECK_EQUAL( index2.value().keyword_index, 3);
|
||||
|
||||
const auto& index3 = fd.find("COORD");
|
||||
BOOST_CHECK_EQUAL( index3.value().file_index , 1);
|
||||
BOOST_CHECK_EQUAL( index3.value().keyword_index, 3);
|
||||
|
||||
const auto& index4 = fd.find("SGOF");
|
||||
BOOST_CHECK_EQUAL( index4.value().file_index , 2);
|
||||
BOOST_CHECK_EQUAL( index4.value().keyword_index, 2);
|
||||
|
||||
fd.erase(index4.value());
|
||||
const auto& index5 = fd.find("SGOF");
|
||||
BOOST_CHECK( !index5.has_value() );
|
||||
|
||||
|
||||
const auto& index6 = fd.find("SWOF");
|
||||
const auto& index7 = fd.find("SOLUTION");
|
||||
|
||||
fd.erase(index6.value(), index7.value());
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(RestartTest)
|
||||
{
|
||||
Parser parser;
|
||||
auto python = std::make_shared<Python>();
|
||||
auto deck = parser.parseFile("UDQ_WCONPROD.DATA");
|
||||
FileDeck fd(deck);
|
||||
|
||||
const auto solution = fd.find("SOLUTION");
|
||||
const auto schedule = fd.find("SCHEDULE");
|
||||
|
||||
auto index = solution.value();
|
||||
while (true) {
|
||||
const auto& keyword = fd[++index];
|
||||
if (keyword.name() == "EQUIL")
|
||||
fd.erase(index);
|
||||
|
||||
if (index == schedule)
|
||||
break;
|
||||
}
|
||||
|
||||
UnitSystem units;
|
||||
std::vector<DeckValue> values{ DeckValue{"RESTART_BASE"}, DeckValue{100} };
|
||||
DeckKeyword restart(parser.getKeyword("RESTART"), std::vector<std::vector<DeckValue>>{ values }, units, units);
|
||||
|
||||
index = solution.value();
|
||||
fd.insert(++index, restart);
|
||||
|
||||
BOOST_CHECK( fd[index] == restart );
|
||||
|
||||
|
||||
// time_point tp = asTimePoint( TimeStampUTC(2018, 1, 11) );
|
||||
|
||||
// auto find_date = [tp](const DeckKeyword& keyword)
|
||||
// {
|
||||
// if (keyword.name() == "DATES") {
|
||||
// const auto& record = keyword[0];
|
||||
|
||||
|
||||
// const auto &dayItem = record.getItem(0);
|
||||
// const auto &monthItem = record.getItem(1);
|
||||
// const auto &yearItem = record.getItem(2);
|
||||
|
||||
// // Accept lower- and mixed-case month names.
|
||||
// std::string monthname = uppercase(monthItem.get<std::string>(0));
|
||||
// auto date = asTimePoint( TimeStampUTC( yearItem.get<int>(0), TimeService::eclipseMonthIndices().at(monthname), dayItem.get<int>(0)));
|
||||
|
||||
// return date == tp;
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
22
tests/rst_test_driver.sh
Executable file
22
tests/rst_test_driver.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
rst_deck=$1
|
||||
opmhash=$2
|
||||
deck=$3
|
||||
|
||||
pushd $(mktemp -d)
|
||||
|
||||
${rst_deck} ${deck} HISTORY:0 output/CASE_COPY.DATA -m copy -s
|
||||
${opmhash} ${deck} output/CASE_COPY.DATA -S
|
||||
|
||||
${rst_deck} ${deck} HISTORY:0 -m share -s > CASE_STDOUT.DATA
|
||||
${opmhash} ${deck} CASE_STDOUT.DATA -S
|
||||
|
||||
pushd output
|
||||
chmod -R a-w *
|
||||
popd
|
||||
|
||||
${rst_deck} output/CASE_COPY.DATA HISTORY:0 output/CASE_SHARE.DATA -m copy -s
|
||||
${opmhash} ${deck} output/CASE_SHARE.DATA -S
|
||||
popd
|
Loading…
Reference in New Issue
Block a user