Merge pull request #2095 from joakim-hove/nnc-location

Nnc location
This commit is contained in:
Joakim Hove 2020-11-13 08:26:30 +01:00 committed by GitHub
commit 54cae0f07e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 606 additions and 495 deletions

View File

@ -63,7 +63,6 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp
src/opm/parser/eclipse/EclipseState/EclipseState.cpp src/opm/parser/eclipse/EclipseState/EclipseState.cpp
src/opm/parser/eclipse/EclipseState/EndpointScaling.cpp src/opm/parser/eclipse/EclipseState/EndpointScaling.cpp
src/opm/parser/eclipse/EclipseState/Edit/EDITNNC.cpp
src/opm/parser/eclipse/EclipseState/Grid/FieldProps.cpp src/opm/parser/eclipse/EclipseState/Grid/FieldProps.cpp
src/opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.cpp src/opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.cpp
src/opm/parser/eclipse/EclipseState/Grid/Box.cpp src/opm/parser/eclipse/EclipseState/Grid/Box.cpp
@ -382,6 +381,7 @@ if(ENABLE_ECL_INPUT)
tests/parser/TuningTests.cpp tests/parser/TuningTests.cpp
tests/parser/UDQTests.cpp tests/parser/UDQTests.cpp
tests/parser/UnitTests.cpp tests/parser/UnitTests.cpp
tests/parser/integration/NNCTests.cpp
tests/parser/WellSolventTests.cpp tests/parser/WellSolventTests.cpp
tests/parser/WellTracerTests.cpp tests/parser/WellTracerTests.cpp
tests/parser/WellTests.cpp tests/parser/WellTests.cpp
@ -580,7 +580,6 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp
opm/parser/eclipse/EclipseState/Util/OrderedMap.hpp opm/parser/eclipse/EclipseState/Util/OrderedMap.hpp
opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp
opm/parser/eclipse/EclipseState/Edit/EDITNNC.hpp
opm/parser/eclipse/EclipseState/Grid/FieldData.hpp opm/parser/eclipse/EclipseState/Grid/FieldData.hpp
opm/parser/eclipse/EclipseState/Grid/Keywords.hpp opm/parser/eclipse/EclipseState/Grid/Keywords.hpp
opm/parser/eclipse/EclipseState/Grid/GridDims.hpp opm/parser/eclipse/EclipseState/Grid/GridDims.hpp

View File

