Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write MULTPV to INIT-file #4021

Merged
merged 6 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 58 additions & 38 deletions opm/input/eclipse/EclipseState/Grid/FieldProps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,43 +673,6 @@ bool FieldProps::supported<int>(const std::string& keyword) {
return Fieldprops::keywords::isFipxxx(keyword);
}

void FieldProps::apply_multipliers()
{
static const auto prefix = getMultiplierPrefix();

for(const auto& [mult_keyword, kw_info]: multiplier_kw_infos_)
{
const std::string keyword = mult_keyword.substr(prefix.size());
auto mult_iter = this->double_data.find(mult_keyword);
assert(mult_iter != this->double_data.end());
auto iter = this->double_data.find(keyword);
if (iter == this->double_data.end()) {
iter = this->double_data
.emplace(std::piecewise_construct,
std::forward_as_tuple(keyword),
std::forward_as_tuple(kw_info, this->active_size, kw_info.global ? this->global_size : 0))
.first;
}
using Scalar = typename std::remove_cv_t<std::remove_reference_t<decltype(iter->second.data[0])>>;
std::transform(iter->second.data.begin(), iter->second.data.end(),
mult_iter->second.data.begin(), iter->second.data.begin(),
std::multiplies<Scalar>());

// If data is global, then we also need to set the global_data. I think they should be the same at this stage, though!
if (kw_info.global)
{
assert(mult_iter->second.global_data.has_value() && iter->second.global_data.has_value());
std::transform(iter->second.global_data->begin(),
iter->second.global_data->end(),
mult_iter->second.global_data->begin(),
iter->second.global_data->begin(),
std::multiplies<Scalar>());
}
this->double_data.erase(mult_iter);
}
multiplier_kw_infos_.clear();
}

