Merge pull request #3482 from totto82/bc_schedule

BC in Schedule
This commit is contained in:
Tor Harald Sandve
2023-06-28 16:27:09 +02:00
committed by GitHub
13 changed files with 503 additions and 118 deletions

View File

@@ -161,6 +161,7 @@ if(ENABLE_ECL_INPUT)
src/opm/input/eclipse/Schedule/Action/State.cpp
src/opm/input/eclipse/Schedule/Action/WGNames.cpp
src/opm/input/eclipse/Schedule/ArrayDimChecker.cpp
src/opm/input/eclipse/Schedule/BCProp.cpp
src/opm/input/eclipse/Schedule/CompletedCells.cpp
src/opm/input/eclipse/Schedule/eval_uda.cpp
src/opm/input/eclipse/Schedule/Events.cpp
@@ -1209,6 +1210,7 @@ if(ENABLE_ECL_INPUT)
opm/input/eclipse/Schedule/Action/State.hpp
opm/input/eclipse/Schedule/Action/WGNames.hpp
opm/input/eclipse/Schedule/ArrayDimChecker.hpp
opm/input/eclipse/Schedule/BCProp.hpp
opm/input/eclipse/Schedule/GasLiftOpt.hpp
opm/input/eclipse/Schedule/Network/Balance.hpp
opm/input/eclipse/Schedule/Network/Branch.hpp

View File

@@ -33,59 +33,34 @@ namespace Opm {
class Deck;
class DeckRecord;
enum class BCType {
RATE,
FREE,
DIRICHLET,
THERMAL
};
enum class BCComponent {
OIL,
GAS,
WATER,
SOLVENT,
POLYMER,
NONE
};
class BCConfig {
public:
struct BCFace {
struct BCRegion {
int index;
int i1,i2;
int j1,j2;
int k1,k2;
BCType bctype;
FaceDir::DirEnum dir;
BCComponent component;
double rate;
std::optional<double> pressure;
std::optional<double> temperature;
BCFace() = default;
explicit BCFace(const DeckRecord& record, const GridDims& grid);
BCRegion() = default;
explicit BCRegion(const DeckRecord& record, const GridDims& grid);
static BCFace serializationTestObject();
static BCRegion serializationTestObject();
bool operator==(const BCFace& other) const;
bool operator==(const BCRegion& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(index);
serializer(i1);
serializer(i2);
serializer(j1);
serializer(j2);
serializer(k1);
serializer(k2);
serializer(bctype);
serializer(dir);
serializer(component);
serializer(rate);
serializer(pressure);
serializer(temperature);
}
};
@@ -96,8 +71,8 @@ public:
static BCConfig serializationTestObject();
std::size_t size() const;
std::vector<BCFace>::const_iterator begin() const;
std::vector<BCFace>::const_iterator end() const;
std::vector<BCRegion>::const_iterator begin() const;
std::vector<BCRegion>::const_iterator end() const;
bool operator==(const BCConfig& other) const;
template<class Serializer>
@@ -107,7 +82,7 @@ public:
}
private:
std::vector<BCFace> m_faces;
std::vector<BCRegion> m_faces;
};
} //namespace Opm

View File

