Enable flow diagnostics for grids imported with opm-common

* Update flowdiag-app submodule with grid source abstraction
* Add opm-common grid source for flow diagnostics
* Enable flow diagnostics for grids imported with opm-common
* Test flow graph from opm-common matches resdata
* Test flow diagnostics results are equal for all grid readers
* Document EGrid constructor behavior in flow diagnostics grid source
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-10-13 12:43:01 +02:00
1 parent 5259663a20
commit e217ffb5c6
8 files changed
+497 -4

No files matched your search

@@ -86,6 +86,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RifStimPlanCsvSummaryReader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommonActive.cpp
${CMAKE_CURRENT_LIST_DIR}/RifOpmCommonFlowDiagGridSource.cpp
${CMAKE_CURRENT_LIST_DIR}/RifInpExportTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEdfmTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RifFaultReactivationModelExporter.cpp
@@ -0,0 +1,170 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RifOpmCommonFlowDiagGridSource.h"
#include "RiaLogging.h"
#include "opm/io/eclipse/EGrid.hpp"
#include <algorithm>
#include <tuple>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifOpmCommonFlowDiagGridSource::RifOpmCommonFlowDiagGridSource( const std::string& gridFileName )
{
// The EGrid constructor only indexes the file and reads small topology arrays (GRIDHEAD, ACTNUM, NNC, LGR info). COORD and ZCORN are
// loaded lazily on the first geometry access, which is never done here.
m_grids.push_back( std::make_unique<Opm::EclIO::EGrid>( gridFileName ) );
m_lgrNames = m_grids.front()->list_of_lgrs();
m_lgrParents = m_grids.front()->list_of_lgr_parents();
for ( const auto& lgrName : m_lgrNames )
{
m_grids.push_back( std::make_unique<Opm::EclIO::EGrid>( gridFileName, lgrName ) );
}
if ( m_grids.front()->hasKey( "NNA1" ) )
{
RiaLogging::warning( "Flow diagnostics: NNCs between local grids are not supported for opm-common grid import, and are ignored." );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifOpmCommonFlowDiagGridSource::~RifOpmCommonFlowDiagGridSource() = default;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RifOpmCommonFlowDiagGridSource::numGrids() const
{
return static_cast<int>( m_grids.size() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::string RifOpmCommonFlowDiagGridSource::gridName( int gridID ) const
{
if ( gridID == 0 ) return "";
return m_lgrNames[gridID - 1];
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::array<int, 3> RifOpmCommonFlowDiagGridSource::dimensions( int gridID ) const
{
return m_grids[gridID]->dimension();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<int> RifOpmCommonFlowDiagGridSource::activeToGlobal( int gridID ) const
{
// EGrid marks matrix-inactive cells with -1, also for dual porosity, matching resdata's matrix active cells
const auto& activeIndexes = m_grids[gridID]->active_indexes();
std::vector<int> globalIndexes;
for ( int globalIndex = 0; globalIndex < static_cast<int>( activeIndexes.size() ); globalIndex++ )
{
if ( activeIndexes[globalIndex] >= 0 ) globalIndexes.push_back( globalIndex );
}
return globalIndexes;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<bool> RifOpmCommonFlowDiagGridSource::activeIsSubdivided( int gridID ) const
{
const auto dims = dimensions( gridID );
std::vector<bool> isGlobalCellSubdivided( static_cast<size_t>( dims[0] ) * dims[1] * dims[2], false );
for ( int lgrIndex = 0; lgrIndex < static_cast<int>( m_lgrNames.size() ); lgrIndex++ )
{
if ( parentGridId( lgrIndex ) != gridID ) continue;
for ( int hostCell : m_grids[lgrIndex + 1]->hostCellsGlobalIndex() )
{
if ( hostCell >= 0 && hostCell < static_cast<int>( isGlobalCellSubdivided.size() ) ) isGlobalCellSubdivided[hostCell] = true;
}
}
const auto globalIndexes = activeToGlobal( gridID );
std::vector<bool> isSubdivided( globalIndexes.size() );
for ( size_t activeIndex = 0; activeIndex < globalIndexes.size(); activeIndex++ )
{
isSubdivided[activeIndex] = isGlobalCellSubdivided[globalIndexes[activeIndex]];
}
return isSubdivided;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<Opm::ECLGraphNNC> RifOpmCommonFlowDiagGridSource::nncs() const
{
std::vector<Opm::ECLGraphNNC> nncs;
for ( int gridID = 0; gridID < numGrids(); gridID++ )
{
// nnc_connections() lists NNC1/NNC2 (TRANNNC) followed by NNCG/NNCL (TRANGL). The input index is the position
// within each of these arrays, as used by resdata to map NNC flux vectors in restart files.
int nncIndex = 0;
int globalNncIndex = 0;
for ( const auto& c : m_grids[gridID]->nnc_connections( gridID ) )
{
const bool isGlobalLocal = c.grid1_Id != gridID;
const int inputIndex = isGlobalLocal ? globalNncIndex++ : nncIndex++;
nncs.push_back( { c.grid1_Id, c.grid2_Id, c.grid1_CellIdx - 1, c.grid2_CellIdx - 1, c.transValue, inputIndex } );
}
}
// Same ordering as resdata's ecl_nnc_export()
std::sort( nncs.begin(),
nncs.end(),
[]( const Opm::ECLGraphNNC& a, const Opm::ECLGraphNNC& b )
{ return std::tie( a.grid1, a.grid2, a.glob1, a.glob2 ) < std::tie( b.grid1, b.grid2, b.glob1, b.glob2 ); } );
return nncs;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RifOpmCommonFlowDiagGridSource::parentGridId( int lgrIndex ) const
{
if ( lgrIndex >= static_cast<int>( m_lgrParents.size() ) ) return 0;
auto it = std::find( m_lgrNames.begin(), m_lgrNames.end(), m_lgrParents[lgrIndex] );
if ( it == m_lgrNames.end() ) return 0;
return static_cast<int>( std::distance( m_lgrNames.begin(), it ) ) + 1;
}
@@ -0,0 +1,58 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "opm/utility/ECLGraphGridSource.hpp"
#include <memory>
#include <string>
#include <vector>
namespace Opm::EclIO
{
class EGrid;
}
//==================================================================================================
// Grid topology for flow diagnostics read directly from EGRID using opm-common. Reads only grid
// headers, ACTNUM, LGR host cells and NNC arrays, never cell geometry, so it works the same for
// full and active-cells-only grid import.
//==================================================================================================
class RifOpmCommonFlowDiagGridSource : public Opm::ECLGraphGridSource
{
public:
explicit RifOpmCommonFlowDiagGridSource( const std::string& gridFileName );
~RifOpmCommonFlowDiagGridSource() override;
int numGrids() const override;
std::string gridName( int gridID ) const override;
std::array<int, 3> dimensions( int gridID ) const override;
std::vector<int> activeToGlobal( int gridID ) const override;
std::vector<bool> activeIsSubdivided( int gridID ) const override;
std::vector<Opm::ECLGraphNNC> nncs() const override;
private:
int parentGridId( int lgrIndex ) const;
private:
// Main grid followed by LGRs, in EGRID order
std::vector<std::unique_ptr<Opm::EclIO::EGrid>> m_grids;
std::vector<std::string> m_lgrNames;
std::vector<std::string> m_lgrParents;
};
@@ -23,8 +23,10 @@
#include "RiuMessageDialog.h"
#include "RifEclipseOutputFileTools.h"
#include "RifOpmCommonFlowDiagGridSource.h"
#include "RifReaderEclipseOutput.h"
#include "RifReaderInterface.h"
#include "RifReaderOpmCommon.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
@@ -47,14 +49,25 @@
class RigOpmFlowDiagStaticData
{
public:
RigOpmFlowDiagStaticData( const ecl_grid_type* mainGrid, const std::wstring& initFilename, RiaDefines::EclipseUnitSystem caseUnitSystem )
RigOpmFlowDiagStaticData( const ecl_grid_type* mainGrid,
const Opm::ECLGraphGridSource* gridSource,
const std::wstring& initFilename,
RiaDefines::EclipseUnitSystem caseUnitSystem )
: m_initData( initFilename )
{
try
{
if ( mainGrid )
{
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( mainGrid, m_initData ) );
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( mainGrid, m_initData ) );
}
else if ( gridSource )
{
m_eclGraph = std::make_unique<Opm::ECLGraph>( Opm::ECLGraph::load( *gridSource, m_initData ) );
}
if ( m_eclGraph )
{
m_poreVolume = m_eclGraph->poreVolume();
}
@@ -447,6 +460,8 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
auto fileReader = eclipseCaseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->readerInterface();
ecl_grid_type* mainGrid = nullptr;
std::unique_ptr<RifOpmCommonFlowDiagGridSource> opmGridSource;
if ( auto eclOutput = dynamic_cast<const RifReaderEclipseOutput*>( fileReader ) )
{
mainGrid = eclOutput->loadAllGrids();
@@ -455,11 +470,23 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
return false;
}
}
else if ( dynamic_cast<const RifReaderOpmCommon*>( fileReader ) )
{
try
{
opmGridSource = std::make_unique<RifOpmCommonFlowDiagGridSource>( m_eclipseCase->gridFileName().toStdString() );
}
catch ( const std::exception& e )
{
RiaLogging::warning( std::string( "Flow diagnostics: Could not read grid topology. " ) + e.what() );
}
}
RiaDefines::EclipseUnitSystem caseUnitSystem = eclipseCaseData->unitsType();
try
{
m_opmFlowDiagStaticData = std::make_unique<RigOpmFlowDiagStaticData>( mainGrid, initFileName, caseUnitSystem );
m_opmFlowDiagStaticData =
std::make_unique<RigOpmFlowDiagStaticData>( mainGrid, opmGridSource.get(), initFileName, caseUnitSystem );
}
catch ( const std::exception& e )
{
@@ -470,6 +497,22 @@ bool RigFlowDiagSolverInterface::ensureStaticDataObjectInstanceCreated()
{
ecl_grid_free( mainGrid );
}
// Flow diagnostics results are indexed by the graph's cell index, assumed equal to the active cell result index
auto* eclGraphOwner = m_opmFlowDiagStaticData ? &m_opmFlowDiagStaticData->m_eclGraph : nullptr;
if ( eclGraphOwner && *eclGraphOwner )
{
auto& eclGraph = *eclGraphOwner;
const size_t activeCellCount =
eclipseCaseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirActiveCellCount();
if ( eclGraph->numCells() != activeCellCount )
{
RiaLogging::warning( "Flow diagnostics disabled: Cell count in flow graph (" + std::to_string( eclGraph->numCells() ) +
") differs from active cell count (" + std::to_string( activeCellCount ) + ")." );
eclGraph.reset();
m_opmFlowDiagStaticData->m_poreVolume.clear();
}
}
}
}
@@ -21,6 +21,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RifOpmFlowDeckFile-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderEclipseOutput-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderOpmCommon-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifOpmCommonFlowDiagGridSource-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderEclipseSummary-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigActiveCellInfo-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigCaseCellResultsData-Test.cpp
@@ -0,0 +1,122 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "RiaTestDataDirectory.h"
#include "RifOpmCommonFlowDiagGridSource.h"
#include "opm/utility/ECLGraph.hpp"
#include "opm/utility/ECLResultData.hpp"
#include <filesystem>
namespace
{
//--------------------------------------------------------------------------------------------------
/// Build the flow graph with both resdata and opm-common, and require identical topology
//--------------------------------------------------------------------------------------------------
void compareGraphs( const std::filesystem::path& gridFile )
{
auto initFile = gridFile;
initFile.replace_extension( ".INIT" );
if ( !std::filesystem::exists( gridFile ) || !std::filesystem::exists( initFile ) )
{
GTEST_SKIP() << "Test file not found: " << gridFile;
}
const Opm::ECLInitFileData init( initFile.string() );
const auto resdataGraph = Opm::ECLGraph::load( gridFile.string(), init );
const RifOpmCommonFlowDiagGridSource opmSource( gridFile.string() );
const auto opmGraph = Opm::ECLGraph::load( opmSource, init );
EXPECT_EQ( resdataGraph.numGrids(), opmGraph.numGrids() );
EXPECT_EQ( resdataGraph.activeGrids(), opmGraph.activeGrids() );
EXPECT_EQ( resdataGraph.numCells(), opmGraph.numCells() );
EXPECT_EQ( resdataGraph.numConnections(), opmGraph.numConnections() );
EXPECT_EQ( resdataGraph.neighbours(), opmGraph.neighbours() );
EXPECT_EQ( resdataGraph.poreVolume(), opmGraph.poreVolume() );
EXPECT_EQ( resdataGraph.transmissibility(), opmGraph.transmissibility() );
EXPECT_EQ( resdataGraph.localCellID(), opmGraph.localCellID() );
auto restartFile = gridFile;
restartFile.replace_extension( ".UNRST" );
if ( !std::filesystem::exists( restartFile ) ) return;
const Opm::ECLRestartData restart( restartFile.string() );
if ( !restart.selectReportStep( 1 ) ) return;
for ( const auto phase : resdataGraph.activePhases() )
{
EXPECT_EQ( resdataGraph.flux( restart, phase ), opmGraph.flux( restart, phase ) );
}
}
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataWithLgrAndNnc )
{
const auto gridFile = std::filesystem::path( TEST_MODEL_DIR ) / "TEST10K_FLT_LGR_NNC/TEST10K_FLT_LGR_NNC.EGRID";
if ( !std::filesystem::exists( gridFile ) ) GTEST_SKIP() << "Test file not found: " << gridFile;
// Make sure the model exercises LGR and NNC handling
const RifOpmCommonFlowDiagGridSource opmSource( gridFile.string() );
EXPECT_GT( opmSource.numGrids(), 1 );
EXPECT_FALSE( opmSource.nncs().empty() );
compareGraphs( gridFile );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdata )
{
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "flow_diagnostics_test/SIMPLE_SUMMARY2.EGRID" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataWithNestedLgr )
{
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "NestedHybridGrid/DROGON_NESTED.EGRID" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataOpmFlowOutput )
{
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "msw-export/opm-simulation-reference/BASE_MODEL_1.EGRID" );
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "synthetic_ensemble_with_dynamic_filter/Real0/SYNTH.EGRID" );
}
//--------------------------------------------------------------------------------------------------
/// Norne from a resinsight-tutorials checkout next to the ResInsight checkout. Skipped if not present.
//--------------------------------------------------------------------------------------------------
TEST( RifOpmCommonFlowDiagGridSource, SameGraphAsResdataNorne )
{
compareGraphs( std::filesystem::path( TEST_MODEL_DIR ) / "../../resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" );
}
@@ -10,10 +10,15 @@
#include "RigFlowDiagSolverInterface.h"
#include "RigMainGrid.h"
#include "RimEclipseResultCase.h"
#include "RimFlowDiagSolution.h"
#include <QFileInfo>
#include <QString>
#include "RiaPreferencesGrid.h"
#include "cafPdmField.h"
#include <algorithm>
#include <iostream>
@@ -154,3 +159,96 @@ TEST( FlowDiagnosticsTest, calculateRelPermCurves_Oil_in_Oil_Gas )
<< "Relative permeability mismatch at index " << i << " - Expected: " << expectedKr[i] << ", Actual: " << krogCurve->yVals[i];
}
}
namespace
{
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void setOnlyLoadActiveCells( bool onlyActive )
{
auto field = dynamic_cast<caf::PdmField<bool>*>( RiaPreferencesGrid::current()->findField( "onlyLoadActiveCells" ) );
ASSERT_NE( field, nullptr );
field->setValue( onlyActive );
}
//--------------------------------------------------------------------------------------------------
/// Run flow diagnostics for the last time step, with all wells as tracers
//--------------------------------------------------------------------------------------------------
std::map<RigFlowDiagResultAddress, std::vector<double>>
calculateFlowDiagnostics( const QString& egridFilePath, RiaDefines::GridModelReader reader, bool onlyActiveCells )
{
RiaPreferencesGrid::current()->setGridModelReaderOverride( reader );
setOnlyLoadActiveCells( onlyActiveCells );
auto eclipseCase = std::make_unique<RimEclipseResultCase>();
eclipseCase->setGridFileName( egridFilePath );
const bool isOpen = eclipseCase->openEclipseGridFile();
RiaPreferencesGrid::current()->setGridModelReaderOverride( RiaDefines::GridModelReader::NOT_SET );
setOnlyLoadActiveCells( false );
if ( !isOpen ) return {};
auto flowDiagSolution = eclipseCase->defaultFlowDiagSolution();
if ( !flowDiagSolution || eclipseCase->timeStepDates().empty() ) return {};
const size_t timeStepIndex = eclipseCase->timeStepDates().size() - 1;
RigFlowDiagSolverInterface solver( eclipseCase.get() );
auto result = solver.calculate( timeStepIndex,
RigFlowDiagResultAddress::PHASE_ALL,
flowDiagSolution->allInjectorTracerActiveCellIndices( timeStepIndex ),
flowDiagSolution->allProducerTracerActiveCellIndices( timeStepIndex ) );
return result.nativeResults();
}
//--------------------------------------------------------------------------------------------------
/// Flow diagnostics must give the same results for grids imported with resdata and opm-common,
/// including when only active cell geometry is imported
//--------------------------------------------------------------------------------------------------
void compareResultsForAllGridReaders( const QString& egridFilePath )
{
if ( !QFileInfo::exists( egridFilePath ) )
{
GTEST_SKIP() << "Test file not found: " << egridFilePath.toStdString();
}
const auto resdataResults = calculateFlowDiagnostics( egridFilePath, RiaDefines::GridModelReader::RESDATA, false );
ASSERT_FALSE( resdataResults.empty() );
for ( bool onlyActiveCells : { false, true } )
{
SCOPED_TRACE( onlyActiveCells ? "opm-common, active cells only" : "opm-common" );
const auto opmResults = calculateFlowDiagnostics( egridFilePath, RiaDefines::GridModelReader::OPM_COMMON, onlyActiveCells );
ASSERT_EQ( resdataResults.size(), opmResults.size() );
for ( const auto& [address, values] : resdataResults )
{
auto it = opmResults.find( address );
ASSERT_NE( it, opmResults.end() ) << address.variableName;
EXPECT_EQ( values, it->second ) << address.variableName;
}
}
}
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( FlowDiagnosticsTest, SameResultsForAllGridReaders )
{
compareResultsForAllGridReaders( QString( TEST_MODEL_DIR ) + "/synthetic_ensemble_with_dynamic_filter/Real0/SYNTH.EGRID" );
}
//--------------------------------------------------------------------------------------------------
/// Norne from a resinsight-tutorials checkout next to the ResInsight checkout. Skipped if not present.
//--------------------------------------------------------------------------------------------------
TEST( FlowDiagnosticsTest, SameResultsForAllGridReadersNorne )
{
compareResultsForAllGridReaders( QString( TEST_MODEL_DIR ) +
"/../../resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" );
}