template <>
Fieldprops::FieldData<double>& FieldProps::init_get(const std::string& keyword_name, const Fieldprops::keywords::keyword_info<double>& kw_info,
bool multiplier_in_edit) {
Expand Down Expand Up @@ -814,6 +777,63 @@ bool FieldProps::has<int>(const std::string& keyword) const {
}


void FieldProps::apply_multipliers()
{
// We need to manually search for PORV in the map here instead of using
// the get method. The latter one will compute PORV from the cell volume
// using MULTPV, NTG, and PORO. Our intend here in the EDIT section is
// is to multiply exsisting MULTPV with new new ones and consistently
// change PORV as well. If PORV has been created before this is as easy
// as multiplying it and MULTPV with the additional MULTPV. In the other
// case we do not create PORV at all but just change MULTPV as PORV will
// be correctly computed from it.
// Hence we need to know whether PORV is already there
const bool hasPorvBefore =
(this->double_data.find(ParserKeywords::PORV::keywordName) ==
this->double_data.end());
static const auto prefix = getMultiplierPrefix();

for(const auto& [mult_keyword, kw_info]: multiplier_kw_infos_)
{
const std::string keyword = mult_keyword.substr(prefix.size());
auto mult_iter = this->double_data.find(mult_keyword);
assert(mult_iter != this->double_data.end());
auto iter = this->double_data.find(keyword);
if (iter == this->double_data.end()) {
iter = this->double_data
.emplace(std::piecewise_construct,
std::forward_as_tuple(keyword),
std::forward_as_tuple(kw_info, this->active_size, kw_info.global ? this->global_size : 0))
.first;
}

std::transform(iter->second.data.begin(), iter->second.data.end(),
mult_iter->second.data.begin(), iter->second.data.begin(),
std::multiplies<>());

// If data is global, then we also need to set the global_data. I think they should be the same at this stage, though!
if (kw_info.global)
{
assert(mult_iter->second.global_data.has_value() && iter->second.global_data.has_value());
std::transform(iter->second.global_data->begin(),
iter->second.global_data->end(),
mult_iter->second.global_data->begin(),
iter->second.global_data->begin(),
std::multiplies<>());
}
// If this is MULTPV we also need to apply the additional multiplier to PORV if that was initialized already.
// Note that the check for PORV is essential as otherwise the value constructed durig init_get will already apply
// current MULTPV.
if (keyword == ParserKeywords::MULTPV::keywordName && !hasPorvBefore) {
auto& porv = this->init_get<double>(ParserKeywords::PORV::keywordName);
auto& porv_data = porv.data;
std::transform(porv_data.begin(), porv_data.end(), mult_iter->second.data.begin(), porv_data.begin(), std::multiplies<>());
}
this->double_data.erase(mult_iter);
}
multiplier_kw_infos_.clear();
}

/*
The ACTNUM and PORV keywords are special cased with quite extensive
postprocessing, and should be access through the special ::porv() and
Expand Down Expand Up @@ -1245,7 +1265,7 @@ void FieldProps::init_porv(Fieldprops::FieldData<double>& porv) {

if (this->has<double>("MULTPV")) {
const auto& multpv = this->get<double>("MULTPV");
std::transform(porv_data.begin(), porv_data.end(), multpv.begin(), porv_data.begin(), std::multiplies<double>());
std::transform(porv_data.begin(), porv_data.end(), multpv.begin(), porv_data.begin(), std::multiplies<>());
}

for (const auto& mregp: this->multregp) {
Expand Down
4 changes: 2 additions & 2 deletions opm/input/eclipse/EclipseState/Grid/FieldProps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ namespace ALIAS {
namespace GRID {
static const std::unordered_map<std::string, keyword_info<double>> double_keywords = {{"DISPERC",keyword_info<double>{}.unit_string("Length")},
{"MINPVV", keyword_info<double>{}.unit_string("ReservoirVolume").global_kw(true)},
{"MULTPV", keyword_info<double>{}.init(1.0)},
{"MULTPV", keyword_info<double>{}.init(1.0).mult(true)},
{"NTG", keyword_info<double>{}.init(1.0)},
{"PORO", keyword_info<double>{}.distribute_top(true)},
{"PERMX", keyword_info<double>{}.unit_string("Permeability").distribute_top(true)},
Expand Down Expand Up @@ -181,7 +181,7 @@ namespace EDIT {
due to the special treatment of the TRAN fields.
*/

static const std::unordered_map<std::string, keyword_info<double>> double_keywords = {{"MULTPV", keyword_info<double>{}.init(1.0)},
static const std::unordered_map<std::string, keyword_info<double>> double_keywords = {{"MULTPV", keyword_info<double>{}.init(1.0).mult(true)},
{"PORV", keyword_info<double>{}.unit_string("ReservoirVolume")},
{"MULTX", keyword_info<double>{}.init(1.0).mult(true)},
{"MULTX-", keyword_info<double>{}.init(1.0).mult(true)},
Expand Down
9 changes: 7 additions & 2 deletions opm/output/eclipse/WriteInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,10 +667,15 @@ void Opm::InitIO::write(const ::Opm::EclipseState& es,
{
const auto writeAll = es.cfg().io().writeAllTransMultipliers();

const auto tranMult = es.getTransMult()
auto multipliers = es.getTransMult()
.convertToSimProps(grid.getNumActive(), writeAll);
if (es.fieldProps().has_double("MULTPV")) {
multipliers.insert("MULTPV", UnitSystem::measure::identity,
es.fieldProps().get_double("MULTPV"),
data::TargetType::INIT);
}

writeSimulatorProperties(grid, tranMult, initFile);
writeSimulatorProperties(grid, multipliers, initFile);
}

writeTableData(es, units, initFile);
Expand Down
202 changes: 202 additions & 0 deletions tests/test_EclipseIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,3 +739,205 @@ BOOST_AUTO_TEST_CASE(MULTXYZInit)
testMultxyz({ '3', '3' , '2'}, true);
testMultxyz({ '3', '3' , '3'});
}


std::pair<std::string,std::vector<float>>
createMULTPVDECK(bool edit)
{
auto deckString = std::string { R"(RUNSPEC

TITLE
1D OIL WATER



DIMENS
100 1 1 /

EQLDIMS
/
TABDIMS
2 1 100 /

OIL
WATER

ENDSCALE
/

METRIC

START
1 'JAN' 2024 /

WELLDIMS
3 3 2 2 /

UNIFIN
UNIFOUT

GRID

INIT

DX
100*1 /
DY
100*10 /
DZ
100*1 /

TOPS
100*2000 /

PORO
100*0.3 /

MULTPV
100*5 / -- overwritten by the next MULTPV keyword

MULTPV
100*10 / -- partly overwritten by the next BOX statements

BOX
69 75 1 1 1 1 /
MULTPV
2*1 5*1.5 /
ENDBOX

BOX
76 85 1 1 1 1 /
MULTPV
5*1.5 5*1 /
ENDBOX

EDIT
)"};
std::vector<float> multpv(68, 10.);
multpv.insert(multpv.end(), 2, 1);
multpv.insert(multpv.end(), 10, 1.5);
multpv.insert(multpv.end(), 5, 1.);
multpv.insert(multpv.end(), 15, 10.);

if (edit) {
deckString += std::string { R"(MULTPV
75*10 25*10 / -- overwritten by the next
MULTPV
75*0.8 25*1 /
)"};
std::transform(multpv.begin(), multpv.begin() + 75, multpv.begin(),
[](float f) { return f*.8; });
}
deckString += std::string { R"(PROPS

SOLUTION
SCHEDULE
)"};
return { deckString, multpv };
}

void checkMULTPV(const std::pair<std::string, std::vector<float>>& deckAndValues)
{
const auto [deckString, expectedMultPV] = deckAndValues;
const auto deck = Parser().parseString(deckString);
auto es = EclipseState( deck );
const auto& eclGrid = es.getInputGrid();
const Schedule schedule(deck, es, std::make_shared<Python>());
const SummaryConfig summary_config( deck, schedule, es.fieldProps(), es.aquifer());
const SummaryState st(TimeService::now());
es.getIOConfig().setBaseName( "MULTPVFOO" );
EclipseIO eclWriter( es, eclGrid , schedule, summary_config);
eclWriter.writeInitial( );

EclIO::EclFile initFile { "MULTPVFOO.INIT" };
BOOST_CHECK_MESSAGE( initFile.hasKey("MULTPV"),
R"(INIT file must have MULTPV array)" );
const auto& multPVValues = initFile.get<float>("MULTPV");
auto expect = expectedMultPV.begin();
BOOST_CHECK(multPVValues.size() == expectedMultPV.size());

for (auto multVal = multPVValues.begin(); multVal != multPVValues.end();
++multVal, ++expect) {
BOOST_CHECK_CLOSE(*multVal, *expect, 1e-8);
}
}

BOOST_AUTO_TEST_CASE(MULTPVInit)
{
checkMULTPV(createMULTPVDECK(false));
checkMULTPV(createMULTPVDECK(true));

}

std::pair<std::string,std::vector<float>>
createMULTPVBOXDECK()
{
auto deckString = std::string { R"(RUNSPEC

TITLE
1D OIL WATER



DIMENS
100 1 1 /

EQLDIMS
/
TABDIMS
2 1 100 /

OIL
WATER

ENDSCALE
/

METRIC

START
1 'JAN' 2024 /

WELLDIMS
3 3 2 2 /

UNIFIN
UNIFOUT

GRID

INIT

DX
100*1 /
DY
100*10 /
DZ
100*1 /

TOPS
100*2000 /

PORO
100*0.3 /

BOX
11 100 1 1 1 1 /
MULTPV
90*1.5 /
ENDBOX

EDIT
PROPS
SOLUTION
SCHEDULE
)"};
std::vector<float> expected(10, 1.);
expected.insert(expected.end(), 90, 1.5);
return { deckString, expected };
}

BOOST_AUTO_TEST_CASE(MULTPVBOXInit)
{
checkMULTPV(createMULTPVBOXDECK());
}