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

Add support for USECUPL keyword #4284

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CMakeLists_files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ if(ENABLE_ECL_INPUT)
opm/input/eclipse/Schedule/Network/Node.cpp
opm/input/eclipse/Schedule/ResCoup/ReservoirCouplingInfo.cpp
opm/input/eclipse/Schedule/ResCoup/ReservoirCouplingKeywordHandlers.cpp
opm/input/eclipse/Schedule/ResCoup/GrupSlav.cpp
opm/input/eclipse/Schedule/ResCoup/MasterGroup.cpp
opm/input/eclipse/Schedule/ResCoup/Slaves.cpp
opm/input/eclipse/Schedule/ResCoup/MasterMinimumTimeStep.cpp
opm/input/eclipse/Schedule/ResCoup/ReadCouplingFile.cpp
opm/input/eclipse/Schedule/ResCoup/WriteCouplingFile.cpp
opm/input/eclipse/Schedule/UDQ/UDQKeywordHandlers.cpp
opm/input/eclipse/Schedule/UDQ/UDQActive.cpp
opm/input/eclipse/Schedule/UDQ/UDQAssign.cpp
Expand Down Expand Up @@ -1299,6 +1305,14 @@ if(ENABLE_ECL_INPUT)
opm/input/eclipse/Schedule/Network/Branch.hpp
opm/input/eclipse/Schedule/Network/ExtNetwork.hpp
opm/input/eclipse/Schedule/Network/Node.hpp
opm/input/eclipse/Schedule/ResCoup/ReservoirCouplingInfo.hpp
opm/input/eclipse/Schedule/ResCoup/ReservoirCouplingKeywordHandlers.hpp
opm/input/eclipse/Schedule/ResCoup/GrupSlav.hpp
opm/input/eclipse/Schedule/ResCoup/MasterGroup.hpp
opm/input/eclipse/Schedule/ResCoup/Slaves.hpp
opm/input/eclipse/Schedule/ResCoup/MasterMinimumTimeStep.hpp
opm/input/eclipse/Schedule/ResCoup/ReadCouplingFile.hpp
opm/input/eclipse/Schedule/ResCoup/WriteCouplingFile.hpp
opm/input/eclipse/Schedule/VFPInjTable.hpp
opm/input/eclipse/Schedule/VFPProdTable.hpp
opm/input/eclipse/Schedule/Well/Connection.hpp
Expand Down
157 changes: 157 additions & 0 deletions opm/input/eclipse/Schedule/ResCoup/GrupSlav.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
Copyright 2024 Equinor ASA.

This file is part of the Open Porous Media project (OPM).

OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/


#include <opm/input/eclipse/Schedule/ResCoup/GrupSlav.hpp>
#include <opm/input/eclipse/Schedule/ResCoup/ReservoirCouplingInfo.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
#include <opm/input/eclipse/Schedule/ScheduleStatic.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include "../HandlerContext.hpp"

#include <fmt/format.h>

#include <limits>
#include <stdexcept>