@@ -0,0 +1,113 @@
/*
Copyright 2023 Equinor ASA.
Copyright 2023 Norce.
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_BC_PROP_HPP
#define OPM_BC_PROP_HPP
#include <vector>
#include <cstddef>
#include <optional>
#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
#include <opm/input/eclipse/EclipseState/Grid/GridDims.hpp>
namespace Opm {
class Deck;
class DeckRecord;
enum class BCType {
RATE,
FREE,
DIRICHLET,
THERMAL,
CLOSED,
NONE
};
enum class BCComponent {
OIL,
GAS,
WATER,
SOLVENT,
POLYMER,
NONE
};
class BCProp {
public:
struct BCFace {
int index;
BCType bctype;
BCComponent component;
double rate;
std::optional<double> pressure;
std::optional<double> temperature;
BCFace() = default;
explicit BCFace(const DeckRecord& record);
static BCFace serializationTestObject();
bool operator==(const BCFace& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(index);
serializer(bctype);
serializer(component);
serializer(rate);
serializer(pressure);
serializer(temperature);
}
};
BCProp() = default;
static BCProp serializationTestObject();
std::size_t size() const;
std::vector<BCFace>::const_iterator begin() const;
std::vector<BCFace>::const_iterator end() const;
bool operator==(const BCProp& other) const;
const BCFace& operator[](int index) const;
void updateBCProp(const DeckRecord& record);
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer(m_faces);
}
private:
std::vector<BCFace> m_faces;
};
} //namespace Opm
#endif

View File

@@ -679,6 +679,7 @@ namespace Opm
void handleAQUCT (HandlerContext&);
void handleAQUFETP (HandlerContext&);
void handleAQUFLUX (HandlerContext&);
void handleBCProp (HandlerContext&);
void handleBRANPROP (HandlerContext&);
void handleCOMPDAT (HandlerContext&);
void handleCOMPLUMP (HandlerContext&);

View File

@@ -35,6 +35,7 @@
#include <opm/input/eclipse/Schedule/Tuning.hpp>
#include <opm/input/eclipse/Schedule/OilVaporizationProperties.hpp>
#include <opm/input/eclipse/Schedule/Events.hpp>
#include <opm/input/eclipse/Schedule/BCProp.hpp>
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEnums.hpp>
#include <opm/input/eclipse/Schedule/MessageLimits.hpp>
@@ -482,6 +483,7 @@ namespace Opm {
map_member<std::string, Well> wells;
// constant flux aquifers
std::unordered_map<int, SingleAquiferFlux> aqufluxs;
BCProp bcprop;
std::unordered_map<std::string, double> target_wellpi;
std::optional<NextStep> next_tstep;
@@ -510,6 +512,7 @@ namespace Opm {
serializer(m_whistctl_mode);
serializer(target_wellpi);
serializer(aqufluxs);
serializer(bcprop);
}

View File

@@ -22,73 +22,22 @@
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/input/eclipse/EclipseState/SimulationConfig/BCConfig.hpp>
#include <opm/common/utility/OpmInputError.hpp>
namespace Opm {
namespace {
namespace fromstring {
BCType bctype(const std::string& s) {
if (s == "RATE")
return BCType::RATE;
if (s == "FREE")
return BCType::FREE;
if (s == "DIRICHLET")
return BCType::DIRICHLET;
if (s == "THERMAL")
return BCType::THERMAL;
throw std::invalid_argument("Not recognized boundary condition type: " + s);
}
BCComponent component(const std::string& s) {
if (s == "OIL")
return BCComponent::OIL;
if (s == "GAS")
return BCComponent::GAS;
if (s == "WATER")
return BCComponent::WATER;
if (s == "SOLVENT")
return BCComponent::SOLVENT;
if (s == "POLYMER")
return BCComponent::POLYMER;
if (s == "NONE")
return BCComponent::NONE;
throw std::invalid_argument("Not recognized boundary condition compononet: " + s);
}
}
}
using BC = ParserKeywords::BC;
BCConfig::BCFace::BCFace(const DeckRecord& record, const GridDims& grid) :
using BC = ParserKeywords::BCCON;
BCConfig::BCRegion::BCRegion(const DeckRecord& record, const GridDims& grid) :
index(record.getItem<BC::INDEX>().get<int>(0)),
i1(0),
i2(grid.getNX() - 1),
j1(0),
j2(grid.getNY() - 1),
k1(0),
k2(grid.getNZ() - 1),
bctype(fromstring::bctype(record.getItem<BC::TYPE>().get<std::string>(0))),
dir(FaceDir::FromString(record.getItem<BC::DIRECTION>().get<std::string>(0))),
component(fromstring::component(record.getItem<BC::COMPONENT>().get<std::string>(0))),
rate(record.getItem<BC::RATE>().getSIDouble(0))
dir(FaceDir::FromString(record.getItem<BC::DIRECTION>().get<std::string>(0)))
{
if (const auto& P = record.getItem<BC::PRESSURE>(); ! P.defaultApplied(0)) {
pressure = P.getSIDouble(0);
}
if (const auto& T = record.getItem<BC::TEMPERATURE>(); ! T.defaultApplied(0)) {
temperature = T.getSIDouble(0);
}
if (const auto& I1 = record.getItem<BC::I1>(); ! I1.defaultApplied(0)) {
this->i1 = I1.get<int>(0) - 1;
}
@@ -109,46 +58,48 @@ BCConfig::BCFace::BCFace(const DeckRecord& record, const GridDims& grid) :
}
}
BCConfig::BCFace BCConfig::BCFace::serializationTestObject()
BCConfig::BCRegion BCConfig::BCRegion::serializationTestObject()
{
BCFace result;
result.i1 = 10;
result.i2 = 11;
result.j1 = 12;
result.j2 = 13;
result.k1 = 14;
result.k2 = 15;
result.bctype = BCType::RATE;
BCRegion result;
result.index = 10;
result.i1 = 12;
result.i2 = 13;
result.j1 = 13;
result.j2 = 14;
result.k1 = 15;
result.k2 = 16;
result.dir = FaceDir::XPlus;
result.component = BCComponent::GAS;
result.rate = 100.0;
result.pressure = 101.0;
result.temperature = 102.0;
return result;
}
bool BCConfig::BCFace::operator==(const BCConfig::BCFace& other) const {
return this->i1 == other.i1 &&
bool BCConfig::BCRegion::operator==(const BCConfig::BCRegion& other) const {
return this->index == other.index &&
this->i1 == other.i1 &&
this->i2 == other.i2 &&
this->j1 == other.j1 &&
this->j2 == other.j2 &&
this->k1 == other.k1 &&
this->k2 == other.k2 &&
this->bctype == other.bctype &&
this->dir == other.dir &&
this->component == other.component &&
this->rate == other.rate &&
this->pressure == other.pressure &&
this->temperature == other.temperature;
this->dir == other.dir;
}
BCConfig::BCConfig(const Deck& deck) {
if(deck.hasKeyword<ParserKeywords::BC>()){
for (const auto* keyword : deck.getKeywordList<ParserKeywords::BC>()) {
const std::string reason = "ERROR: The BC keyword is obsolete. \n "
"Instead use BCCON in the GRID section to specify the connections. \n "
"And BCPROP in the SCHEDULE section to specify the type and values. \n"
"Check the OPM manual for details.";
throw OpmInputError { reason, keyword->location()};
}
}
GridDims grid( deck );
for (const auto& kw: deck.getKeywordList<ParserKeywords::BC>()) {
for (const auto& kw: deck.getKeywordList<ParserKeywords::BCCON>()) {
for (const auto& record : *kw)
this->m_faces.emplace_back( record, grid );
}
@@ -158,7 +109,7 @@ BCConfig::BCConfig(const Deck& deck) {
BCConfig BCConfig::serializationTestObject()
{
BCConfig result;
result.m_faces = {BCFace::serializationTestObject()};
result.m_faces = {BCRegion::serializationTestObject()};
return result;
}
@@ -168,11 +119,11 @@ std::size_t BCConfig::size() const {
return this->m_faces.size();
}
std::vector<BCConfig::BCFace>::const_iterator BCConfig::begin() const {
std::vector<BCConfig::BCRegion>::const_iterator BCConfig::begin() const {
return this->m_faces.begin();
}
std::vector<BCConfig::BCFace>::const_iterator BCConfig::end() const {
std::vector<BCConfig::BCRegion>::const_iterator BCConfig::end() const {
return this->m_faces.end();
}

View File

@@ -0,0 +1,169 @@
/*
Copyright 2023 Equinor ASA.
Copyright 2023 Norce.
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.
*/
#include <string>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/B.hpp>
#include <opm/input/eclipse/Schedule/BCProp.hpp>
namespace Opm {
namespace {
namespace fromstring {
BCType bctype(const std::string& s) {
if (s == "RATE")
return BCType::RATE;
if (s == "FREE")
return BCType::FREE;
if (s == "DIRICHLET")
return BCType::DIRICHLET;
if (s == "THERMAL")
return BCType::THERMAL;
if (s == "CLOSED")
return BCType::CLOSED;
throw std::invalid_argument("Not recognized boundary condition type: " + s);
}
BCComponent component(const std::string& s) {
if (s == "OIL")
return BCComponent::OIL;
if (s == "GAS")
return BCComponent::GAS;
if (s == "WATER")
return BCComponent::WATER;
if (s == "SOLVENT")
return BCComponent::SOLVENT;
if (s == "POLYMER")
return BCComponent::POLYMER;
if (s == "NONE")
return BCComponent::NONE;
throw std::invalid_argument("Not recognized boundary condition compononet: " + s);
}
}
}
using BCKEY = ParserKeywords::BCPROP;
BCProp::BCFace::BCFace(const DeckRecord& record) :
index(record.getItem<BCKEY::INDEX>().get<int>(0)),
bctype(fromstring::bctype(record.getItem<BCKEY::TYPE>().get<std::string>(0))),
component(fromstring::component(record.getItem<BCKEY::COMPONENT>().get<std::string>(0))),
rate(record.getItem<BCKEY::RATE>().getSIDouble(0))
{
if (const auto& P = record.getItem<BCKEY::PRESSURE>(); ! P.defaultApplied(0)) {
pressure = P.getSIDouble(0);
}
if (const auto& T = record.getItem<BCKEY::TEMPERATURE>(); ! T.defaultApplied(0)) {
temperature = T.getSIDouble(0);
}
}
BCProp::BCFace BCProp::BCFace::serializationTestObject()
{
BCFace result;
result.index = 100;
result.bctype = BCType::RATE;
result.component = BCComponent::GAS;
result.rate = 101.0;
result.pressure = 102.0;
result.temperature = 103.0;
return result;
}
bool BCProp::BCFace::operator==(const BCProp::BCFace& other) const {
return this->index == other.index &&
this->bctype == other.bctype &&
this->component == other.component &&
this->rate == other.rate &&
this->pressure == other.pressure &&
this->temperature == other.temperature;
}
void BCProp::updateBCProp(const DeckRecord& record) {
const BCProp::BCFace bcnew( record );
for (auto& bc : m_faces) {
if (bc.index == bcnew.index && bc.component == bcnew.component)
{
bc = bcnew;
return;
}
}
this->m_faces.emplace_back( bcnew );
}
BCProp BCProp::serializationTestObject()
{
BCProp result;
result.m_faces = {BCFace::serializationTestObject()};
return result;
}
std::size_t BCProp::size() const {
return this->m_faces.size();
}
std::vector<BCProp::BCFace>::const_iterator BCProp::begin() const {
return this->m_faces.begin();
}
std::vector<BCProp::BCFace>::const_iterator BCProp::end() const {
return this->m_faces.end();
}
const BCProp::BCFace& BCProp::operator[](int index) const {
for (auto& bc : m_faces) {
if (bc.index == index)
{
return bc;
}
}
// add throw
return this->m_faces[0];
}
bool BCProp::operator==(const BCProp& other) const {
return this->m_faces == other.m_faces;
}
} //namespace Opm

