Merge pull request #2817 from joakim-hove/schedule-fp

Schedule fp
This commit is contained in:
Joakim Hove 2021-11-12 07:52:58 +01:00 committed by GitHub
commit fb1dcd0203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 355 additions and 297 deletions

View File

@ -137,7 +137,7 @@ namespace Opm {
private:
void initIOConfigPostSchedule(const Deck& deck);
void initTransMult();
void applyMULTXYZ();
void initFaults(const Deck& deck);
void initPara(const Deck& deck);

View File

@ -29,6 +29,7 @@ namespace Opm {
class EclipseGrid;
class Deck;
class DeckKeyword;
class FieldProps;
class Phases;
class TableManager;
@ -47,6 +48,9 @@ public:
virtual std::vector<int> actnum() const;
virtual std::vector<double> porv(bool global = false) const;
void apply_schedule_keywords(const std::vector<DeckKeyword>& keywords);
/*
The number of cells in the fields managed by this FieldPropsManager.
Initially this will correspond to the number of active cells in the grid

View File

@ -614,6 +614,7 @@ namespace Opm
void handleGCONSALE (HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGCONSUMP (HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGEFAC (HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGEOKeyword(HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGLIFTOPT (HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGPMAINT (HandlerContext&, const ParseContext&, ErrorGuard&);
void handleGRUPNET (HandlerContext&, const ParseContext&, ErrorGuard&);

View File

@ -235,17 +235,6 @@ class KeywordLocation;
*/
const static std::string UNIT_SYSTEM_MISMATCH;
/*
Some property modfiers can be modified in the Schedule
section; this effectively means that Eclipse supports time
dependent geology. This is marked as an exocit special
feature in Eclipse, and not supported at all in the
EclipseState object of opm-parser. If these modifiers are
encountered in the Schedule section the behavior is
regulated by this setting.
*/
const static std::string UNSUPPORTED_SCHEDULE_GEO_MODIFIER;
/*
If the third item in the THPRES keyword is defaulted the

View File

@ -149,7 +149,7 @@ AquiferConfig load_aquifers(const Deck& deck, const TableManager& tables, NNC& i
m_title.pop_back();
}
this->initTransMult();
this->applyMULTXYZ();
this->initFaults(deck);
const auto& init_config = this->getInitConfig();
@ -307,16 +307,18 @@ AquiferConfig load_aquifers(const Deck& deck, const TableManager& tables, NNC& i
this->aquifer_config.loadFromRestart(aquifers, this->m_tables);
}
void EclipseState::initTransMult() {
void EclipseState::applyMULTXYZ() {
const auto& fp = this->field_props;
if (fp.has_double("MULTX")) this->m_transMult.applyMULT(fp.get_global_double("MULTX") , FaceDir::XPlus);
if (fp.has_double("MULTX-")) this->m_transMult.applyMULT(fp.get_global_double("MULTX-"), FaceDir::XMinus);
if (fp.has_double("MULTY")) this->m_transMult.applyMULT(fp.get_global_double("MULTY") , FaceDir::YPlus);
if (fp.has_double("MULTY-")) this->m_transMult.applyMULT(fp.get_global_double("MULTY-"), FaceDir::YMinus);
if (fp.has_double("MULTZ")) this->m_transMult.applyMULT(fp.get_global_double("MULTZ") , FaceDir::ZPlus);
if (fp.has_double("MULTZ-")) this->m_transMult.applyMULT(fp.get_global_double("MULTZ-"), FaceDir::ZMinus);
static const std::vector<std::pair<std::string, FaceDir::DirEnum>> multipliers = {{"MULTX" , FaceDir::XPlus},
{"MULTX-", FaceDir::XMinus},
{"MULTY" , FaceDir::YPlus},
{"MULTY-", FaceDir::YMinus},
{"MULTZ" , FaceDir::ZPlus},
{"MULTZ-", FaceDir::ZMinus}};
for (const auto& [field, face] : multipliers) {
if (fp.has_double(field))
this->m_transMult.applyMULT(fp.get_global_double(field), face);
}
}
void EclipseState::initFaults(const Deck& deck) {
@ -361,44 +363,38 @@ AquiferConfig load_aquifers(const Deck& deck, const TableManager& tables, NNC& i
}
}
/*
The apply_schedule_keywords can apply a small set of keywords from the
Schdule section for transmissibility scaling; the currently supported
keywords are: {MULTFLT, MULTX, MULTX-, MULTY, MULTY-, MULTZ, MULTZ-}.
Observe that the multiplier scalars which are in the schedule section are
applied by multiplying with the transmissibility which has already been
calculated, i.e. to increase the permeability you must use a multiplier
greater than one.
*/
void EclipseState::apply_schedule_keywords(const std::vector<DeckKeyword>& keywords) {
using namespace ParserKeywords;
static const std::unordered_set<std::string> multipliers = {"MULTFLT", "MULTX", "MULTX-", "MULTY", "MULTY-", "MULTZ", "MULTZ-"};
for (const auto& keyword : keywords) {
if (keyword.isKeyword<MULTFLT>()) {
for (const auto& record : keyword) {
const std::string& faultName = record.getItem<MULTFLT::fault>().get< std::string >(0);
auto& fault = m_faults.getFault( faultName );
double tmpMultFlt = record.getItem<MULTFLT::factor>().get< double >(0);
double oldMultFlt = fault.getTransMult( );
double newMultFlt = oldMultFlt * tmpMultFlt;
auto multflt = record.getItem<MULTFLT::factor>().get< double >(0);
/*
This extremely contrived way of doing it is because of difference in
behavior and section awareness between the Fault object and the
Transmult object:
1. MULTFLT keywords found in the SCHEDULE section should apply the
transmissibility modifiers cumulatively - i.e. the current
transmissibility across the fault should be *multiplied* with the
newly entered MULTFLT value, and the resulting transmissibility
multplier for this fault should be the product of the newly
entered value and the current value.
2. The TransMult::applyMULTFLT() implementation will *multiply* the
transmissibility across a face with the value in the fault
object. Hence the current value has already been multiplied in;
we therefor first *set* the MULTFLT value to the new value, then
apply it to the TransMult object and then eventually update the
MULTFLT value in the fault instance.
*/
fault.setTransMult( tmpMultFlt );
fault.setTransMult( multflt );
m_transMult.applyMULTFLT( fault );
fault.setTransMult( newMultFlt );
}
}
if (multipliers.count(keyword.name()) == 1)
OpmLog::info(fmt::format("Apply transmissibility multiplier: {}", keyword.name()));
}
this->field_props.apply_schedule_keywords(keywords);
this->applyMULTXYZ();
}

View File

@ -791,7 +791,7 @@ void FieldProps::handle_double_keyword(Section section, const Fieldprops::keywor
const auto& deck_data = keyword.getSIDoubleData();
const auto& deck_status = keyword.getValueStatus();
if (section == Section::EDIT && kw_info.multiplier)
if ((section == Section::EDIT || section == Section::SCHEDULE) && kw_info.multiplier)
multiply_deck(kw_info, keyword, field_data, deck_data, deck_status, box);
else
assign_deck(kw_info, keyword, field_data, deck_data, deck_status, box);
@ -1272,21 +1272,31 @@ void FieldProps::scanSOLUTIONSection(const SOLUTIONSection& solution_section) {
}
}
void FieldProps::scanSCHEDULESection(const SCHEDULESection& schedule_section) {
void FieldProps::handle_schedule_keywords(const std::vector<DeckKeyword>& keywords) {
Box box(*this->grid_ptr);
for (const auto& keyword : schedule_section) {
// When called in the SCHEDULE section the context is that the scaling factors
// have already been applied.
for (const auto& [kw, _] : Fieldprops::keywords::SCHEDULE::double_keywords) {
(void)_;
if (this->has<double>(kw)) {
auto& field_data = this->init_get<double>(kw);
field_data.default_assign(1.0);
}
}
for (const auto& keyword : keywords) {
const std::string& name = keyword.name();
if (Fieldprops::keywords::SCHEDULE::double_keywords.count(name) == 1) {
this->handle_double_keyword(Section::SCHEDULE, Fieldprops::keywords::SCHEDULE::double_keywords.at(name), keyword, box);
continue;
}
if (Fieldprops::keywords::SCHEDULE::int_keywords.count(name) == 1) {
this->handle_int_keyword(Fieldprops::keywords::SCHEDULE::int_keywords.at(name), keyword, box);
if (Fieldprops::keywords::box_keywords.count(name) == 1) {
handle_box_keyword(keyword, box);
continue;
}
this->handle_keyword(keyword, box);
}
}

View File

@ -267,7 +267,13 @@ static const std::unordered_map<std::string, keyword_info<double>> double_keywor
namespace SCHEDULE {
static const std::unordered_map<std::string, keyword_info<double>> double_keywords = {};
static const std::unordered_map<std::string, keyword_info<double>> double_keywords = {{"MULTX", keyword_info<double>{}.init(1.0).mult(true)},
{"MULTX-", keyword_info<double>{}.init(1.0).mult(true)},
{"MULTY", keyword_info<double>{}.init(1.0).mult(true)},
{"MULTY-", keyword_info<double>{}.init(1.0).mult(true)},
{"MULTZ", keyword_info<double>{}.init(1.0).mult(true).global_kw(true)},
{"MULTZ-", keyword_info<double>{}.init(1.0).mult(true)}};
static const std::unordered_map<std::string, keyword_info<int>> int_keywords = {{"ROCKNUM", keyword_info<int>{}}};
}
@ -485,6 +491,7 @@ public:
return this->double_data.size();
}
void handle_schedule_keywords(const std::vector<DeckKeyword>& keywords);
bool tran_active(const std::string& keyword) const;
void apply_tran(const std::string& keyword, std::vector<double>& data);
std::vector<char> serialize_tran() const;
@ -497,7 +504,6 @@ private:
void scanPROPSSection(const PROPSSection& props_section);
void scanREGIONSSection(const REGIONSSection& regions_section);
void scanSOLUTIONSection(const SOLUTIONSection& solution_section);
void scanSCHEDULESection(const SCHEDULESection& schedule_section);
double getSIValue(const std::string& keyword, double raw_value) const;
double getSIValue(ScalarOperation op, const std::string& keyword, double raw_value) const;
template <typename T>

View File

@ -18,6 +18,7 @@
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
@ -46,6 +47,10 @@ void FieldPropsManager::reset_actnum(const std::vector<int>& actnum) {
this->fp->reset_actnum(actnum);
}
void FieldPropsManager::apply_schedule_keywords(const std::vector<DeckKeyword>& keywords) {
this->fp->handle_schedule_keywords(keywords);
}
template <typename T>
const std::vector<T>& FieldPropsManager::get(const std::string& keyword) const {

View File

@ -726,11 +726,17 @@ namespace {
this->snapshots.back().events().addEvent( ScheduleEvents::GEO_MODIFIER );
}
void Schedule::handleMXUNSUPP(HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
void Schedule::handleGEOKeyword(HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
this->snapshots.back().geo_keywords().push_back(handlerContext.keyword);
this->snapshots.back().events().addEvent( ScheduleEvents::GEO_MODIFIER );
}
void Schedule::handleMXUNSUPP(HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
std::string msg_fmt = fmt::format("Problem with keyword {{keyword}} at report step {}\n"
"In {{file}} line {{line}}\n"
"OPM does not support grid property modifier {} in the Schedule section", handlerContext.currentStep, handlerContext.keyword.name());
parseContext.handleError( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , msg_fmt, handlerContext.keyword.location(), errors );
OpmLog::warning(OpmInputError::format(msg_fmt, handlerContext.keyword.location()));
}
void Schedule::handleNETBALAN(HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
@ -2094,6 +2100,7 @@ namespace {
bool Schedule::handleNormalKeyword(HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
using handler_function = void (Schedule::*)(HandlerContext&, const ParseContext&, ErrorGuard&);
static const std::unordered_map<std::string,handler_function> handler_functions = {
{ "BOX", &Schedule::handleGEOKeyword},
{ "BRANPROP", &Schedule::handleBRANPROP },
{ "COMPDAT" , &Schedule::handleCOMPDAT },
{ "COMPLUMP", &Schedule::handleCOMPLUMP },
@ -2104,6 +2111,7 @@ namespace {
{ "DRSDTR" , &Schedule::handleDRSDTR },
{ "DRVDT" , &Schedule::handleDRVDT },
{ "DRVDTR" , &Schedule::handleDRVDTR },
{ "ENDBOX" , &Schedule::handleGEOKeyword},
{ "EXIT", &Schedule::handleEXIT },
{ "GCONINJE", &Schedule::handleGCONINJE },
{ "GCONPROD", &Schedule::handleGCONPROD },
@ -2127,12 +2135,12 @@ namespace {
{ "MULTSIGV", &Schedule::handleMXUNSUPP },
{ "MULTTHT" , &Schedule::handleMXUNSUPP },
{ "MULTTHT-", &Schedule::handleMXUNSUPP },
{ "MULTX" , &Schedule::handleMXUNSUPP },
{ "MULTX-" , &Schedule::handleMXUNSUPP },
{ "MULTY" , &Schedule::handleMXUNSUPP },
{ "MULTY-" , &Schedule::handleMXUNSUPP },
{ "MULTZ" , &Schedule::handleMXUNSUPP },
{ "MULTZ-" , &Schedule::handleMXUNSUPP },
{ "MULTX" , &Schedule::handleGEOKeyword},
{ "MULTX-" , &Schedule::handleGEOKeyword},
{ "MULTY" , &Schedule::handleGEOKeyword},
{ "MULTY-" , &Schedule::handleGEOKeyword},
{ "MULTZ" , &Schedule::handleGEOKeyword},
{ "MULTZ-" , &Schedule::handleGEOKeyword},
{ "NETBALAN", &Schedule::handleNETBALAN },
{ "NEXTSTEP", &Schedule::handleNEXTSTEP },
{ "NODEPROP", &Schedule::handleNODEPROP },

View File

@ -90,7 +90,6 @@ namespace Opm {
this->addKey(RUNSPEC_NUMGROUPS_TOO_LARGE, InputError::THROW_EXCEPTION);
this->addKey(RUNSPEC_GROUPSIZE_TOO_LARGE, InputError::THROW_EXCEPTION);
addKey(UNSUPPORTED_SCHEDULE_GEO_MODIFIER, InputError::THROW_EXCEPTION);
addKey(UNSUPPORTED_INITIAL_THPRES, InputError::THROW_EXCEPTION);
addKey(UNSUPPORTED_TERMINATE_IF_BHP, InputError::THROW_EXCEPTION);
@ -338,7 +337,6 @@ namespace Opm {
const std::string ParseContext::RUNSPEC_NUMGROUPS_TOO_LARGE = "RUNSPEC_NUMGROUPS_TOO_LARGE";
const std::string ParseContext::RUNSPEC_GROUPSIZE_TOO_LARGE = "RUNSPEC_GROUPSIZE_TOO_LARGE";
const std::string ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER = "UNSUPPORTED_SCHEDULE_GEO_MODIFIER";
const std::string ParseContext::UNSUPPORTED_INITIAL_THPRES = "UNSUPPORTED_INITIAL_THPRES";
const std::string ParseContext::UNSUPPORTED_TERMINATE_IF_BHP = "UNSUPPORTED_TERMINATE_IF_BHP";

View File

@ -2,7 +2,8 @@
"name": "MULT_XYZ",
"sections": [
"GRID",
"EDIT"
"EDIT",
"SCHEDULE"
],
"deck_names": [
"MULTTHT",

View File

@ -33,6 +33,7 @@
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Units/Units.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <opm/parser/eclipse/Deck/DeckSection.hpp>
@ -43,6 +44,7 @@
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/SatfuncPropertyInitializers.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include "src/opm/parser/eclipse/EclipseState/Grid/FieldProps.hpp"
@ -2596,3 +2598,106 @@ COPYREG
BOOST_CHECK( FieldPropsManager::rst_cmp(fp, fp2) );
(void) satnum;
}
BOOST_AUTO_TEST_CASE(SCHEDULE_MULTZ) {
std::string deck_string1 = R"(
GRID
PORO
200*0.15 /
PERMX
200*1 /
REGIONS
FIPNUM
200*1 /
MULTNUM
50*1 50*2 100*3 /
EDIT
MULTZ
200*3 /
SCHEDULE
TSTEP
1 /
BOX
1 10 1 10 1 1 /
MULTZ
100*2 /
ENDBOX
TSTEP
1 /
BOX
1 10 1 10 2 2 /
MULTZ
100*4 /
ENDBOX
TSTEP
10 /
MULTZ
100*2 100*4/
MULTZ
200*10 /
)";
UnitSystem unit_system(UnitSystem::UnitType::UNIT_TYPE_METRIC);
EclipseGrid grid(10,10, 2);
Deck deck = Parser{}.parseString(deck_string1);
TableManager tables;
FieldPropsManager fp(deck, Phases{true, true, true}, grid, tables);
Runspec runspec (deck);
auto python = std::make_shared<Python>();
Schedule sched(deck, grid, fp, runspec, python);
const auto& multz0 = fp.get_double("MULTZ");
for (std::size_t k=0; k < 2; k++) {
for (std::size_t ij=0; ij < 100; ij++)
BOOST_CHECK_EQUAL(multz0[ij + k*100], 3.0);
}
// Observe that the MULTZ multiplier is reset to 1.0 for every timestep
const auto& sched1 = sched[1];
fp.apply_schedule_keywords(sched1.geo_keywords());
const auto& multz1 = fp.get_double("MULTZ");
for (std::size_t ij=0; ij < 100; ij++) {
BOOST_CHECK_EQUAL(multz1[ij] , 2.0);
BOOST_CHECK_EQUAL(multz1[ij + 100], 1.0);
}
const auto& sched2 = sched[2];
fp.apply_schedule_keywords(sched2.geo_keywords());
const auto& multz2 = fp.get_double("MULTZ");
for (std::size_t ij=0; ij < 100; ij++) {
BOOST_CHECK_EQUAL(multz2[ij] , 1.0);
BOOST_CHECK_EQUAL(multz2[ij + 100], 4.0);
}
const auto& sched3 = sched[3];
fp.apply_schedule_keywords(sched3.geo_keywords());
for (std::size_t ij=0; ij < 100; ij++) {
BOOST_CHECK_EQUAL(multz2[ij] , 20.0);
BOOST_CHECK_EQUAL(multz2[ij + 100], 40.0);
}
}

View File

@ -88,7 +88,6 @@ TSTEP -- 3,4
TableManager table ( deck );
FieldPropsManager fp( deck, Phases{true, true, true}, grid, table);
parseContext.update( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , InputError::IGNORE );
{
Runspec runspec ( deck );
Schedule schedule( deck, grid , fp, runspec , parseContext, errors, python);

View File

@ -286,70 +286,6 @@ BOOST_AUTO_TEST_CASE( CheckMissingSizeKeyword) {
}
BOOST_AUTO_TEST_CASE( CheckUnsupportedInSCHEDULE ) {
const char * deckStringUnSupported =
"START\n"
" 10 'JAN' 2000 /\n"
"RUNSPEC\n"
"DIMENS\n"
" 10 10 10 / \n"
"GRID\n"
"DX\n"
"1000*0.25 /\n"
"DY\n"
"1000*0.25 /\n"
"DZ\n"
"1000*0.25 /\n"
"TOPS\n"
"100*0.25 /\n"
"SCHEDULE\n"
"MULTZ\n"
" 1000*0.10 /\n"
"\n";
const char * deckStringSupported =
"START\n"
" 10 'JAN' 2000 /\n"
"RUNSPEC\n"
"DIMENS\n"
" 10 10 10 / \n"
"GRID\n"
"DX\n"
"1000*0.25 /\n"
"DY\n"
"1000*0.25 /\n"
"DZ\n"
"1000*0.25 /\n"
"TOPS\n"
"100*0.25 /\n"
"SCHEDULE\n"
"MULTFLT\n"
" 'F1' 0.10 /\n"
"/\n"
"\n";
ErrorGuard errors;
ParseContext parseContext;
Parser parser(true);
auto deckSupported = parser.parseString( deckStringSupported , parseContext, errors );
auto deckUnSupported = parser.parseString( deckStringUnSupported , parseContext, errors );
EclipseGrid grid( deckSupported );
TableManager table ( deckSupported );
FieldPropsManager fp(deckSupported, Phases{true, true, true}, grid, table);
Runspec runspec(deckSupported);
auto python = std::make_shared<Python>();
parseContext.update( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , InputError::IGNORE );
BOOST_CHECK_NO_THROW( Schedule( deckSupported , grid , fp, runspec, parseContext, errors, python ));
BOOST_CHECK_NO_THROW( Schedule( deckUnSupported, grid , fp, runspec, parseContext, errors, python ));
parseContext.update( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , InputError::THROW_EXCEPTION );
BOOST_CHECK_THROW( Schedule( deckUnSupported , grid , fp, runspec , parseContext , errors, python), OpmInputError );
BOOST_CHECK_NO_THROW( Schedule( deckSupported , grid , fp, runspec , parseContext, errors, python));
}
BOOST_AUTO_TEST_CASE(TestRandomSlash) {
const char * deck1 =