namespace Opm {
namespace ReservoirCoupling {
GrupSlav GrupSlav::serializationTestObject()
{
return GrupSlav{"MANI-D", "D1-M", FilterFlag::MAST, FilterFlag::MAST, FilterFlag::MAST,
FilterFlag::MAST, FilterFlag::MAST, FilterFlag::MAST, FilterFlag::MAST};
}

bool GrupSlav::operator==(const GrupSlav& rhs) const {
return
this->m_name == rhs.m_name &&
this->m_master_group_name == rhs.m_master_group_name &&
this->m_oil_prod_flag == rhs.m_oil_prod_flag &&
this->m_liquid_prod_flag == rhs.m_liquid_prod_flag &&
this->m_gas_prod_flag == rhs.m_gas_prod_flag &&
this->m_fluid_volume_prod_flag == rhs.m_fluid_volume_prod_flag &&
this->m_oil_inj_flag == rhs.m_oil_inj_flag &&
this->m_water_inj_flag == rhs.m_water_inj_flag &&
this->m_gas_inj_flag == rhs.m_gas_inj_flag;
}

// NOTE: This function is needed by Boost.Test, see test/ReservoirCoupling/GrupSlavTest.cpp
std::ostream& operator<<(std::ostream& os, const GrupSlav::FilterFlag& flag) {
switch (flag) {
case GrupSlav::FilterFlag::MAST:
os << "MAST";
break;
case GrupSlav::FilterFlag::SLAV:
os << "SLAV";
break;
case GrupSlav::FilterFlag::BOTH:
os << "BOTH";
break;
default:
throw std::invalid_argument("Invalid filter flag");
}
return os;
}

} // namespace ReservoirCoupling

void checkValidSlaveGroupName(const std::string& name, HandlerContext& handlerContext)
{
const auto& rescoup = handlerContext.state().rescoup();
if (rescoup.hasGrupSlav(name)) {
std::string msg = fmt::format("GRUPSLAV group {} already defined. Redefining", name);
OpmLog::warning(OpmInputError::format(msg, handlerContext.keyword.location()));
}
const auto& groups = handlerContext.state().groups;
if (!groups.has(name)) {
std::string msg = fmt::format("Group '{}': Not defined. Slave groups should be defined in advance by using GRUPTREE or WELSPECS before referenced in GRUPSLAV.", name);
throw OpmInputError(msg, handlerContext.keyword.location());
}
}

ReservoirCoupling::GrupSlav::FilterFlag getFilterFlag(const DeckItem& keyword, HandlerContext& handlerContext)
{
try {
return ReservoirCoupling::GrupSlav::filterFlagFromString(keyword.getTrimmedString(0));
} catch (const std::invalid_argument& e) {
std::string msg = fmt::format("Invalid filter flag: {}", keyword.getTrimmedString(0));
throw OpmInputError(msg, handlerContext.keyword.location());
}
}

void handleGRUPSLAV(HandlerContext& handlerContext)
{
const auto& schedule_state = handlerContext.state();
auto rescoup = schedule_state.rescoup();
const auto& keyword = handlerContext.keyword;
bool slave_mode = handlerContext.static_schedule().slave_mode;
if (!slave_mode) {
std::string msg = fmt::format("GRUPSLAV is only allowed in slave mode.");
throw OpmInputError(msg, handlerContext.keyword.location());
}
if (schedule_state.sim_step() != 0) {
// Currently, I cannot see any reason why GRUPSLAV should be allowed at
// any other report step than the first one. So to keep it simple, we throw
// an error if it is used in any other step. This will also simplify the
// implementation details of MPI communication between master and slave for now..
std::string msg = fmt::format("GRUPSLAV is only allowed in the first simulation step.");
throw OpmInputError(msg, handlerContext.keyword.location());
}
for (const auto& record : keyword) {
const std::string& group_name =
record.getItem<ParserKeywords::GRUPSLAV::SLAVE_GROUP>().getTrimmedString(0);
checkValidSlaveGroupName(group_name, handlerContext);
auto deck_item = record.getItem<ParserKeywords::GRUPSLAV::MASTER_GROUP>();
std::string master_group_name;
if (deck_item.defaultApplied(0)) {
// If defaulted, the master group name is the same as the slave group name
master_group_name = group_name;
} else {
master_group_name = deck_item.getTrimmedString(0);
}
auto oil_prod_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::OIL_PROD_CONSTRAINTS>(), handlerContext);
auto liquid_prod_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::WAT_PROD_CONSTRAINTS>(), handlerContext);
auto gas_prod_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::GAS_PROD_CONSTRAINTS>(), handlerContext);
auto fluid_volume_prod_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::FLUID_VOL_PROD_CONSTRAINTS>(), handlerContext);
auto oil_inj_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::OIL_INJ_CONSTRAINTS>(), handlerContext);
auto water_inj_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::WAT_INJ_CONSTRAINTS>(), handlerContext);
auto gas_inj_flag = getFilterFlag(
record.getItem<ParserKeywords::GRUPSLAV::GAS_INJ_CONSTRAINTS>(), handlerContext);