View File

@@ -145,6 +145,13 @@ namespace {
}
}
void Schedule::handleBCProp(Schedule::HandlerContext& handlerContext) {
auto& bcprop = this->snapshots.back().bcprop;
for (const auto& record : handlerContext.keyword) {
bcprop.updateBCProp(record);
}
}
void Schedule::handleBRANPROP(HandlerContext& handlerContext) {
auto ext_network = this->snapshots.back().network.get();
@@ -2466,6 +2473,7 @@ Well{0} entered with 'FIELD' parent group:
{ "AQUCT", &Schedule::handleAQUCT },
{ "AQUFETP", &Schedule::handleAQUFETP },
{ "AQUFLUX", &Schedule::handleAQUFLUX },
{ "BCPROP", &Schedule::handleBCProp },
{ "BOX", &Schedule::handleGEOKeyword},
{ "BRANPROP", &Schedule::handleBRANPROP },
{ "COMPDAT" , &Schedule::handleCOMPDAT },

View File

@@ -0,0 +1,40 @@
{
"name": "BCCON",
"sections": [
"GRID"
],
"items": [
{
"name": "INDEX",
"value_type": "INT"
},
{
"name": "I1",
"value_type": "INT"
},
{
"name": "I2",
"value_type": "INT"
},
{
"name": "J1",
"value_type": "INT"
},
{
"name": "J2",
"value_type": "INT"
},
{
"name": "K1",
"value_type": "INT"
},
{
"name": "K2",
"value_type": "INT"
},
{
"name": "DIRECTION",
"value_type": "STRING"
}
]
}

View File

@@ -0,0 +1,38 @@
{
"name": "BCPROP",
"sections": [
"SCHEDULE"
],
"items": [
{
"name": "INDEX",
"value_type": "INT"
},
{
"name": "TYPE",
"value_type": "STRING"
},
{
"name": "COMPONENT",
"value_type": "STRING",
"default": "NONE"
},
{
"name": "RATE",
"value_type": "DOUBLE",
"dimension": "Mass/Time*Length*Length",
"default": 0
},
{
"name": "PRESSURE",
"value_type": "DOUBLE",
"dimension": "Pressure",
"default": 1
},
{
"name": "TEMPERATURE",
"value_type": "DOUBLE",
"dimension": "Temperature"
}
]
}

View File

@@ -1104,6 +1104,8 @@ set( keywords
002_Frontsim/N/NOGRAV
900_OPM/B/BC
900_OPM/B/BCCON
900_OPM/B/BCPROP
900_OPM/C/CO2STOR
900_OPM/C/COMPTRAJ
900_OPM/D/DISGASW

View File

@@ -59,6 +59,7 @@
#include <opm/input/eclipse/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/EclipseState/SimulationConfig/BCConfig.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/DeckItem.hpp>
@@ -70,6 +71,7 @@
#include <opm/input/eclipse/Parser/ParseContext.hpp>
#include <opm/input/eclipse/Units/Dimension.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
@@ -5436,3 +5438,43 @@ END
BOOST_CHECK(wvfpexp2.prevent());
}
BOOST_AUTO_TEST_CASE(createDeckWithBC) {
std::string input = R"(
START -- 0
19 JUN 2007 /
SOLUTION
SCHEDULE
BCPROP
1 RATE GAS 100.0 /
2 FREE /
/
DATES -- 1
10 OKT 2008 /
/
BCPROP
1 RATE GAS 200.0 /
2 FREE 4* /
/
)";
const auto& schedule = make_schedule(input);
{
size_t currentStep = 0;
const auto& bc = schedule[currentStep].bcprop;
BOOST_CHECK_EQUAL(bc.size(), 2);
const auto& bcface0 = bc[0];
BOOST_CHECK_CLOSE(bcface0.rate * Opm::unit::day, 100, 1e-8 );
}
{
size_t currentStep = 1;
const auto& bc = schedule[currentStep].bcprop;
BOOST_CHECK_EQUAL(bc.size(), 2);
const auto& bcface0 = bc[0];
BOOST_CHECK_CLOSE(bcface0.rate * Opm::unit::day, 200, 1e-8 );
}
}