@ -44,11 +44,9 @@ list(APPEND EXTRA_TESTS EclipseStateTests)
foreach (test BoxTest foreach (test BoxTest
CheckDeckValidity CheckDeckValidity
EclipseGridCreateFromDeck EclipseGridCreateFromDeck
EDITNNCTests
IncludeTest IncludeTest
IntegrationTests IntegrationTests
IOConfigIntegrationTest IOConfigIntegrationTest
NNCTests
ParseKEYWORD ParseKEYWORD
Polymer Polymer
ScheduleCreateFromDeck ScheduleCreateFromDeck

View File

@ -112,7 +112,7 @@ public:
* are not yet written to disk. * are not yet written to disk.
*/ */
void writeInitial( data::Solution simProps = data::Solution(), std::map<std::string, std::vector<int> > int_data = {}, const NNC& nnc = NNC()); void writeInitial( data::Solution simProps = data::Solution(), std::map<std::string, std::vector<int> > int_data = {}, const std::vector<NNCdata>& nnc = {});
/** /**
* \brief Overwrite the initial OIP values. * \brief Overwrite the initial OIP values.

View File

@ -24,6 +24,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
namespace Opm { namespace Opm {
class EclipseGrid; class EclipseGrid;
@ -52,7 +54,7 @@ namespace Opm { namespace InitIO {
const ::Opm::Schedule& schedule, const ::Opm::Schedule& schedule,
const ::Opm::data::Solution& simProps, const ::Opm::data::Solution& simProps,
std::map<std::string, std::vector<int>> int_data, std::map<std::string, std::vector<int>> int_data,
const ::Opm::NNC& nnc, const std::vector<::Opm::NNCdata>& nnc,
::Opm::EclIO::OutputStream::Init& initFile); ::Opm::EclIO::OutputStream::Init& initFile);
}} // namespace Opm::InitIO }} // namespace Opm::InitIO

View File

@ -27,7 +27,6 @@
#include <opm/parser/eclipse/EclipseState/AquiferConfig.hpp> #include <opm/parser/eclipse/EclipseState/AquiferConfig.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseConfig.hpp> #include <opm/parser/eclipse/EclipseState/EclipseConfig.hpp>
#include <opm/parser/eclipse/EclipseState/TracerConfig.hpp> #include <opm/parser/eclipse/EclipseState/TracerConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Edit/EDITNNC.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp> #include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FaultCollection.hpp> #include <opm/parser/eclipse/EclipseState/Grid/FaultCollection.hpp>
@ -90,11 +89,6 @@ namespace Opm {
const NNC& getInputNNC() const; const NNC& getInputNNC() const;
bool hasInputNNC() const; bool hasInputNNC() const;
/// editing non-neighboring connections
/// the non-standard adjacencies as specified in input deck
const EDITNNC& getInputEDITNNC() const;
bool hasInputEDITNNC() const;
// The potentially parallelized field properties // The potentially parallelized field properties
virtual const FieldPropsManager& fieldProps() const; virtual const FieldPropsManager& fieldProps() const;
// Always the non-parallel field properties // Always the non-parallel field properties
@ -127,7 +121,6 @@ namespace Opm {
m_eclipseConfig.serializeOp(serializer); m_eclipseConfig.serializeOp(serializer);
m_deckUnitSystem.serializeOp(serializer); m_deckUnitSystem.serializeOp(serializer);
m_inputNnc.serializeOp(serializer); m_inputNnc.serializeOp(serializer);
m_inputEditNnc.serializeOp(serializer);
m_gridDims.serializeOp(serializer); m_gridDims.serializeOp(serializer);
m_simulationConfig.serializeOp(serializer); m_simulationConfig.serializeOp(serializer);
m_transMult.serializeOp(serializer); m_transMult.serializeOp(serializer);
@ -152,9 +145,8 @@ namespace Opm {
Runspec m_runspec; Runspec m_runspec;
EclipseConfig m_eclipseConfig; EclipseConfig m_eclipseConfig;
UnitSystem m_deckUnitSystem; UnitSystem m_deckUnitSystem;
NNC m_inputNnc;
EDITNNC m_inputEditNnc;
EclipseGrid m_inputGrid; EclipseGrid m_inputGrid;
NNC m_inputNnc;
GridDims m_gridDims; GridDims m_gridDims;
FieldPropsManager field_props; FieldPropsManager field_props;
SimulationConfig m_simulationConfig; SimulationConfig m_simulationConfig;

View File

@ -1,60 +0,0 @@
/*
Copyright 2018 Equinor AS
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_COMMON_EDITNNC_HPP
#define OPM_COMMON_EDITNNC_HPP
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
namespace Opm
{
/// Represents edit information for non-neighboring connections (NNCs, faults, etc.)
class EDITNNC
{
public:
EDITNNC() = default;
/// Construct from input deck
explicit EDITNNC(const Deck& deck);
/// Returns an instance used for serialization test
static EDITNNC serializeObject();
/// \brief Get an ordered set of EDITNNC
const std::vector<NNCdata>& data() const
{
return m_editnnc;
}
/// \brief Get the number of entries
size_t size() const;
/// \brief Whether EDITNNC was empty.
bool empty() const;
bool operator==(const EDITNNC& data) const;
template<class Serializer>
void serializeOp(Serializer& serializer)
{
serializer.vector(m_editnnc);
}
private:
std::vector<NNCdata> m_editnnc;
};
}
#endif // OPM_COMMON_EDITNNC_HPP

View File

@ -89,7 +89,7 @@ namespace Opm {
size_t activeIndex(size_t i, size_t j, size_t k) const; size_t activeIndex(size_t i, size_t j, size_t k) const;
size_t activeIndex(size_t globalIndex) const; size_t activeIndex(size_t globalIndex) const;
void save(const std::string& filename, bool formatted, const Opm::NNC& nnc, const Opm::UnitSystem& units) const; void save(const std::string& filename, bool formatted, const std::vector<Opm::NNCdata>& nnc, const Opm::UnitSystem& units) const;
/* /*
Observe that the there is a getGlobalIndex(i,j,k) Observe that the there is a getGlobalIndex(i,j,k)
implementation in the base class. This method - translating implementation in the base class. This method - translating

View File

@ -22,11 +22,17 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <optional>
#include <tuple>
#include <vector> #include <vector>
#include <opm/common/OpmLog/KeywordLocation.hpp>
namespace Opm namespace Opm
{ {
class GridDims;
struct NNCdata { struct NNCdata {
NNCdata(size_t c1, size_t c2, double t) NNCdata(size_t c1, size_t c2, double t)
: cell1(c1), cell2(c2), trans(t) : cell1(c1), cell2(c2), trans(t)
@ -48,41 +54,89 @@ struct NNCdata {
serializer(trans); serializer(trans);
} }
// Observe that the operator< is only for cell ordering and does not consider the
// trans member
bool operator<(const NNCdata& other) const
{
return std::tie(this->cell1, this->cell2) < std::tie(other.cell1, other.cell2);
}
size_t cell1; size_t cell1;
size_t cell2; size_t cell2;
double trans; double trans;
}; };
class Deck;
/// Represents non-neighboring connections (non-standard adjacencies).
/// This class is essentially a directed weighted graph. class Deck;
class EclipseGrid;
/*
This class is an internalization of the NNC and EDITNNC keywords. Because the
opm-common codebase does not itself manage the simulation grid the purpose of
the NNC class is mainly to hold on to the NNC/EDITNNC input and pass it on to
the grid construction proper.
The EDITNNC keywords can operate on two different types of NNCs.
1. NNCs which have been explicitly entered using the NNC keyword.
2. NNCs which are inderectly inferred from the grid - e.g. due to faults.
When processing the EDITNNC keyword the class will search through the NNCs
configured explicitly with the NNC keyword and apply the edit transformation
on those NNCs, EDITNNCs which affect NNCs which are not configured explicitly
are stored for later use by the simulator.
The class guarantees the following ordering:
1. For all NNC / EDITNNC records we will have cell1 <= cell2
2. The vectors NNC::input() and NNC::edit() will be ordered in ascending
order.
While constructing from a deck NNCs connected to inactive cells will be
silently ignored. Do observe though that the addNNC() function does not check
the arguments and alas there is no guarantee that only active cells are
involved.
*/
class NNC class NNC
{ {
public: public:
NNC() = default; NNC() = default;
/// Construct from input deck. /// Construct from input deck.
explicit NNC(const Deck& deck); NNC(const EclipseGrid& grid, const Deck& deck);
static NNC serializeObject(); static NNC serializeObject();
void addNNC(const size_t cell1, const size_t cell2, const double trans); bool addNNC(const size_t cell1, const size_t cell2, const double trans);
const std::vector<NNCdata>& data() const { return m_nnc; } const std::vector<NNCdata>& input() const { return m_input; }
size_t numNNC() const; const std::vector<NNCdata>& edit() const { return m_edit; }
bool hasNNC() const; KeywordLocation input_location(const NNCdata& nnc) const;
KeywordLocation edit_location(const NNCdata& nnc) const;
bool operator==(const NNC& data) const; bool operator==(const NNC& data) const;
template<class Serializer> template<class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
{ {
serializer.vector(m_nnc); serializer.vector(m_input);
serializer.vector(m_edit);
serializer(m_nnc_location);
serializer(m_edit_location);
} }
private: private:
std::vector<NNCdata> m_nnc; void load_input(const EclipseGrid& grid, const Deck& deck);
void load_edit(const EclipseGrid& grid, const Deck& deck);
void add_edit(const NNCdata& edit_node);
bool update_nnc(std::size_t global_index1, std::size_t global_index2, double tran_mult);
std::vector<NNCdata> m_input;
std::vector<NNCdata> m_edit;
std::optional<KeywordLocation> m_nnc_location;
std::optional<KeywordLocation> m_edit_location;
}; };

View File

@ -10,7 +10,7 @@ namespace {
py::list getNNC( const EclipseState& state ) { py::list getNNC( const EclipseState& state ) {
py::list l; py::list l;
for( const auto& x : state.getInputNNC().data() ) for( const auto& x : state.getInputNNC().input() )
l.append( py::make_tuple( x.cell1, x.cell2, x.trans ) ); l.append( py::make_tuple( x.cell1, x.cell2, x.trans ) );
return l; return l;
} }

View File

@ -99,8 +99,8 @@ namespace Opm {
class EclipseIO::Impl { class EclipseIO::Impl {
public: public:
Impl( const EclipseState&, EclipseGrid, const Schedule&, const SummaryConfig& ); Impl( const EclipseState&, EclipseGrid, const Schedule&, const SummaryConfig& );
void writeINITFile( const data::Solution& simProps, std::map<std::string, std::vector<int> > int_data, const NNC& nnc) const; void writeINITFile( const data::Solution& simProps, std::map<std::string, std::vector<int> > int_data, const std::vector<NNCdata>& nnc) const;
void writeEGRIDFile( const NNC& nnc ); void writeEGRIDFile( const std::vector<NNCdata>& nnc );
bool wantRFTOutput( const int report_step, const bool isSubstep ) const; bool wantRFTOutput( const int report_step, const bool isSubstep ) const;
const EclipseState& es; const EclipseState& es;
@ -130,7 +130,7 @@ EclipseIO::Impl::Impl( const EclipseState& eclipseState,
void EclipseIO::Impl::writeINITFile(const data::Solution& simProps, void EclipseIO::Impl::writeINITFile(const data::Solution& simProps,
std::map<std::string, std::vector<int>> int_data, std::map<std::string, std::vector<int>> int_data,
const NNC& nnc) const const std::vector<NNCdata>& nnc) const
{ {
EclIO::OutputStream::Init initFile { EclIO::OutputStream::Init initFile {
EclIO::OutputStream::ResultSet { this->outputDir, this->baseName }, EclIO::OutputStream::ResultSet { this->outputDir, this->baseName },
@ -142,7 +142,7 @@ void EclipseIO::Impl::writeINITFile(const data::Solution& simP
} }
void EclipseIO::Impl::writeEGRIDFile( const NNC& nnc ) { void EclipseIO::Impl::writeEGRIDFile( const std::vector<NNCdata>& nnc ) {
const auto formatted = this->es.cfg().io().getFMTOUT(); const auto formatted = this->es.cfg().io().getFMTOUT();
const auto ext = '.' const auto ext = '.'
@ -168,7 +168,7 @@ int_data: Writes key(string) and integers vector to INIT file as eclipse keyword
- Key: Max 8 chars. - Key: Max 8 chars.
- Wrong input: invalid_argument exception. - Wrong input: invalid_argument exception.
*/ */
void EclipseIO::writeInitial( data::Solution simProps, std::map<std::string, std::vector<int> > int_data, const NNC& nnc) { void EclipseIO::writeInitial( data::Solution simProps, std::map<std::string, std::vector<int> > int_data, const std::vector<NNCdata>& nnc) {
if( !this->impl->output_enabled ) if( !this->impl->output_enabled )
return; return;

View File

@ -544,14 +544,14 @@ namespace {
} }
} }
void writeNonNeighbourConnections(const ::Opm::NNC& nnc, void writeNonNeighbourConnections(const std::vector<::Opm::NNCdata>& nnc,
const ::Opm::UnitSystem& units, const ::Opm::UnitSystem& units,
::Opm::EclIO::OutputStream::Init& initFile) ::Opm::EclIO::OutputStream::Init& initFile)
{ {
auto tran = std::vector<double>{}; auto tran = std::vector<double>{};
tran.reserve(nnc.numNNC()); tran.reserve(nnc.size());
for (const auto& nd : nnc.data()) { for (const auto& nd : nnc) {
tran.push_back(nd.trans); tran.push_back(nd.trans);
} }
@ -566,7 +566,7 @@ void Opm::InitIO::write(const ::Opm::EclipseState& es,
const ::Opm::Schedule& schedule, const ::Opm::Schedule& schedule,
const ::Opm::data::Solution& simProps, const ::Opm::data::Solution& simProps,
std::map<std::string, std::vector<int>> int_data, std::map<std::string, std::vector<int>> int_data,
const ::Opm::NNC& nnc, const std::vector<::Opm::NNCdata>& nnc,
::Opm::EclIO::OutputStream::Init& initFile) ::Opm::EclIO::OutputStream::Init& initFile)
{ {
const auto& units = es.getUnits(); const auto& units = es.getUnits();
@ -587,7 +587,7 @@ void Opm::InitIO::write(const ::Opm::EclipseState& es,
writeIntegerMaps(std::move(int_data), initFile); writeIntegerMaps(std::move(int_data), initFile);
writeSatFuncScaling(es, units, initFile); writeSatFuncScaling(es, units, initFile);
if (nnc.numNNC() > std::size_t{0}) { if (!nnc.empty()) {
writeNonNeighbourConnections(nnc, units, initFile); writeNonNeighbourConnections(nnc, units, initFile);
} }
} }

View File

@ -57,9 +57,8 @@ namespace Opm {
m_runspec( deck ), m_runspec( deck ),
m_eclipseConfig( deck ), m_eclipseConfig( deck ),
m_deckUnitSystem( deck.getActiveUnitSystem() ), m_deckUnitSystem( deck.getActiveUnitSystem() ),
m_inputNnc( deck ),
m_inputEditNnc( deck ),
m_inputGrid( deck, nullptr ), m_inputGrid( deck, nullptr ),
m_inputNnc( m_inputGrid, deck),
m_gridDims( deck ), m_gridDims( deck ),
field_props( deck, m_runspec.phases(), m_inputGrid, m_tables), field_props( deck, m_runspec.phases(), m_inputGrid, m_tables),
m_simulationConfig( m_eclipseConfig.getInitConfig().restartRequested(), deck, field_props), m_simulationConfig( m_eclipseConfig.getInitConfig().restartRequested(), deck, field_props),
@ -177,16 +176,9 @@ namespace Opm {
} }
bool EclipseState::hasInputNNC() const { bool EclipseState::hasInputNNC() const {
return m_inputNnc.hasNNC(); return !m_inputNnc.input().empty();
} }
const EDITNNC& EclipseState::getInputEDITNNC() const {
return m_inputEditNnc;
}
bool EclipseState::hasInputEDITNNC() const {
return !m_inputEditNnc.empty();
}
std::string EclipseState::getTitle() const { std::string EclipseState::getTitle() const {
return m_title; return m_title;
} }

View File

@ -1,101 +0,0 @@
#include <array>
#include <sstream>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/Edit/EDITNNC.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/E.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
namespace Opm
{
bool isNeighbor(const std::array<size_t, 3>& ijk1, const std::array<size_t, 3>& ijk2)
{
if ( (ijk1[0] + 1) == ijk2[0] || (ijk1[0] - 1) == ijk2[0] )
{
return ijk1[1] == ijk2[1] && ijk1[2] == ijk2[2];
}
if ( (ijk1[1] + 1) == ijk2[1] || (ijk1[1] - 1) == ijk2[1] )
{
return ijk1[0] == ijk2[0] && ijk1[2] == ijk2[2];
}
if( (ijk1[2] + 1) == ijk2[2] || (ijk1[2] - 1) == ijk2[2] )
{
return ijk1[1] == ijk2[1] && ijk1[1] == ijk2[1];
}
return false;
}
void readEditNncs(const std::vector< const DeckKeyword* >& editNncsKw, std::vector<NNCdata>& editNncs, const GridDims& gridDims)
{
for (size_t idx_nnc = 0; idx_nnc<editNncsKw.size(); ++idx_nnc) {
const auto& nnc = *editNncsKw[idx_nnc];
editNncs.reserve(editNncs.size()+nnc.size());
for (size_t i = 0; i < nnc.size(); ++i) {
std::array<size_t, 3> ijk1;
ijk1[0] = static_cast<size_t>(nnc.getRecord(i).getItem(0).get< int >(0)-1);
ijk1[1] = static_cast<size_t>(nnc.getRecord(i).getItem(1).get< int >(0)-1);
ijk1[2] = static_cast<size_t>(nnc.getRecord(i).getItem(2).get< int >(0)-1);
size_t global_index1 = gridDims.getGlobalIndex(ijk1[0],ijk1[1],ijk1[2]);
std::array<size_t, 3> ijk2;
ijk2[0] = static_cast<size_t>(nnc.getRecord(i).getItem(3).get< int >(0)-1);
ijk2[1] = static_cast<size_t>(nnc.getRecord(i).getItem(4).get< int >(0)-1);
ijk2[2] = static_cast<size_t>(nnc.getRecord(i).getItem(5).get< int >(0)-1);
size_t global_index2 = gridDims.getGlobalIndex(ijk2[0],ijk2[1],ijk2[2]);
const double trans = nnc.getRecord(i).getItem(6).get<double>(0);
using std::abs;
if ( !isNeighbor(ijk1, ijk2) )
{
editNncs.emplace_back(global_index1, global_index2, trans);
}
else
{
std::ostringstream sstr;
sstr << "Cannot edit neighboring connection from " << global_index1 <<" to "<<
global_index2<< " with EDITNNC";
Opm::OpmLog::warning(sstr.str());
}
}
}
}
EDITNNC::EDITNNC(const Deck& deck)
{
GridDims gridDims(deck);
const auto& tmpEditNncs = deck.getKeywordList<ParserKeywords::EDITNNC>();
readEditNncs(tmpEditNncs, m_editnnc, gridDims);
auto compare = [](const NNCdata& d1, const NNCdata& d2)
{ return d1.cell1 < d2.cell1 ||
( d1.cell1 == d2.cell1 && d1.cell2 < d2.cell2 );};
std::sort(m_editnnc.begin(), m_editnnc.end(), compare);
}
EDITNNC EDITNNC::serializeObject()
{
EDITNNC result;
result.m_editnnc = {{1,2,1.0},{2,3,2.0}};
return result;
}
size_t EDITNNC::size() const {
return(m_editnnc.size());
}
bool EDITNNC::empty() const {
return m_editnnc.empty();
}
bool EDITNNC::operator==(const EDITNNC& data) const {
return m_editnnc == data.m_editnnc;
}
} // namespace Opm

View File

@ -1558,7 +1558,7 @@ std::vector<double> EclipseGrid::createDVector(const std::array<int,3>& dims, st
return m_zcorn; return m_zcorn;
} }
void EclipseGrid::save(const std::string& filename, bool formatted, const Opm::NNC& nnc, const Opm::UnitSystem& units) const { void EclipseGrid::save(const std::string& filename, bool formatted, const std::vector<Opm::NNCdata>& nnc, const Opm::UnitSystem& units) const {
Opm::UnitSystem::UnitType unitSystemType = units.getType(); Opm::UnitSystem::UnitType unitSystemType = units.getType();
const auto length = ::Opm::UnitSystem::measure::length; const auto length = ::Opm::UnitSystem::measure::length;
@ -1601,7 +1601,7 @@ std::vector<double> EclipseGrid::createDVector(const std::array<int,3>& dims, st
std::vector<int> nnc1; std::vector<int> nnc1;
std::vector<int> nnc2; std::vector<int> nnc2;
for (const NNCdata& n : nnc.data() ) { for (const NNCdata& n : nnc ) {
nnc1.push_back(n.cell1 + 1); nnc1.push_back(n.cell1 + 1);
nnc2.push_back(n.cell2 + 1); nnc2.push_back(n.cell2 + 1);
} }

View File

@ -23,64 +23,218 @@
#include <opm/parser/eclipse/Deck/DeckKeyword.hpp> #include <opm/parser/eclipse/Deck/DeckKeyword.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp> #include <opm/parser/eclipse/Deck/DeckRecord.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp> #include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/E.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/N.hpp> #include <opm/parser/eclipse/Parser/ParserKeywords/N.hpp>
namespace Opm namespace Opm
{ {
NNC::NNC(const Deck& deck) {
GridDims gridDims(deck);
const auto& nncs = deck.getKeywordList<ParserKeywords::NNC>();
for (size_t idx_nnc = 0; idx_nnc<nncs.size(); ++idx_nnc) {
const auto& nnc = *nncs[idx_nnc];
for (size_t i = 0; i < nnc.size(); ++i) {
std::array<size_t, 3> ijk1;
ijk1[0] = static_cast<size_t>(nnc.getRecord(i).getItem(0).get< int >(0)-1);
ijk1[1] = static_cast<size_t>(nnc.getRecord(i).getItem(1).get< int >(0)-1);
ijk1[2] = static_cast<size_t>(nnc.getRecord(i).getItem(2).get< int >(0)-1);
size_t global_index1 = gridDims.getGlobalIndex(ijk1[0],ijk1[1],ijk1[2]);
std::array<size_t, 3> ijk2; namespace {
ijk2[0] = static_cast<size_t>(nnc.getRecord(i).getItem(3).get< int >(0)-1);
ijk2[1] = static_cast<size_t>(nnc.getRecord(i).getItem(4).get< int >(0)-1);
ijk2[2] = static_cast<size_t>(nnc.getRecord(i).getItem(5).get< int >(0)-1);
size_t global_index2 = gridDims.getGlobalIndex(ijk2[0],ijk2[1],ijk2[2]);
const double trans = nnc.getRecord(i).getItem(6).getSIDouble(0); std::optional<std::size_t> global_index(const EclipseGrid& grid, const DeckRecord& record, std::size_t item_offset) {
std::size_t i = static_cast<size_t>(record.getItem(0 + item_offset).get< int >(0)-1);
std::size_t j = static_cast<size_t>(record.getItem(1 + item_offset).get< int >(0)-1);
std::size_t k = static_cast<size_t>(record.getItem(2 + item_offset).get< int >(0)-1);
addNNC(global_index1,global_index2,trans); if (i >= grid.getNX())
} return {};
if (j >= grid.getNY())
return {};
if (k >= grid.getNZ())
return {};
if (!grid.cellActive(i,j,k))
return {};
return grid.getGlobalIndex(i,j,k);
}
std::optional<std::pair<std::size_t, std::size_t>> make_index_pair(const EclipseGrid& grid, const DeckRecord& record) {
auto g1 = global_index(grid, record, 0);
auto g2 = global_index(grid, record, 3);
if (!g1)
return {};
if (!g2)
return {};
if (*g1 < *g2)
return std::make_pair(*g1, *g2);
else
return std::make_pair(*g2, *g1);
}
bool is_neighbor(const EclipseGrid& grid, std::size_t g1, std::size_t g2) {
auto diff = g2 - g1;
if (diff == 0)
return true;
if (diff == 1)
return true;
if (diff == grid.getNX())
return true;
if (diff == grid.getNY())
return true;
return false;
}
}
NNC::NNC(const EclipseGrid& grid, const Deck& deck) {
this->load_input(grid, deck);
this->load_edit(grid, deck);
}
void NNC::load_input(const EclipseGrid& grid, const Deck& deck) {
for (const auto& keyword_ptr : deck.getKeywordList<ParserKeywords::NNC>()) {
for (const auto& record : *keyword_ptr) {
auto index_pair = make_index_pair(grid, record);
if (!index_pair)
continue;
auto [g1, g2] = index_pair.value();
double trans = record.getItem(6).getSIDouble(0);
this->m_input.emplace_back(g1, g2, trans);
}
if (!this->m_nnc_location)
this->m_nnc_location = keyword_ptr->location();
}
std::sort(this->m_input.begin(), this->m_input.end());
}
void NNC::load_edit(const EclipseGrid& grid, const Deck& deck) {
std::vector<NNCdata> nnc_edit;
for (const auto& keyword_ptr : deck.getKeywordList<ParserKeywords::EDITNNC>()) {
for (const auto& record : *keyword_ptr) {
double tran_mult = record.getItem(6).get<double>(0);
if (tran_mult == 1.0)
continue;
auto index_pair = make_index_pair(grid, record);
if (!index_pair)
continue;
auto [g1, g2] = index_pair.value();
if (is_neighbor(grid, g1, g2))
continue;
nnc_edit.emplace_back( g1, g2, tran_mult);
}
if (!this->m_edit_location)
this->m_edit_location = keyword_ptr->location();
}
std::sort(nnc_edit.begin(), nnc_edit.end());
auto current_input = this->m_input.begin();
for (const auto& current_edit : nnc_edit) {
if (current_input == this->m_input.end()) {
this->add_edit(current_edit);
continue;
}
if (current_input->cell1 != current_edit.cell1 || current_input->cell2 != current_edit.cell2) {
current_input = std::lower_bound(this->m_input.begin(),
this->m_input.end(),
NNCdata(current_edit.cell1, current_edit.cell2, 0));
if (current_input == this->m_input.end()) {
this->add_edit(current_edit);
continue;
}
}
bool edit_processed = false;
while (current_input != this->m_input.end()
&& current_input->cell1 == current_edit.cell1
&& current_input->cell2 == current_edit.cell2)
{
current_input->trans *= current_edit.trans;
++current_input;
edit_processed = true;
}
if (!edit_processed)
this->add_edit(current_edit);
} }
} }
NNC NNC::serializeObject() NNC NNC::serializeObject()
{ {
NNC result; NNC result;
result.m_nnc = {{1,2,1.0},{2,3,2.0}}; result.m_input= {{1,2,1.0},{2,3,2.0}};
result.m_edit= {{1,2,1.0},{2,3,2.0}};
result.m_nnc_location = {"NNC?", "File", 123};
result.m_edit_location = {"EDITNNC?", "File", 123};
return result; return result;
} }
void NNC::addNNC(const size_t cell1, const size_t cell2, const double trans) { bool NNC::addNNC(const size_t cell1, const size_t cell2, const double trans) {
NNCdata tmp; if (cell1 > cell2)
tmp.cell1 = cell1; return this->addNNC(cell2, cell1, trans);
tmp.cell2 = cell2;
tmp.trans = trans;
m_nnc.push_back(tmp);
}
size_t NNC::numNNC() const { auto nnc = NNCdata(cell1, cell2, trans);
return(m_nnc.size()); auto insert_iter = std::lower_bound(this->m_input.begin(), this->m_input.end(), nnc);
} this->m_input.insert( insert_iter, nnc);
return true;
bool NNC::hasNNC() const {
return m_nnc.size()>0;
} }
bool NNC::operator==(const NNC& data) const { bool NNC::operator==(const NNC& data) const {
return m_nnc == data.m_nnc; return m_input == data.m_input &&
m_edit == data.m_edit &&
m_edit_location == data.m_edit_location &&
m_nnc_location == data.m_nnc_location;
}
void NNC::add_edit(const NNCdata& edit_node) {
if (!this->m_edit.empty()) {
auto& back = this->m_edit.back();
if (back.cell1 == edit_node.cell1 && back.cell2 == edit_node.cell2) {
back.trans *= edit_node.trans;
return;
}
}
this->m_edit.push_back(edit_node);
}
/*
In principle we can have multiple NNC keywords, and to provide a good error
message we should be able to return the location of the offending NNC. That
will require some bookkeeping of which NNC originated in which NNC
keyword/location. For now we just return the location of the first NNC
keyword, but we should be ready for a more elaborate implementation without
any API change.
*/
KeywordLocation NNC::input_location([[maybe_unused]] const NNCdata& nnc) const {
if (this->m_nnc_location)
return *this->m_nnc_location;
else
return {};
}
KeywordLocation NNC::edit_location([[maybe_unused]] const NNCdata& nnc) const {
if (this->m_edit_location)
return *this->m_edit_location;
else
return {};
} }
} // namespace Opm } // namespace Opm

View File

@ -1686,7 +1686,7 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
const auto& grid1 = es.getInputGrid(); const auto& grid1 = es.getInputGrid();
Opm::NNC nnc( deck ); Opm::NNC nnc(grid1, deck);
bool formatted = false; bool formatted = false;
time_t timer; time_t timer;
@ -1702,7 +1702,7 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::filesystem::create_directory(testDir); Opm::filesystem::create_directory(testDir);
std::string fileName = testDir + "/" + "TMP.EGRID"; std::string fileName = testDir + "/" + "TMP.EGRID";
grid1.save(fileName, formatted, nnc, units); grid1.save(fileName, formatted, nnc.input(), units);
Opm::EclIO::EclFile file1(fileName); Opm::EclIO::EclFile file1(fileName);
@ -1757,13 +1757,13 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::EclipseState es2(deck2); Opm::EclipseState es2(deck2);
Opm::UnitSystem units2 = es.getDeckUnitSystem(); Opm::UnitSystem units2 = es.getDeckUnitSystem();
Opm::NNC nnc2( deck2 );
const auto& grid2 = es2.getInputGrid(); const auto& grid2 = es2.getInputGrid();
Opm::NNC nnc2(grid2, deck2 );
std::string fileName2 = testDir + "/" + "TMP2.FEGRID"; std::string fileName2 = testDir + "/" + "TMP2.FEGRID";
grid2.save(fileName2, true, nnc2, units); grid2.save(fileName2, true, nnc2.input(), units);
Opm::EclIO::EclFile file2(fileName2); Opm::EclIO::EclFile file2(fileName2);
@ -1783,13 +1783,13 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::EclipseState es3(deck3); Opm::EclipseState es3(deck3);
Opm::UnitSystem units3 = es.getDeckUnitSystem(); Opm::UnitSystem units3 = es.getDeckUnitSystem();
Opm::NNC nnc3( deck3 );
const auto& grid3 = es3.getInputGrid(); const auto& grid3 = es3.getInputGrid();
Opm::NNC nnc3(grid3, deck3);
std::string fileName3 = testDir + "/" + "TMP3.FEGRID"; std::string fileName3 = testDir + "/" + "TMP3.FEGRID";
grid3.save(fileName3, true, nnc3, units3); grid3.save(fileName3, true, nnc3.input(), units3);
Opm::EclIO::EclFile file3(fileName3); Opm::EclIO::EclFile file3(fileName3);
@ -1878,7 +1878,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
const auto length = ::Opm::UnitSystem::measure::length; const auto length = ::Opm::UnitSystem::measure::length;
const auto& grid1 = es1.getInputGrid(); const auto& grid1 = es1.getInputGrid();
Opm::NNC nnc( deck1 ); Opm::NNC nnc(grid1, deck1);
bool formatted = true; bool formatted = true;
@ -1895,7 +1895,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
Opm::filesystem::create_directory(testDir); Opm::filesystem::create_directory(testDir);
std::string fileName = testDir + "/" + "TMP.FEGRID"; std::string fileName = testDir + "/" + "TMP.FEGRID";
grid1.save(fileName, formatted, nnc, units1); grid1.save(fileName, formatted, nnc.input(), units1);
Opm::EclIO::EclFile file1(fileName); Opm::EclIO::EclFile file1(fileName);
@ -1949,7 +1949,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
BOOST_CHECK( file1.hasKey("NNCHEAD")); BOOST_CHECK( file1.hasKey("NNCHEAD"));
const std::vector<int> nnchead = file1.get<int>("NNCHEAD"); const std::vector<int> nnchead = file1.get<int>("NNCHEAD");
BOOST_CHECK( nnchead[0] == static_cast<int>(nnc.numNNC()) ); BOOST_CHECK( nnchead[0] == static_cast<int>(nnc.input().size()) );
std::vector<int> ref_nnc1 = { 6, 7, 8 }; std::vector<int> ref_nnc1 = { 6, 7, 8 };
std::vector<int> ref_nnc2 = { 26, 27, 28 }; std::vector<int> ref_nnc2 = { 26, 27, 28 };
@ -1981,7 +1981,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
std::string fileName2 = testDir + "/" + "TMP2.FEGRID"; std::string fileName2 = testDir + "/" + "TMP2.FEGRID";
grid2.save(fileName2, true, nnc, units2); grid2.save(fileName2, true, nnc.input(), units2);
Opm::EclIO::EclFile file2(fileName2); Opm::EclIO::EclFile file2(fileName2);
@ -2588,16 +2588,16 @@ BOOST_AUTO_TEST_CASE(TEST_GDFILE_2) {
Opm::UnitSystem units1a = es1a.getDeckUnitSystem(); Opm::UnitSystem units1a = es1a.getDeckUnitSystem();
const auto& grid1a = es1a.getInputGrid(); const auto& grid1a = es1a.getInputGrid();
Opm::NNC nnc( deck1a ); Opm::NNC nnc(grid1a, deck1a);
grid1a.save("BAD_CP_M.EGRID", false, nnc, units1a); grid1a.save("BAD_CP_M.EGRID", false, nnc.input(), units1a);
auto deck1b = parser.parseString( deckData1b) ; auto deck1b = parser.parseString( deckData1b) ;
Opm::EclipseState es1b( deck1b ); Opm::EclipseState es1b( deck1b );
Opm::UnitSystem units1b = es1b.getDeckUnitSystem(); Opm::UnitSystem units1b = es1b.getDeckUnitSystem();
const auto& grid1b = es1b.getInputGrid(); const auto& grid1b = es1b.getInputGrid();
grid1b.save("BAD_CP_F.EGRID", false, nnc, units1b); grid1b.save("BAD_CP_F.EGRID", false, nnc.input(), units1b);
auto deck1 = parser.parseString( deckData1) ; auto deck1 = parser.parseString( deckData1) ;
Opm::EclipseGrid grid1( deck1); Opm::EclipseGrid grid1( deck1);

View File

@ -1,43 +0,0 @@
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /
EDIT
EDITNNC
3 1 1 1 1 1 0.1 /
1 1 1 1 2 1 0.01 /
2 1 1 2 3 1 0.1 /
/
EDITNNC
1 1 1 2 1 1 0.1 /
2 1 1 2 3 1 0.1 /
/

View File

@ -1,40 +0,0 @@
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /
NNC
1 1 1 2 1 1 0.5 /
1 1 1 1 2 1 1.0 /
/
NNC
1 1 1 2 1 1 0.5 /
1 2 1 1 2 1 1.0 /
/

View File

@ -1,30 +0,0 @@
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /

View File

@ -1,69 +0,0 @@
/*
Copyright 2015 IRIS
Copyright 2018 Equinor AS
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/Edit/EDITNNC.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/Units/Units.hpp>
#define BOOST_TEST_MODULE NNCTests
#include <boost/test/unit_test.hpp>
using namespace Opm;
inline std::string pathprefix() {
return boost::unit_test::framework::master_test_suite().argv[1];
}
BOOST_AUTO_TEST_CASE(noNNC)
{
Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/noNNC.DATA");
EclipseState eclipseState(deck);
const auto& editnnc = eclipseState.getInputEDITNNC();
BOOST_CHECK(!eclipseState.hasInputEDITNNC());
BOOST_CHECK(editnnc.empty());
}
BOOST_AUTO_TEST_CASE(readDeck)
{
Parser parser;
auto deck = parser.parseFile(pathprefix() + "EDITNNC/EDITNNC.DATA");
EclipseState eclipseState(deck);
const auto& editnnc = eclipseState.getInputEDITNNC();
BOOST_CHECK(!editnnc.empty());
const std::vector<NNCdata>& data = editnnc.data();
// test the NNCs in nnc.DATA
BOOST_CHECK_EQUAL(editnnc.size(), 3); //neighbouring connections in EDITNNC are ignored
BOOST_CHECK_EQUAL(data[0].cell1, 1);
BOOST_CHECK_EQUAL(data[0].cell2, 21);
BOOST_CHECK_EQUAL(data[0].trans, 0.1);
BOOST_CHECK_EQUAL(data[1].cell1, 1);
BOOST_CHECK_EQUAL(data[1].cell2, 21);
BOOST_CHECK_EQUAL(data[1].trans, 0.1);
BOOST_CHECK_EQUAL(data[2].cell1, 2);
BOOST_CHECK_EQUAL(data[2].cell2, 0);
BOOST_CHECK_EQUAL(data[2].trans, 0.1);
}

View File

@ -20,7 +20,7 @@
#include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp> #include <opm/parser/eclipse/EclipseState/Grid/NNC.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp> #include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp> #include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp> #include <opm/parser/eclipse/EclipseState/Grid/GridDims.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp> #include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/Units/Units.hpp> #include <opm/parser/eclipse/Units/Units.hpp>
@ -30,67 +30,330 @@
using namespace Opm; using namespace Opm;
inline std::string pathprefix() {
return boost::unit_test::framework::master_test_suite().argv[1]; const std::string no_nnc_input = R"(
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /
)";
const std::string actnum = R"(
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
ACTNUM
10*0 90*1
/
NNC
1 1 1 1 3 1 0.5 / -- Inactive cell
3 1 1 3 3 1 0.5 / -- Inactive cell
2 2 1 3 3 1 1.0 / -- Valid
100 100 100 200 200 200 1.0 / -- Very invalid
/
PORO
100*0.15 /
EDIT
EDITNNC
5 1 1 1 5 3 0.5 / -- Inactive cell
2 2 1 3 3 1 0.5 / -- Valid - coupled to NNC
4 4 1 5 5 1 2.0 / -- Valid
-1 4 4 -1 7 7 1.0 / -- Very invalid
/
)";
const std::string nnc_input = R"(
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /
NNC
1 1 1 2 1 1 0.5 /
1 1 1 1 2 1 1.0 /
/
NNC
1 1 1 2 1 1 0.5 /
1 2 1 1 2 1 1.0 /
/
)";
const std::string editnnc_input = R"(
RUNSPEC
OIL
GAS
WATER
DIMENS
10 10 1 /
GRID
NNC
7 1 1 3 1 1 0.1 /
3 1 1 5 1 1 0.1 /
/
DXV
10*1000.0
/
DYV
10*1000.0
/
DZ
100*20.0
/
TOPS
100*10
/
PORO
100*0.15 /
EDIT
EDITNNC
5 1 1 3 1 1 2.0 /
3 1 1 1 1 1 0.1 /
1 1 1 1 2 1 0.01 / -- This is ignored because the two cells are ijk neighbours
2 1 1 2 3 1 0.2 /
/
EDITNNC
1 1 1 2 1 1 0.1 / -- Ignored
2 1 1 2 3 1 0.3 /
/
)";
std::optional<NNCdata> find_nnc(const std::vector<NNCdata>& v, std::size_t c1, std::size_t c2) {
if (c1 > c2)
return find_nnc(v, c2, c1);
auto iter = std::find_if(v.begin(), v.end(), [c1,c2](const NNCdata& nnc) { return nnc.cell1 == c1 && nnc.cell2 == c2; });
if (iter != v.end())
return *iter;
return {};
} }
void check_edit_nnc(const std::vector<NNCdata>& v, std::size_t c1, std::size_t c2, double t) {
const auto& nnc = find_nnc(v, c1, c2);
BOOST_REQUIRE(nnc.has_value());
BOOST_CHECK_CLOSE(nnc->trans, t , 1e-6);
}
void check_nnc(const std::vector<NNCdata>& v, std::size_t c1, std::size_t c2, double t) {
check_edit_nnc(v, c1, c2, t*Opm::Metric::Transmissibility);
}
bool has_nnc(const std::vector<NNCdata>& v, std::size_t c1, std::size_t c2) {
const auto& nnc = find_nnc(v, c1, c2);
return nnc.has_value();
}
void check_order(const std::vector<NNCdata>& d) {
for (const auto& nnc : d)
BOOST_CHECK(nnc.cell1 <= nnc.cell2);
if (d.size() <= 1)
return;
for (std::size_t index=1; index < d.size(); index++) {
const auto& nnc1 = d[index - 1];
const auto& nnc2 = d[index];
if (nnc1 < nnc2)
continue;
BOOST_CHECK_EQUAL(nnc1.cell1, nnc2.cell1);
BOOST_CHECK_EQUAL(nnc1.cell2, nnc2.cell2);
}
}
void check_order(const NNC& nnc) {
check_order(nnc.input());
check_order(nnc.edit());
}
BOOST_AUTO_TEST_CASE(noNNC) BOOST_AUTO_TEST_CASE(noNNC)
{ {
Parser parser; Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/noNNC.DATA"); auto deck = parser.parseString(no_nnc_input);
EclipseState eclipseState(deck); EclipseState eclipseState(deck);
const auto& nnc = eclipseState.getInputNNC(); const auto& nnc = eclipseState.getInputNNC();
check_order(nnc);
BOOST_CHECK(!eclipseState.hasInputNNC()); BOOST_CHECK(!eclipseState.hasInputNNC());
BOOST_CHECK(!nnc.hasNNC()); BOOST_CHECK(nnc.input().empty());
} }
BOOST_AUTO_TEST_CASE(readDeck) BOOST_AUTO_TEST_CASE(readDeck)
{ {
Parser parser; Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/NNC.DATA"); auto deck = parser.parseString(nnc_input);
EclipseState eclipseState(deck); EclipseState eclipseState(deck);
const auto& grid = eclipseState.getInputGrid();
const auto& nnc = eclipseState.getInputNNC(); const auto& nnc = eclipseState.getInputNNC();
BOOST_CHECK(nnc.hasNNC()); check_order(nnc);
const std::vector<NNCdata>& nncdata = nnc.data(); BOOST_CHECK(!nnc.input().empty());
const std::vector<NNCdata>& nncdata = nnc.input();
// test the NNCs in nnc.DATA // test the NNCs in nnc.DATA
BOOST_CHECK_EQUAL(nnc.numNNC(), 4); BOOST_CHECK_EQUAL(nncdata.size(), 4);
BOOST_CHECK_EQUAL(nncdata[0].cell1, 0); check_nnc(nncdata, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(1,0,0), 0.50);
BOOST_CHECK_EQUAL(nncdata[0].cell2, 1); check_nnc(nncdata, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(0,1,0), 1.00);
BOOST_CHECK_EQUAL(nncdata[0].trans, 0.5 * Opm::Metric::Transmissibility);
BOOST_CHECK_EQUAL(nncdata[1].cell1, 0);
BOOST_CHECK_EQUAL(nncdata[1].cell2, 10);
BOOST_CHECK_EQUAL(nncdata[1].trans, 1.0 * Opm::Metric::Transmissibility);
const auto& loc = nnc.input_location( nncdata[0] );
BOOST_CHECK_EQUAL(loc.keyword, "NNC");
} }
BOOST_AUTO_TEST_CASE(addNNCfromDeck)
BOOST_AUTO_TEST_CASE(noNNC_EDIT)
{ {
Parser parser; Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/NNC.DATA"); auto deck = parser.parseString(no_nnc_input);
EclipseState eclipseState(deck); EclipseState eclipseState(deck);
auto nnc = eclipseState.getInputNNC(); const auto& editnnc = eclipseState.getInputNNC();
BOOST_CHECK(nnc.hasNNC()); BOOST_CHECK(editnnc.edit().empty());
const std::vector<NNCdata>& nncdata = nnc.data();
BOOST_CHECK_EQUAL(nnc.numNNC(), 4);
// test add NNC
nnc.addNNC(2, 2, 2.0);
BOOST_CHECK_EQUAL(nnc.numNNC(), 5);
BOOST_CHECK_EQUAL(nncdata[4].cell1, 2);
BOOST_CHECK_EQUAL(nncdata[4].cell2, 2);
BOOST_CHECK_EQUAL(nncdata[4].trans, 2.0);
} }
BOOST_AUTO_TEST_CASE(addNNC)
BOOST_AUTO_TEST_CASE(readDeck_EDIT)
{ {
Opm::NNC nnc; Parser parser;
// add NNC auto deck = parser.parseString(editnnc_input);
nnc.addNNC(2,2,2.0); EclipseGrid grid(10,10,10);
const std::vector<NNCdata>& nncdata = nnc.data();
BOOST_CHECK_EQUAL(nnc.numNNC(), 1); NNC nnc(grid, deck);
BOOST_CHECK_EQUAL(nncdata[0].cell1, 2); const std::vector<NNCdata>& data = nnc.edit();
BOOST_CHECK_EQUAL(nncdata[0].cell1, 2);
BOOST_CHECK_EQUAL(nncdata[0].trans, 2.0); BOOST_CHECK_EQUAL(data.size(), 2); //neighbouring connections in EDITNNC are ignored
BOOST_CHECK(!has_nnc(data, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(0,1,0)));
BOOST_CHECK(!has_nnc(data, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(1,0,0)));
check_order(nnc);
check_edit_nnc(data, grid.getGlobalIndex(2,0,0), grid.getGlobalIndex(0,0,0), 0.1);
check_edit_nnc(data, grid.getGlobalIndex(1,0,0), grid.getGlobalIndex(1,2,0), 0.06);
const std::vector<NNCdata>& input = nnc.input();
check_nnc(input, grid.getGlobalIndex(2,0,0), grid.getGlobalIndex(4,0,0), 0.20);
check_nnc(input, grid.getGlobalIndex(2,0,0), grid.getGlobalIndex(6,0,0), 0.10);
} }
BOOST_AUTO_TEST_CASE(ACTNUM)
{
Parser parser;
auto deck = parser.parseString(actnum);
EclipseState eclipseState(deck);
const auto& grid = eclipseState.getInputGrid();
const auto& editnnc = eclipseState.getInputNNC();
const auto& edit = editnnc.edit();
const auto& input = editnnc.input();
BOOST_CHECK_EQUAL(edit.size(), 1);
BOOST_CHECK_EQUAL(input.size(), 1);
check_nnc(input, grid.getGlobalIndex(1,1,0), grid.getGlobalIndex(2,2,0), 0.5);
check_edit_nnc(edit, grid.getGlobalIndex(3,3,0), grid.getGlobalIndex(4,4,0), 2.0);
check_order(editnnc);
}