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/EclipseState.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/FieldPropsManager.cpp
src/opm/parser/eclipse/EclipseState/Grid/Box.cpp
@ -382,6 +381,7 @@ if(ENABLE_ECL_INPUT)
tests/parser/TuningTests.cpp
tests/parser/UDQTests.cpp
tests/parser/UnitTests.cpp
tests/parser/integration/NNCTests.cpp
tests/parser/WellSolventTests.cpp
tests/parser/WellTracerTests.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/OrderedMap.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/Keywords.hpp
opm/parser/eclipse/EclipseState/Grid/GridDims.hpp

View File

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

View File

@ -112,7 +112,7 @@ public:
* 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.

View File

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

View File

@ -27,7 +27,6 @@
#include <opm/parser/eclipse/EclipseState/AquiferConfig.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseConfig.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/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FaultCollection.hpp>
@ -78,7 +77,7 @@ namespace Opm {
const InitConfig& getInitConfig() const;
InitConfig& getInitConfig();
const SimulationConfig& getSimulationConfig() const;
virtual const EclipseGrid& getInputGrid() const;
@ -90,11 +89,6 @@ namespace Opm {
const NNC& getInputNNC() 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
virtual const FieldPropsManager& fieldProps() const;
// Always the non-parallel field properties
@ -127,7 +121,6 @@ namespace Opm {
m_eclipseConfig.serializeOp(serializer);
m_deckUnitSystem.serializeOp(serializer);
m_inputNnc.serializeOp(serializer);
m_inputEditNnc.serializeOp(serializer);
m_gridDims.serializeOp(serializer);
m_simulationConfig.serializeOp(serializer);
m_transMult.serializeOp(serializer);
@ -152,9 +145,8 @@ namespace Opm {
Runspec m_runspec;
EclipseConfig m_eclipseConfig;
UnitSystem m_deckUnitSystem;
NNC m_inputNnc;
EDITNNC m_inputEditNnc;
EclipseGrid m_inputGrid;
NNC m_inputNnc;
GridDims m_gridDims;
FieldPropsManager field_props;
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 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)
implementation in the base class. This method - translating

View File

@ -1,18 +1,18 @@
/*
Copyright 2015 IRIS
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/>.
*/
@ -22,11 +22,17 @@
#include <cstddef>
#include <memory>
#include <optional>
#include <tuple>
#include <vector>
#include <opm/common/OpmLog/KeywordLocation.hpp>
namespace Opm
{
class GridDims;
struct NNCdata {
NNCdata(size_t c1, size_t c2, double t)
: cell1(c1), cell2(c2), trans(t)
@ -48,41 +54,89 @@ struct NNCdata {
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 cell2;
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
{
public:
NNC() = default;
/// Construct from input deck.
explicit NNC(const Deck& deck);
NNC(const EclipseGrid& grid, const Deck& deck);
static NNC serializeObject();
void addNNC(const size_t cell1, const size_t cell2, const double trans);
const std::vector<NNCdata>& data() const { return m_nnc; }
size_t numNNC() const;
bool hasNNC() const;
bool addNNC(const size_t cell1, const size_t cell2, const double trans);
const std::vector<NNCdata>& input() const { return m_input; }
const std::vector<NNCdata>& edit() const { return m_edit; }
KeywordLocation input_location(const NNCdata& nnc) const;
KeywordLocation edit_location(const NNCdata& nnc) const;
bool operator==(const NNC& data) const;
template<class 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:
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 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 ) );
return l;
}

View File

@ -99,8 +99,8 @@ namespace Opm {
class EclipseIO::Impl {
public:
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 writeEGRIDFile( const NNC& nnc );
void writeINITFile( const data::Solution& simProps, std::map<std::string, std::vector<int> > int_data, const std::vector<NNCdata>& nnc) const;
void writeEGRIDFile( const std::vector<NNCdata>& nnc );
bool wantRFTOutput( const int report_step, const bool isSubstep ) const;
const EclipseState& es;
@ -130,7 +130,7 @@ EclipseIO::Impl::Impl( const EclipseState& eclipseState,
void EclipseIO::Impl::writeINITFile(const data::Solution& simProps,
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::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 ext = '.'
@ -168,7 +168,7 @@ int_data: Writes key(string) and integers vector to INIT file as eclipse keyword
- Key: Max 8 chars.
- 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 )
return;

View File

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

View File

@ -57,9 +57,8 @@ namespace Opm {
m_runspec( deck ),
m_eclipseConfig( deck ),
m_deckUnitSystem( deck.getActiveUnitSystem() ),
m_inputNnc( deck ),
m_inputEditNnc( deck ),
m_inputGrid( deck, nullptr ),
m_inputNnc( m_inputGrid, deck),
m_gridDims( deck ),
field_props( deck, m_runspec.phases(), m_inputGrid, m_tables),
m_simulationConfig( m_eclipseConfig.getInitConfig().restartRequested(), deck, field_props),
@ -177,16 +176,9 @@ namespace Opm {
}
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 {
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;
}
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();
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> nnc2;
for (const NNCdata& n : nnc.data() ) {
for (const NNCdata& n : nnc ) {
nnc1.push_back(n.cell1 + 1);
nnc2.push_back(n.cell2 + 1);
}

View File

@ -1,18 +1,18 @@
/*
Copyright 2015 IRIS
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/>.
*/
@ -23,64 +23,218 @@
#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/Grid/NNC.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/E.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/N.hpp>
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;
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);
addNNC(global_index1,global_index2,trans);
namespace {
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);
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 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;
}
void NNC::addNNC(const size_t cell1, const size_t cell2, const double trans) {
NNCdata tmp;
tmp.cell1 = cell1;
tmp.cell2 = cell2;
tmp.trans = trans;
m_nnc.push_back(tmp);
}
bool NNC::addNNC(const size_t cell1, const size_t cell2, const double trans) {
if (cell1 > cell2)
return this->addNNC(cell2, cell1, trans);
size_t NNC::numNNC() const {
return(m_nnc.size());
}
bool NNC::hasNNC() const {
return m_nnc.size()>0;
auto nnc = NNCdata(cell1, cell2, trans);
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::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

View File

@ -1686,7 +1686,7 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
const auto& grid1 = es.getInputGrid();
Opm::NNC nnc( deck );
Opm::NNC nnc(grid1, deck);
bool formatted = false;
time_t timer;
@ -1702,7 +1702,7 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::filesystem::create_directory(testDir);
std::string fileName = testDir + "/" + "TMP.EGRID";
grid1.save(fileName, formatted, nnc, units);
grid1.save(fileName, formatted, nnc.input(), units);
Opm::EclIO::EclFile file1(fileName);
@ -1757,13 +1757,13 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::EclipseState es2(deck2);
Opm::UnitSystem units2 = es.getDeckUnitSystem();
Opm::NNC nnc2( deck2 );
const auto& grid2 = es2.getInputGrid();
Opm::NNC nnc2(grid2, deck2 );
std::string fileName2 = testDir + "/" + "TMP2.FEGRID";
grid2.save(fileName2, true, nnc2, units);
grid2.save(fileName2, true, nnc2.input(), units);
Opm::EclIO::EclFile file2(fileName2);
@ -1783,13 +1783,13 @@ BOOST_AUTO_TEST_CASE(SAVE_FIELD_UNITS) {
Opm::EclipseState es3(deck3);
Opm::UnitSystem units3 = es.getDeckUnitSystem();
Opm::NNC nnc3( deck3 );
const auto& grid3 = es3.getInputGrid();
Opm::NNC nnc3(grid3, deck3);
std::string fileName3 = testDir + "/" + "TMP3.FEGRID";
grid3.save(fileName3, true, nnc3, units3);
grid3.save(fileName3, true, nnc3.input(), units3);
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& grid1 = es1.getInputGrid();
Opm::NNC nnc( deck1 );
Opm::NNC nnc(grid1, deck1);
bool formatted = true;
@ -1895,7 +1895,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
Opm::filesystem::create_directory(testDir);
std::string fileName = testDir + "/" + "TMP.FEGRID";
grid1.save(fileName, formatted, nnc, units1);
grid1.save(fileName, formatted, nnc.input(), units1);
Opm::EclIO::EclFile file1(fileName);
@ -1949,7 +1949,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
BOOST_CHECK( file1.hasKey("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_nnc2 = { 26, 27, 28 };
@ -1981,7 +1981,7 @@ BOOST_AUTO_TEST_CASE(SAVE_METRIC_UNITS) {
std::string fileName2 = testDir + "/" + "TMP2.FEGRID";
grid2.save(fileName2, true, nnc, units2);
grid2.save(fileName2, true, nnc.input(), units2);
Opm::EclIO::EclFile file2(fileName2);
@ -2588,16 +2588,16 @@ BOOST_AUTO_TEST_CASE(TEST_GDFILE_2) {
Opm::UnitSystem units1a = es1a.getDeckUnitSystem();
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) ;
Opm::EclipseState es1b( deck1b );
Opm::UnitSystem units1b = es1b.getDeckUnitSystem();
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) ;
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/Parser/Parser.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/Units/Units.hpp>
@ -30,67 +30,330 @@
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)
{
Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/noNNC.DATA");
auto deck = parser.parseString(no_nnc_input);
EclipseState eclipseState(deck);
const auto& nnc = eclipseState.getInputNNC();
check_order(nnc);
BOOST_CHECK(!eclipseState.hasInputNNC());
BOOST_CHECK(!nnc.hasNNC());
BOOST_CHECK(nnc.input().empty());
}
BOOST_AUTO_TEST_CASE(readDeck)
{
Parser parser;
auto deck = parser.parseFile(pathprefix() + "NNC/NNC.DATA");
auto deck = parser.parseString(nnc_input);
EclipseState eclipseState(deck);
const auto& grid = eclipseState.getInputGrid();
const auto& nnc = eclipseState.getInputNNC();
BOOST_CHECK(nnc.hasNNC());
const std::vector<NNCdata>& nncdata = nnc.data();
check_order(nnc);
BOOST_CHECK(!nnc.input().empty());
const std::vector<NNCdata>& nncdata = nnc.input();
// test the NNCs in nnc.DATA
BOOST_CHECK_EQUAL(nnc.numNNC(), 4);
BOOST_CHECK_EQUAL(nncdata[0].cell1, 0);
BOOST_CHECK_EQUAL(nncdata[0].cell2, 1);
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);
BOOST_CHECK_EQUAL(nncdata.size(), 4);
check_nnc(nncdata, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(1,0,0), 0.50);
check_nnc(nncdata, grid.getGlobalIndex(0,0,0), grid.getGlobalIndex(0,1,0), 1.00);
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;
auto deck = parser.parseFile(pathprefix() + "NNC/NNC.DATA");
auto deck = parser.parseString(no_nnc_input);
EclipseState eclipseState(deck);
auto nnc = eclipseState.getInputNNC();
BOOST_CHECK(nnc.hasNNC());
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);
const auto& editnnc = eclipseState.getInputNNC();
BOOST_CHECK(editnnc.edit().empty());
}
BOOST_AUTO_TEST_CASE(addNNC)
BOOST_AUTO_TEST_CASE(readDeck_EDIT)
{
Opm::NNC nnc;
// add NNC
nnc.addNNC(2,2,2.0);
const std::vector<NNCdata>& nncdata = nnc.data();
BOOST_CHECK_EQUAL(nnc.numNNC(), 1);
BOOST_CHECK_EQUAL(nncdata[0].cell1, 2);
BOOST_CHECK_EQUAL(nncdata[0].cell1, 2);
BOOST_CHECK_EQUAL(nncdata[0].trans, 2.0);
Parser parser;
auto deck = parser.parseString(editnnc_input);
EclipseGrid grid(10,10,10);
NNC nnc(grid, deck);
const std::vector<NNCdata>& data = nnc.edit();
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);
}