View File

@@ -119,6 +119,30 @@ GRID
REGIONS
)";
const std::string& inputStr_bc = R"(
RUNSPEC
DIMENS
10 3 4 /
GRID
SOLUTION
BC
1 1 1 1 1 10 X- FREE GAS/
/
REGIONS
)";
const std::string& inputStr_bccon = R"(
RUNSPEC
DIMENS
10 3 4 /
GRID
BCCON
1 1 1 1 1 1 1 X- /
2 10 10 4* X /
/
REGIONS
)";
namespace {
std::string simDeckStringTEMP()
{
@@ -150,6 +174,23 @@ static Deck createDeck(const std::string& input) {
return parser.parseString(input);
}
BOOST_AUTO_TEST_CASE(SimulationConfigBCCON) {
auto deck = createDeck(inputStr_bccon);
BCConfig bcconfig(deck);
std::vector<int> i1s = { 1, 10};
std::vector<int> k2s = { 1, 4};
std::vector<Opm::FaceDir::DirEnum> dirs = { Opm::FaceDir::XMinus , Opm::FaceDir::XPlus};
for (const auto bc : bcconfig) {
BOOST_CHECK(bc.i1 == (i1s[bc.index - 1 ]) - 1);
BOOST_CHECK(bc.k2 == (k2s[bc.index - 1 ]) - 1);
BOOST_CHECK(bc.dir == dirs[bc.index -1 ]);
}
}
BOOST_AUTO_TEST_CASE(SimulationConfigBC) {
BOOST_CHECK_THROW( BCConfig(createDeck(inputStr_bc)), OpmInputError );
}
BOOST_AUTO_TEST_CASE(SimulationConfigGetThresholdPressureTableTest) {
auto deck = createDeck(inputStr);