ReservoirCoupling::GrupSlav grupslav{ group_name, master_group_name, oil_prod_flag, liquid_prod_flag,
gas_prod_flag, fluid_volume_prod_flag, oil_inj_flag, water_inj_flag,
gas_inj_flag};
rescoup.grupSlavs().emplace( group_name, std::move( grupslav ));
}
// TODO: - Validate that a slave group is not subordinate to another slave group
// - Validate that a slave group is not subject to production or injection
// constraints applied to any superior group within the slave reservoir
handlerContext.state().rescoup.update( std::move( rescoup ));
}

} // namespace Opm
158 changes: 158 additions & 0 deletions opm/input/eclipse/Schedule/ResCoup/GrupSlav.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright 2024 Equinor ASA.

This file is part of the Open Porous Media project (OPM).

OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_RESERVOIR_COUPLING_GRUPSLAV_HPP
#define OPM_RESERVOIR_COUPLING_GRUPSLAV_HPP

#include <iostream>
#include <map>
#include <stdexcept>
#include <string>

namespace Opm {

class HandlerContext;

namespace ReservoirCoupling {
class GrupSlav {
public:
enum class FilterFlag {
MAST,
SLAV,
BOTH
};

GrupSlav() = default;

GrupSlav(
const std::string& name,
const std::string& master_group_name,
FilterFlag oil_prod_flag,
FilterFlag liquid_prod_flag,
FilterFlag gas_prod_flag,
FilterFlag fluid_volume_prod_flag,
FilterFlag oil_inj_flag,
FilterFlag water_inj_flag,
FilterFlag gas_inj_flag
) :
m_name{name},
m_master_group_name{master_group_name},
m_oil_prod_flag{oil_prod_flag},
m_liquid_prod_flag{liquid_prod_flag},
m_gas_prod_flag{gas_prod_flag},
m_fluid_volume_prod_flag{fluid_volume_prod_flag},
m_oil_inj_flag{oil_inj_flag},
m_water_inj_flag{water_inj_flag},
m_gas_inj_flag{gas_inj_flag}
{}

static GrupSlav serializationTestObject();

const std::string& name() const {
return this->m_name;
}

const std::string& masterGroupName() const {
return this->m_master_group_name;
}

FilterFlag oilProdFlag() const {
return this->m_oil_prod_flag;
}

FilterFlag liquidProdFlag() const {
return this->m_liquid_prod_flag;
}

FilterFlag gasProdFlag() const {
return this->m_gas_prod_flag;
}

FilterFlag fluidVolumeProdFlag() const {
return this->m_fluid_volume_prod_flag;
}

FilterFlag oilInjFlag() const {
return this->m_oil_inj_flag;
}

FilterFlag waterInjFlag() const {
return this->m_water_inj_flag;
}

FilterFlag gasInjFlag() const {
return this->m_gas_inj_flag;
}

void name(const std::string& value) {
this->m_name = value;
}

void masterGroupName(const std::string& value) {
this->m_master_group_name = value;
}

bool operator==(const GrupSlav& other) const;

template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(m_name);
serializer(m_master_group_name);
serializer(m_oil_prod_flag);
serializer(m_liquid_prod_flag);
serializer(m_gas_prod_flag);
serializer(m_fluid_volume_prod_flag);
serializer(m_oil_inj_flag);
serializer(m_water_inj_flag);
serializer(m_gas_inj_flag);
}

static FilterFlag filterFlagFromString(const std::string& flag) {
if (flag == "MAST") {
return FilterFlag::MAST;
} else if (flag == "SLAV") {
return FilterFlag::SLAV;
} else if (flag == "BOTH") {
return FilterFlag::BOTH;
} else {
throw std::invalid_argument("Invalid filter flag: " + flag);
}
}
private:
std::string m_name;
std::string m_master_group_name;
FilterFlag m_oil_prod_flag;
FilterFlag m_liquid_prod_flag;
FilterFlag m_gas_prod_flag;
FilterFlag m_fluid_volume_prod_flag;
FilterFlag m_oil_inj_flag;
FilterFlag m_water_inj_flag;
FilterFlag m_gas_inj_flag;
};

// NOTE: This operator is needed by Boost.Test, See tests/parser/ReservoirCouplingTests.cpp
std::ostream& operator<<(std::ostream& os, const GrupSlav::FilterFlag& flag);

} // namespace ReservoirCoupling

extern void handleGRUPSLAV(HandlerContext& handlerContext);

} // namespace Opm

#endif // OPM_RESERVOIR_COUPLING_GRUPSLAV_HPP
Loading