Merge pull request #2246 from GitPaean/new_numerical_aquifer

numerical aquifers
This commit is contained in:
Joakim Hove
2021-01-28 11:14:02 +01:00
committed by GitHub
13 changed files with 811 additions and 11 deletions
+8
View File
@@ -60,6 +60,10 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Aquifer/Aquifetp.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/Aquancon.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/AquiferHelpers.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferConnection.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/SingleNumericalAquifer.cpp
src/opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquifers.cpp
src/opm/parser/eclipse/EclipseState/checkDeck.cpp
src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp
src/opm/parser/eclipse/EclipseState/EclipseState.cpp
@@ -702,6 +706,10 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Aquifer/AquiferConfig.hpp
opm/parser/eclipse/EclipseState/Aquifer/AquiferCT.hpp
opm/parser/eclipse/EclipseState/Aquifer/Aquifetp.hpp
opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.hpp
opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferConnection.hpp
opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/SingleNumericalAquifer.hpp
opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquifers.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/ActionAST.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/ActionContext.hpp
opm/parser/eclipse/EclipseState/Schedule/Action/ActionResult.hpp
@@ -23,17 +23,20 @@
#include <opm/parser/eclipse/EclipseState/Aquifer/Aquancon.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/Aquifetp.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/AquiferCT.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquifers.hpp>
namespace Opm {
class TableManager;
class EclipseGrid;
class Deck;
class FieldPropsManager;
class AquiferConfig {
public:
AquiferConfig() = default;
AquiferConfig(const TableManager& tables, const EclipseGrid& grid, const Deck& deck);
AquiferConfig(const TableManager& tables, const EclipseGrid& grid,
const Deck& deck, const FieldPropsManager& field_props);
AquiferConfig(const Aquifetp& fetp, const AquiferCT& ct, const Aquancon& conn);
static AquiferConfig serializeObject();
@@ -51,12 +54,14 @@ public:
aquifetp.serializeOp(serializer);
aquiferct.serializeOp(serializer);
aqconn.serializeOp(serializer);
numerical_aquifers.serializeOp(serializer);
}
private:
Aquifetp aquifetp;
AquiferCT aquiferct;
Aquancon aqconn;
NumericalAquifers numerical_aquifers;
};
}
@@ -0,0 +1,68 @@
/*
Copyright (C) 2020 SINTEF Digital
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_NUMERICALAQUIFERCELL_HPP
#define OPM_NUMERICALAQUIFERCELL_HPP
#include <optional>
namespace Opm {
class DeckRecord;
class EclipseGrid;
class FieldPropsManager;
struct NumericalAquiferCell {
NumericalAquiferCell(const DeckRecord&, const EclipseGrid&, const FieldPropsManager&);
NumericalAquiferCell() = default;
size_t aquifer_id;
size_t I, J, K;
double area;
double length;
double porosity;
double permeability;
double depth;
std::optional<double> init_pressure;
int pvttable;
int sattable;
size_t global_index;
double cellVolume() const;
double poreVolume() const;
double transmissiblity() const;
bool operator == (const NumericalAquiferCell& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer) {
serializer(this->aquifer_id);
serializer(this->I);
serializer(this->J);
serializer(this->K);
serializer(this->area);
serializer(this->length);
serializer(this->porosity);
serializer(this->permeability);
serializer(this->depth);
serializer(this->pvttable);
serializer(this->sattable);
serializer(this->global_index);
}
};
}
#endif //OPM_NUMERICALAQUIFERCELL_HPP
@@ -0,0 +1,74 @@
/*
Copyright (C) 2020 SINTEF Digital
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_NUMERICALAQUIFERCONNECTION_HPP
#define OPM_NUMERICALAQUIFERCONNECTION_HPP
#include <opm/parser/eclipse/EclipseState/Grid/FaceDir.hpp>
#include <map>
#include <array>
#include <vector>
namespace Opm {
class EclipseGrid;
class Deck;
class DeckRecord;
struct NumericalAquiferConnection {
// TODO: I do not think we need all the values here
size_t aquifer_id;
size_t I, J, K;
size_t global_index;
FaceDir::DirEnum face_dir;
double trans_multipler;
int trans_option;
bool connect_active_cell;
// The following are options related to VE simulation
double ve_frac_relperm;
double ve_frac_cappress;
NumericalAquiferConnection(size_t i, size_t j, size_t k, size_t global_index, bool allow_connect_active, const DeckRecord& record);
NumericalAquiferConnection() = default;
bool operator==(const NumericalAquiferConnection& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer) {
serializer(this->aquifer_id);
serializer(this->I);
serializer(this->J);
serializer(this->K);
serializer(this->global_index);
serializer(this->face_dir);
serializer(this->trans_multipler);
serializer(this->trans_option);
serializer(this->connect_active_cell);
serializer(this->ve_frac_relperm);
serializer(this->ve_frac_cappress);
}
static std::map<size_t, std::map<size_t, NumericalAquiferConnection>> generateConnections(const Deck& deck, const EclipseGrid& grid);
private:
static std::vector<NumericalAquiferConnection> connectionsFromSingleRecord(const EclipseGrid& grid, const DeckRecord& record);
};
}
#endif //OPM_NUMERICALAQUIFERCONNECTION_HPP
@@ -0,0 +1,61 @@
/*
Copyright (C) 2020 SINTEF Digital
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_NUMERICALAQUIFERS_HPP
#define OPM_NUMERICALAQUIFERS_HPP
#include <unordered_map>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/SingleNumericalAquifer.hpp>
namespace Opm {
class Deck;
class EclipseGrid;
class FieldPropsManager;
struct NumericalAquiferCell;
class NumericalAquifers {
public:
NumericalAquifers() = default;
NumericalAquifers(const Deck& deck, const EclipseGrid& grid, const FieldPropsManager& field_props);
size_t numAquifer() const;
bool hasAquifer(size_t aquifer_id) const;
const SingleNumericalAquifer& getAquifer(size_t aquifer_id) const;
bool operator==(const NumericalAquifers& other) const;
std::unordered_map<size_t, const NumericalAquiferCell*> allAquiferCells() const;
static NumericalAquifers serializeObject();
template <class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.map(this->m_aquifers);
}
private:
std::unordered_map <size_t, SingleNumericalAquifer> m_aquifers;
void addAquiferCell(const NumericalAquiferCell& aqu_cell);
void addAquiferConnections(const Deck &deck, const EclipseGrid &grid);
};
}
#endif //OPM_NUMERICALAQUIFERS_HPP
@@ -0,0 +1,65 @@
/*
Copyright (C) 2020 SINTEF Digital
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_SINGLENUMERICALAQUIFER_HPP
#define OPM_SINGLENUMERICALAQUIFER_HPP
#include <vector>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferConnection.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.hpp>
namespace Opm {
class SingleNumericalAquifer {
public:
explicit SingleNumericalAquifer(const size_t aqu_id);
SingleNumericalAquifer() = default;
void addAquiferCell(const NumericalAquiferCell& aqu_cell);
void addAquiferConnection(const NumericalAquiferConnection& aqu_con);
// TODO: the following two can be made one function. Let us see
// how we use them at the end
size_t numCells() const;
size_t numConnections() const;
const NumericalAquiferCell* getCellPrt(size_t index) const;
bool operator==(const SingleNumericalAquifer& other) const;
template<class Serializer>
void serializeOp(Serializer& serializer) {
serializer(this->id_);
serializer.vector(this->cells_);
serializer.vector(this->connections_);
}
private:
// Maybe this id_ is not necessary
// Because if it is a map, the id will be there
// Then adding aquifer cells will be much easier with the
// default constructor
size_t id_;
std::vector<NumericalAquiferCell> cells_;
std::vector<NumericalAquiferConnection> connections_;
};
}
#endif //OPM_SINGLENUMERICALAQUIFER_HPP
@@ -22,14 +22,14 @@
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
namespace Opm {
AquiferConfig::AquiferConfig(const TableManager& tables, const EclipseGrid& grid, const Deck& deck):
AquiferConfig::AquiferConfig(const TableManager& tables, const EclipseGrid& grid,
const Deck& deck, const FieldPropsManager& field_props):
aquifetp(deck),
aquiferct(tables, deck),
aqconn(grid,deck)
aqconn(grid,deck),
numerical_aquifers(deck, grid, field_props)
{}
AquiferConfig::AquiferConfig(const Aquifetp& fetp, const AquiferCT& ct, const Aquancon& conn) :
@@ -44,18 +44,22 @@ AquiferConfig AquiferConfig::serializeObject()
result.aquifetp = Aquifetp::serializeObject();
result.aquiferct = AquiferCT::serializeObject();
result.aqconn = Aquancon::serializeObject();
result.numerical_aquifers = NumericalAquifers::serializeObject();
return result;
}
bool AquiferConfig::active() const {
return this->aqconn.active();
return this->aquiferct.size() > 0 ||
this->aquifetp.size() > 0 ||
this->numerical_aquifers.numAquifer() > 0;
}
bool AquiferConfig::operator==(const AquiferConfig& other) {
return this->aquifetp == other.aquifetp &&
this->aquiferct == other.aquiferct &&
this->aqconn == other.aqconn;
this->aqconn == other.aqconn &&
this->numerical_aquifers == other.numerical_aquifers;
}
const AquiferCT& AquiferConfig::ct() const {
@@ -71,7 +75,9 @@ const Aquancon& AquiferConfig::connections() const {
}
bool AquiferConfig::hasAquifer(const int aquID) const {
return aquifetp.hasAquifer(aquID) || aquiferct.hasAquifer(aquID);
return aquifetp.hasAquifer(aquID) ||
aquiferct.hasAquifer(aquID) ||
numerical_aquifers.hasAquifer(aquID);
}
}
@@ -0,0 +1,106 @@
/*
Copyright (C) 2020 SINTEF Digital
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/parser/eclipse/Parser/ParserKeywords/A.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.hpp>
namespace Opm {
using AQUNUM = ParserKeywords::AQUNUM;
NumericalAquiferCell::NumericalAquiferCell(const DeckRecord& record, const EclipseGrid& grid, const FieldPropsManager& field_props)
: aquifer_id( record.getItem<AQUNUM::AQUIFER_ID>().get<int>(0) )
, I ( record.getItem<AQUNUM::I>().get<int>(0) - 1 )
, J ( record.getItem<AQUNUM::J>().get<int>(0) - 1 )
, K ( record.getItem<AQUNUM::K>().get<int>(0) - 1 )
, area (record.getItem<AQUNUM::CROSS_SECTION>().getSIDouble(0) )
, length ( record.getItem<AQUNUM::LENGTH>().getSIDouble(0) )
, permeability( record.getItem<AQUNUM::PERM>().getSIDouble(0) )
{
const auto& poro = field_props.get_double("PORO");
const auto& pvtnum = field_props.get_int("PVTNUM");
const auto& satnum = field_props.get_int("SATNUM");
this->global_index = grid.getGlobalIndex(I, J, K);
const size_t active_index = grid.activeIndex(this->global_index);
if ( !record.getItem<AQUNUM::PORO>().defaultApplied(0) ) {
this->porosity = record.getItem<AQUNUM::PORO>().getSIDouble(0);
} else {
this->porosity = poro[active_index];
}
if ( !record.getItem<AQUNUM::DEPTH>().defaultApplied(0) ) {
this->depth = record.getItem<AQUNUM::DEPTH>().getSIDouble(0);
} else {
this->depth = grid.getCellDepth(this->global_index);
}
if ( !record.getItem<AQUNUM::INITIAL_PRESSURE>().defaultApplied(0) ) {
this->init_pressure = record.getItem<AQUNUM::INITIAL_PRESSURE>().getSIDouble(0);
}
if ( !record.getItem<AQUNUM::PVT_TABLE_NUM>().defaultApplied(0) ) {
this->pvttable = record.getItem<AQUNUM::PVT_TABLE_NUM>().get<int>(0);
} else {
this->pvttable = pvtnum[active_index];
}
if ( !record.getItem<AQUNUM::SAT_TABLE_NUM>().defaultApplied(0) ) {
this->sattable = record.getItem<AQUNUM::SAT_TABLE_NUM>().get<int>(0);
} else {
this->sattable = satnum[active_index];
}
}
double NumericalAquiferCell::cellVolume() const {
return this->area * this->length;
}
bool NumericalAquiferCell::operator==(const NumericalAquiferCell& other) const {
return this->aquifer_id == other.aquifer_id &&
this->I == other.I &&
this->J == other.J &&
this->K == other.K &&
this->area == other.area &&
this->length == other.length &&
this->porosity == other.porosity &&
this->permeability == other.permeability &&
this->depth == other.depth &&
this->init_pressure == other.init_pressure &&
this->pvttable == other.pvttable &&
this->sattable == other.sattable &&
this->global_index == other.global_index;
}
double NumericalAquiferCell::poreVolume() const {
return this->porosity * this->cellVolume();
}
double NumericalAquiferCell::transmissiblity() const {
return 2. * this->permeability * this->area / this->length;;
}
}
@@ -0,0 +1,130 @@
/*
Copyright (C) 2020 SINTEF Digital
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/parser/eclipse/Parser/ParserKeywords/A.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferConnection.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <fmt/format.h>
#include "../AquiferHelpers.hpp"
#include <string>
namespace Opm {
std::map<size_t, std::map<size_t, NumericalAquiferConnection>>
NumericalAquiferConnection::generateConnections(const Deck &deck, const EclipseGrid &grid)
{
using AQUCON=ParserKeywords::AQUCON;
if ( !deck.hasKeyword<AQUCON>() ) return {};
std::map<size_t, std::map<size_t, NumericalAquiferConnection>> connections;
const auto& aqucon_keywords = deck.getKeywordList<AQUCON>();
for (const auto& keyword : aqucon_keywords) {
OpmLog::info(OpmInputError::format("Initializing numerical aquifer connections from {keyword} in {file} line {line}", keyword->location()));
for (const auto& record : *keyword) {
const auto cons_from_record = NumericalAquiferConnection::connectionsFromSingleRecord(grid, record);
for (auto con : cons_from_record) {
const size_t aqu_id = con.aquifer_id;
const size_t global_index = con.global_index;
auto& aqu_cons = connections[aqu_id];
if (aqu_cons.find(global_index) == aqu_cons.end()) {
aqu_cons.insert({global_index, con});
} else {
auto error = fmt::format("Numerical aquifer cell at ({}, {}, {}) is declared more than once"
" as a connection for numerical aquifer {}",
con.I + 1, con.J + 1, con.K + 1, con.aquifer_id);
throw OpmInputError(error, keyword->location());
}
}
}
}
return connections;
}
// TODO: we should not need all following the information here
using AQUCON = ParserKeywords::AQUCON;
NumericalAquiferConnection::NumericalAquiferConnection(const size_t i, const size_t j, const size_t k,
const size_t global_index_in, const bool allow_connection_active, const DeckRecord& record)
: aquifer_id(record.getItem<AQUCON::ID>().get<int>(0))
, I(i)
, J(j)
, K(k)
, global_index(global_index_in)
, face_dir(FaceDir::FromString(record.getItem<AQUCON::CONNECT_FACE>().getTrimmedString(0)))
, trans_multipler(record.getItem<AQUCON::TRANS_MULT>().get<double>(0))
, trans_option(record.getItem<AQUCON::TRANS_OPTION>().get<int>(0))
, connect_active_cell(allow_connection_active)
, ve_frac_relperm(record.getItem<AQUCON::VEFRAC>().get<double>(0))
, ve_frac_cappress(record.getItem<AQUCON::VEFRACP>().get<double>(0))
{
}
std::vector<NumericalAquiferConnection> NumericalAquiferConnection::connectionsFromSingleRecord(const EclipseGrid& grid, const DeckRecord& record) {
std::vector<NumericalAquiferConnection> cons;
const size_t i1 = record.getItem<AQUCON::I1>().get<int>(0) - 1;
const size_t j1 = record.getItem<AQUCON::J1>().get<int>(0) - 1;
const size_t k1 = record.getItem<AQUCON::K1>().get<int>(0) - 1;
const size_t i2 = record.getItem<AQUCON::I2>().get<int>(0) - 1;
const size_t j2 = record.getItem<AQUCON::J2>().get<int>(0) - 1;
const size_t k2 = record.getItem<AQUCON::K2>().get<int>(0) - 1;
// whether the connection face can connect to active/internal cells
// by default NO, which means basically the aquifer should be outside of the reservoir
const bool allow_internal_cells = DeckItem::to_bool( record.getItem<AQUCON::ALLOW_INTERNAL_CELLS>().getTrimmedString(0) );
const FaceDir::DirEnum face_dir
= FaceDir::FromString(record.getItem<AQUCON::CONNECT_FACE>().getTrimmedString(0));
for (size_t k = k1; k <= k2; ++k) {
for (size_t j = j1; j <=j2; ++j) {
for (size_t i = i1; i <= i2; ++i) {
// TODO: we probably should give a message here
if (!grid.cellActive(i, j, k)) {
continue;
}
if (allow_internal_cells ||
!AquiferHelpers::neighborCellInsideReservoirAndActive(grid, i, j, k, face_dir)) {
const size_t global_index = grid.getGlobalIndex(i, j, k);
cons.emplace_back(i, j, k, global_index, allow_internal_cells, record);
}
}
}
}
return cons;
}
bool NumericalAquiferConnection::operator==(const NumericalAquiferConnection& other) const {
return this->aquifer_id == other.aquifer_id &&
this->I == other.I &&
this->J == other.J &&
this->K == other.K &&
this->global_index == other.global_index &&
this->face_dir == other.face_dir &&
this->trans_multipler == other.trans_multipler &&
this->trans_option == other.trans_option &&
this->connect_active_cell == other.connect_active_cell &&
this->ve_frac_relperm == other.ve_frac_relperm &&
this->ve_frac_cappress == other.ve_frac_cappress;
}
}
@@ -0,0 +1,148 @@
/*
Copyright (C) 2020 SINTEF Digital
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/parser/eclipse/Parser/ParserKeywords/A.hpp>
#include <fmt/format.h>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/KeywordLocation.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquifers.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/SingleNumericalAquifer.hpp>
#include <set>
namespace Opm {
NumericalAquifers::NumericalAquifers(const Deck& deck, const EclipseGrid& grid,
const FieldPropsManager& field_props) {
using AQUNUM=ParserKeywords::AQUNUM;
if ( !deck.hasKeyword<AQUNUM>() ) return;
std::set<size_t> cells;
// there might be multiple keywords of keyword AQUNUM, it is not totally
// clear about the rules here. For now, we take care of all the keywords
const auto& aqunum_keywords = deck.getKeywordList<AQUNUM>();
for (const auto& keyword : aqunum_keywords) {
for (const auto& record : *keyword) {
const NumericalAquiferCell aqu_cell(record, grid, field_props);
if (cells.count(aqu_cell.global_index) > 0) {
auto error = fmt::format("Numerical aquifer cell at ({}, {}, {}) is declared more than once",
aqu_cell.I + 1, aqu_cell.J + 1, aqu_cell.K + 1);
throw OpmInputError(error, keyword->location());
} else {
this->addAquiferCell(aqu_cell);
cells.insert(aqu_cell.global_index);
}
}
}
this->addAquiferConnections(deck, grid);
}
void NumericalAquifers::addAquiferCell(const NumericalAquiferCell& aqu_cell) {
const size_t id = aqu_cell.aquifer_id;
if (!this->hasAquifer(id)) {
this->m_aquifers.insert(std::make_pair(id, SingleNumericalAquifer{id}));
}
auto& aquifer = this->m_aquifers.at(id);
aquifer.addAquiferCell(aqu_cell);
}
bool NumericalAquifers::hasAquifer(const size_t aquifer_id) const {
return (this->m_aquifers.find(aquifer_id) != this->m_aquifers.end());
}
void NumericalAquifers::addAquiferConnections(const Deck& deck, const EclipseGrid& grid) {
const auto aquifer_connections = NumericalAquiferConnection::generateConnections(deck, grid);
for (auto& pair : this->m_aquifers) {
const size_t aqu_id = pair.first;
const auto& aqu_cons = aquifer_connections.find(aqu_id);
if (aqu_cons == aquifer_connections.end()) {
const auto error = fmt::format("Numerical aquifer {} does not have any connections\n", aqu_id);
throw std::runtime_error(error);
}
auto& aquifer = pair.second;
const auto& cons = aqu_cons->second;
const auto all_aquifer_cells = this->allAquiferCells();
// For now, there is no two aquifers can be connected to one cell
// aquifer can not connect to aquifer cells
for (const auto& con : cons) {
const auto& aqu_con = con.second;
const size_t con_global_index = aqu_con.global_index;
const auto cell_iter = all_aquifer_cells.find(con_global_index);
if (cell_iter != all_aquifer_cells.end()) {
const size_t cell_aquifer_id = cell_iter->second->aquifer_id;
auto msg = fmt::format("Problem with keyword AQUCON \n"
"Aquifer connection declared at grid cell ({}, {}, {}), is a aquifer cell "
"of Aquifer {}, and will be removed",
aqu_con.I + 1, aqu_con.J + 1, aqu_con.K + 1, cell_aquifer_id);
OpmLog::warning(msg);
continue;
}
aquifer.addAquiferConnection(con.second);
}
}
}
bool NumericalAquifers::operator==(const NumericalAquifers& other) const {
return this->m_aquifers == other.m_aquifers;
}
size_t NumericalAquifers::numAquifer() const {
return this->m_aquifers.size();
}
NumericalAquifers NumericalAquifers::serializeObject() {
NumericalAquifers result;
result.m_aquifers = {{1, SingleNumericalAquifer{1}}};
return result;
}
const SingleNumericalAquifer& NumericalAquifers::getAquifer(const size_t aquifer_id) const {
const auto iter = this->m_aquifers.find(aquifer_id);
if ( iter != this->m_aquifers.end() ) {
return iter->second;
} else {
const auto msg = fmt::format(" There is no numerical aquifer {}", aquifer_id);
throw std::runtime_error(msg);
}
}
std::unordered_map<size_t, const NumericalAquiferCell*> NumericalAquifers::allAquiferCells() const {
std::unordered_map<size_t, const NumericalAquiferCell*> cells;
for (const auto& [id, aquifer] : this->m_aquifers) {
for (size_t i = 0; i < aquifer.numCells(); ++i) {
const NumericalAquiferCell* cell_ptr = aquifer.getCellPrt(i);
cells.insert(std::make_pair(cell_ptr->global_index, cell_ptr));
}
}
return cells;
}
}
@@ -0,0 +1,55 @@
/*
Copyright (C) 2020 SINTEF Digital
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/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferConnection.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/NumericalAquiferCell.hpp>
#include <opm/parser/eclipse/EclipseState/Aquifer/NumericalAquifer/SingleNumericalAquifer.hpp>
namespace Opm {
SingleNumericalAquifer::SingleNumericalAquifer(const size_t aqu_id)
: id_(aqu_id)
{
}
void SingleNumericalAquifer::addAquiferCell(const NumericalAquiferCell& aqu_cell) {
cells_.push_back(aqu_cell);
}
size_t SingleNumericalAquifer::numCells() const {
return this->cells_.size();
}
const NumericalAquiferCell* SingleNumericalAquifer::getCellPrt(const size_t index) const {
return &this->cells_[index];
}
void SingleNumericalAquifer::addAquiferConnection(const NumericalAquiferConnection& aqu_con) {
this->connections_.push_back(aqu_con);
}
bool SingleNumericalAquifer::operator==(const SingleNumericalAquifer& other) const {
return this->cells_ == other.cells_ &&
this->connections_ == other.connections_ &&
this->id_ == other.id_;
}
size_t SingleNumericalAquifer::numConnections() const {
return this->connections_.size();
}
}
@@ -69,7 +69,7 @@ namespace Opm {
if( this->runspec().phases().size() < 3 )
OpmLog::info(fmt::format("Only {} fluid phases are enabled", this->runspec().phases().size() ));
this->aquifer_config = AquiferConfig(this->m_tables, this->m_inputGrid, deck);
this->aquifer_config = AquiferConfig(this->m_tables, this->m_inputGrid, deck, this->field_props);
if (deck.hasKeyword( "TITLE" )) {
const auto& titleKeyword = deck.getKeyword( "TITLE" );
+76 -2
View File
@@ -539,12 +539,23 @@ BOOST_AUTO_TEST_CASE(Test_Aquifer_Config) {
const std::string deck_string = R"(
DIMENS
3 3 3 /
GRID
DX
27*1 /
DY
27*1 /
DZ
27*1 /
TOPS
9*1 /
PORO
27*1 /
)";
Opm::Parser parser;
Opm::Deck deck = parser.parseString(deck_string);
Opm::EclipseState ecl_state(deck);
Opm::TableManager tables;
Opm::EclipseGrid grid(10,10,10);
Opm::AquiferConfig conf(tables, grid, deck);
Opm::AquiferConfig conf(tables, ecl_state.getInputGrid(), deck, ecl_state.fieldProps());
BOOST_CHECK(!conf.active());
@@ -554,3 +565,66 @@ DIMENS
Opm::AquiferConfig conf2(fetp, ct, conn);
BOOST_CHECK( conf == conf2 );
}
inline Deck createNumericalAquiferDeck() {
const char *deckData = R"(
DIMENS
8 15 3 /
AQUDIMS
3 2 1* 1* 1* 50 1* 1* /
GRID
DX
360*10./
DY
360*10./
DZ
360*1./
TOPS
360*100./
PORO
360*0.25/
PERMX
360*1000./
PERMY
360*1000./
PERMZ
360*10./
AQUNUM
--AQnr. I J K Area Length PHI K Depth Initial.Pr PVTNUM SATNUM
1 1 1 1 1000000.0 10000 0.25 400 2585.00 285.00 1 1 /
1 3 1 1 1500000.0 20000 0.24 600 2585.00 285.00 1 1 /
1 4 1 1 2000000.0 30000 0.23 700 2585.00 285.00 1 1 /
/
AQUCON
-- Connect numerical aquifer to the reservoir
-- Id.nr I1 I2 J1 J2 K1 K2 Face Trans.mult. Trans.opt.
1 1 8 15 15 3 3 'J+' 1.00 1 /
/
)";
Parser parser;
return parser.parseString(deckData);
}
BOOST_AUTO_TEST_CASE(NumericalAquiferTest){
const Opm::Deck numaquifer_deck = createNumericalAquiferDeck();
const Opm::EclipseState ecl_state(numaquifer_deck);
const Opm::EclipseGrid& grid = ecl_state.getInputGrid();
const Opm::NumericalAquifers num_aqu{numaquifer_deck, grid, ecl_state.fieldProps()};
BOOST_CHECK(num_aqu.hasAquifer(1));
BOOST_CHECK(num_aqu.numAquifer() == 1);
const auto all_aquifer_cells = num_aqu.allAquiferCells();
BOOST_CHECK(all_aquifer_cells.count(0) > 0);
BOOST_CHECK(all_aquifer_cells.count(2) > 0);
BOOST_CHECK(all_aquifer_cells.count(3) > 0);
BOOST_CHECK(all_aquifer_cells.count(1) == 0);
const auto& aquifer = num_aqu.getAquifer(1);
BOOST_CHECK(aquifer.numCells() == 3);
BOOST_CHECK(aquifer.numConnections() == 8 );
}