move all applications into their top-level directory

thanks to [at]akva2 for the suggestion.
This commit is contained in:
Andreas Lauser 2016-11-11 15:02:34 +01:00
parent 67c978fe75
commit 759c2dbdaa
17 changed files with 992 additions and 0 deletions

433
ebos/collecttoiorank.hh Normal file
View File

@ -0,0 +1,433 @@
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
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 2 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/>.
Consult the COPYING file in the top-level source directory of this
module for the precise wording of the license and the list of
copyright holders.
*/
#ifndef EWOMS_PARALLELSERIALOUTPUT_HH
#define EWOMS_PARALLELSERIALOUTPUT_HH
//#if HAVE_OPM_GRID
#include <dune/grid/common/p2pcommunicator.hh>
#include <dune/grid/utility/persistentcontainer.hh>
#include <dune/grid/common/gridenums.hh>
//#else
//#error "This header needs the opm-grid module."
//#endif
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <opm/common/Exceptions.hpp>
#include <dune/grid/common/mcmgmapper.hh>
#include <stdexcept>
namespace Ewoms
{
template < class GridManager >
class CollectDataToIORank
{
public:
typedef typename GridManager :: Grid Grid;
typedef typename Grid :: CollectiveCommunication CollectiveCommunication;
// global id
class GlobalCellIndex
{
int globalId_;
int localIndex_;
bool isInterior_;
public:
GlobalCellIndex() : globalId_(-1), localIndex_(-1), isInterior_(true) {}
void setGhost() { isInterior_ = false; }
void setId( const int globalId ) { globalId_ = globalId; }
void setIndex( const int localIndex ) { localIndex_ = localIndex; }
int localIndex () const { return localIndex_; }
int id () const { return globalId_; }
bool isInterior() const { return isInterior_; }
};
typedef typename Dune::PersistentContainer< Grid, GlobalCellIndex > GlobalIndexContainer;
static const int dimension = Grid :: dimension ;
typedef typename Grid :: LeafGridView GridView;
typedef GridView AllGridView;
typedef Dune :: Point2PointCommunicator< Dune :: SimpleMessageBuffer > P2PCommunicatorType;
typedef typename P2PCommunicatorType :: MessageBufferType MessageBufferType;
typedef std::vector< GlobalCellIndex > LocalIndexMapType;
typedef std::vector<int> IndexMapType;
typedef std::vector< IndexMapType > IndexMapStorageType;
class DistributeIndexMapping : public P2PCommunicatorType::DataHandleInterface
{
protected:
const std::vector<int>& distributedGlobalIndex_;
IndexMapType& localIndexMap_;
IndexMapStorageType& indexMaps_;
std::map< const int, const int > globalPosition_;
#ifndef NDEBUG
std::set< int > checkPosition_;
#endif
public:
DistributeIndexMapping( const std::vector<int>& globalIndex,
const std::vector<int>& distributedGlobalIndex,
IndexMapType& localIndexMap,
IndexMapStorageType& indexMaps )
: distributedGlobalIndex_( distributedGlobalIndex ),
localIndexMap_( localIndexMap ),
indexMaps_( indexMaps ),
globalPosition_()
{
const size_t size = globalIndex.size();
// create mapping globalIndex --> localIndex
for ( size_t index = 0; index < size; ++index )
{
globalPosition_.insert( std::make_pair( globalIndex[ index ], index ) );
}
// on I/O rank we need to create a mapping from local to global
if( ! indexMaps_.empty() )
{
// for the ioRank create a localIndex to index in global state map
IndexMapType& indexMap = indexMaps_.back();
const size_t localSize = localIndexMap_.size();
indexMap.resize( localSize );
for( size_t i=0; i<localSize; ++i )
{
const int id = distributedGlobalIndex_[ localIndexMap_[ i ] ];
indexMap[ i ] = globalPosition_[ id ] ;
#ifndef NDEBUG
assert( checkPosition_.find( id ) == checkPosition_.end() );
checkPosition_.insert( id );
#endif
}
}
}
void pack( const int link, MessageBufferType& buffer )
{
// we should only get one link
if( link != 0 ) {
OPM_THROW(std::logic_error,"link in method pack is not 0 as execpted");
}
// pack all interior global cell id's
const int size = localIndexMap_.size();
buffer.write( size );
for( int index = 0; index < size; ++index )
{
const int globalIdx = distributedGlobalIndex_[ localIndexMap_[ index ] ];
buffer.write( globalIdx );
}
}
void unpack( const int link, MessageBufferType& buffer )
{
// get index map for current link
IndexMapType& indexMap = indexMaps_[ link ];
assert( ! globalPosition_.empty() );
// unpack all interior global cell id's
int numCells = 0;
buffer.read( numCells );
indexMap.resize( numCells );
for( int index = 0; index < numCells; ++index )
{
int globalId = -1;
buffer.read( globalId );
assert( globalPosition_.find( globalId ) != globalPosition_.end() );
indexMap[ index ] = globalPosition_[ globalId ];
#ifndef NDEBUG
assert( checkPosition_.find( globalId ) == checkPosition_.end() );
checkPosition_.insert( globalId );
#endif
}
}
};
enum { ioRank = 0 };
CollectDataToIORank( const GridManager& gridManager )
: toIORankComm_( ),
isIORank_( gridManager.grid().comm().rank() == ioRank )
{
typedef typename GridManager::GridView GridView;
typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView, Dune::MCMGElementLayout>
ElementMapper;
const CollectiveCommunication& comm = gridManager.grid().comm();
{
std::set< int > send, recv;
// the I/O rank receives from all other ranks
if( isIORank() )
{
const auto& eclGrid = gridManager.eclGrid();
// create the ACTNUM array based on the "real" grid which is used for
// simulation. Note that this is still an approximation because the simulation
// grid may also modify the geometry of cells (e.g. because of the PINCH
// keyword), but at least the number of cells is correct, so all values are
// hopefully displayed at approximately the correct location.
std::vector<int> actnumData(eclGrid->getCartesianSize(), 0);
ElementMapper elemMapper(gridManager.gridView());
auto elemIt = gridManager.gridView().template begin<0>();
const auto& elemEndIt = gridManager.gridView().template end<0>();
for (; elemIt != elemEndIt; ++elemIt) {
#if DUNE_VERSION_NEWER(DUNE_COMMON, 2,4)
int elemIdx = elemMapper.index(*elemIt );
#else
int elemIdx = elemMapper.map(*elemIt );
#endif
int cartElemIdx = gridManager.cartesianIndex(elemIdx);
actnumData[cartElemIdx] = 1;
}
// the I/O rank needs a picture of the global grid
const size_t cartesianSize = eclGrid->getCartesianSize();
// reserve memory
globalCartesianIndex_.reserve( cartesianSize );
globalCartesianIndex_.clear();
// get a global cartesian index for each active cell in the eclipse grid
for( size_t cartIndex=0; cartIndex<cartesianSize; ++cartIndex )
{
if( actnumData[cartIndex] > 0 )
{
globalCartesianIndex_.push_back( cartIndex );
}
}
for(int i=0; i<comm.size(); ++i)
{
if( i != ioRank )
{
recv.insert( i );
}
}
}
else // all other simply send to the I/O rank
{
send.insert( ioRank );
}
localIndexMap_.clear();
const size_t gridSize = gridManager.grid().size( 0 );
localIndexMap_.reserve( gridSize );
unsigned int index = 0;
auto localView = gridManager.grid().leafGridView();
for( auto it = localView.template begin< 0 >(),
end = localView.template end< 0 >(); it != end; ++it, ++index )
{
const auto element = *it ;
// only store interior element for collection
if( element.partitionType() == Dune :: InteriorEntity )
{
localIndexMap_.push_back( index );
}
}
// insert send and recv linkage to communicator
toIORankComm_.insertRequest( send, recv );
if( isIORank() )
{
// need an index map for each rank
indexMaps_.clear();
indexMaps_.resize( comm.size() );
}
// store the local cartesian index
IndexMapType distributedCartesianIndex;
distributedCartesianIndex.reserve( gridSize );
for( size_t cell = 0 ; cell<gridSize; ++cell )
distributedCartesianIndex.push_back( gridManager.cartesianIndex( cell ) );
// distribute global id's to io rank for later association of dof's
DistributeIndexMapping distIndexMapping( globalCartesianIndex_, distributedCartesianIndex, localIndexMap_, indexMaps_ );
toIORankComm_.exchange( distIndexMapping );
}
}
template <class BufferList>
class PackUnPackOutputBuffers : public P2PCommunicatorType::DataHandleInterface
{
BufferList& bufferList_;
const IndexMapType& localIndexMap_;
const IndexMapStorageType& indexMaps_;
public:
PackUnPackOutputBuffers( BufferList& bufferList,
const IndexMapType& localIndexMap,
const IndexMapStorageType& indexMaps,
const size_t globalSize,
const bool isIORank )
: bufferList_( bufferList ),
localIndexMap_( localIndexMap ),
indexMaps_( indexMaps )
{
if( isIORank )
{
MessageBufferType buffer;
pack( 0, buffer );
// resize all buffers
for (auto it = bufferList_.begin(), end = bufferList_.end(); it != end; ++it )
{
it->second->resize( globalSize );
}
// the last index map is the local one
doUnpack( indexMaps.back(), buffer );
}
}
// pack all data associated with link
void pack( const int link, MessageBufferType& buffer )
{
// we should only get one link
if( link != 0 ) {
OPM_THROW(std::logic_error,"link in method pack is not 0 as execpted");
}
size_t buffers = bufferList_.size();
buffer.write( buffers );
for (auto it = bufferList_.begin(), end = bufferList_.end(); it != end; ++it )
{
write( buffer, localIndexMap_, *(it->second) );
}
}
void doUnpack( const IndexMapType& indexMap, MessageBufferType& buffer )
{
size_t buffers = 0;
buffer.read( buffers );
assert( buffers == bufferList_.size() );
for( auto it = bufferList_.begin(), end = bufferList_.end(); it != end; ++it )
{
read( buffer, indexMap, *(it->second) );
}
}
// unpack all data associated with link
void unpack( const int link, MessageBufferType& buffer )
{
doUnpack( indexMaps_[ link ], buffer );
}
protected:
template <class Vector>
void write( MessageBufferType& buffer, const IndexMapType& localIndexMap, const Vector& data ) const
{
const size_t size = localIndexMap.size();
assert( size <= data.size() );
buffer.write( size );
for( size_t i=0; i<size; ++i )
{
buffer.write( data[ localIndexMap[ i ] ] );
}
}
template <class Vector>
void read( MessageBufferType& buffer, const IndexMapType& indexMap, Vector& data ) const
{
size_t size = indexMap.size();
assert( size <= data.size() );
buffer.read( size );
assert( size == indexMap.size() );
for( size_t i=0; i<size; ++i )
{
buffer.read( data[ indexMap[ i ] ] );
}
}
void writeString( MessageBufferType& buffer, const std::string& s) const
{
const int size = s.size();
buffer.write( size );
for( int i=0; i<size; ++i )
{
buffer.write( s[ i ] );
}
}
void readString( MessageBufferType& buffer, std::string& s) const
{
int size = -1;
buffer.read( size );
s.resize( size );
for( int i=0; i<size; ++i )
{
buffer.read( s[ i ] );
}
}
};
// gather solution to rank 0 for EclipseWriter
template <class BufferList>
void collect( BufferList& bufferList ) const
{
PackUnPackOutputBuffers< BufferList >
packUnpack( bufferList,
localIndexMap_,
indexMaps_,
numCells(),
isIORank() );
//toIORankComm_.exchangeCached( packUnpack );
toIORankComm_.exchange( packUnpack );
#ifndef NDEBUG
// mkae sure every process is on the same page
toIORankComm_.barrier();
#endif
}
bool isIORank() const
{
return isIORank_;
}
bool isParallel() const
{
return toIORankComm_.size() > 1;
}
size_t numCells () const { return globalCartesianIndex_.size(); }
protected:
P2PCommunicatorType toIORankComm_;
IndexMapType globalCartesianIndex_;
IndexMapType localIndexMap_;
IndexMapStorageType indexMaps_;
// true if we are on I/O rank
bool isIORank_;
};
} // end namespace Opm
#endif

44
ebos/ebos.cc Normal file
View File

@ -0,0 +1,44 @@
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
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 2 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/>.
Consult the COPYING file in the top-level source directory of this
module for the precise wording of the license and the list of
copyright holders.
*/
/*!
* \file
*
* \brief A general-purpose simulator for ECL decks using the black-oil model.
*/
#include "config.h"
#include <ewoms/common/quad.hh>
#include <ewoms/common/start.hh>
#include "eclproblem.hh"
namespace Ewoms {
namespace Properties {
NEW_TYPE_TAG(EclProblem, INHERITS_FROM(BlackOilModel, EclBaseProblem));
}}
int main(int argc, char **argv)
{
typedef TTAG(EclProblem) ProblemTypeTag;
return Ewoms::start<ProblemTypeTag>(argc, argv);
}

View File

@ -0,0 +1,160 @@
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
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 2 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/>.
Consult the COPYING file in the top-level source directory of this
module for the precise wording of the license and the list of
copyright holders.
*/
/*!
* \file
* \copydoc Ewoms::EclPolyhedralGridManager
*/
#ifndef EWOMS_ECL_POLYHEDRAL_GRID_MANAGER_HH
#define EWOMS_ECL_POLYHEDRAL_GRID_MANAGER_HH
#include "eclbasegridmanager.hh"
#include <dune/grid/polyhedralgrid.hh>
namespace Ewoms {
template <class TypeTag>
class EclPolyhedralGridManager;
namespace Properties {
NEW_TYPE_TAG(EclPolyhedralGridManager, INHERITS_FROM(EclBaseGridManager));
// declare the properties
SET_TYPE_PROP(EclPolyhedralGridManager, GridManager, Ewoms::EclPolyhedralGridManager<TypeTag>);
SET_TYPE_PROP(EclPolyhedralGridManager, Grid, Dune::PolyhedralGrid<3, 3>);
SET_TYPE_PROP(EclPolyhedralGridManager, EquilGrid, typename GET_PROP_TYPE(TypeTag, Grid));
} // namespace Properties
/*!
* \ingroup EclBlackOilSimulator
*
* \brief Helper class for grid instantiation of ECL file-format using problems.
*
* This class uses Dune::PolyhedralGrid as the simulation grid.
*/
template <class TypeTag>
class EclPolyhedralGridManager : public EclBaseGridManager<TypeTag>
{
friend class EclBaseGridManager<TypeTag>;
typedef EclBaseGridManager<TypeTag> ParentType;
typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar;
typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator;
public:
typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
typedef typename GET_PROP_TYPE(TypeTag, EquilGrid) EquilGrid;
typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView;
private:
typedef Grid* GridPointer;
typedef EquilGrid* EquilGridPointer;
typedef Dune::CartesianIndexMapper<Grid> CartesianIndexMapper;
typedef CartesianIndexMapper* CartesianIndexMapperPointer;
public:
/*!
* \brief Inherit the constructors from the base class.
*/
using EclBaseGridManager<TypeTag>::EclBaseGridManager;
~EclPolyhedralGridManager()
{
delete cartesianIndexMapper_;
delete grid_;
}
/*!
* \brief Return a reference to the simulation grid.
*/
Grid& grid()
{ return *grid_; }
/*!
* \brief Return a reference to the simulation grid.
*/
const Grid& grid() const
{ return *grid_; }
/*!
* \brief Returns a refefence to the grid which should be used by the EQUIL
* initialization code.
*
* The EQUIL keyword is used to specify the initial condition of the reservoir in
* hydrostatic equilibrium. Since the code which does this is not accepting arbitrary
* DUNE grids (the code is part of the opm-core module), this is not necessarily the
* same as the grid which is used for the actual simulation.
*/
const EquilGrid& equilGrid() const
{ return *grid_; }
/*!
* \brief Indicates that the initial condition has been computed and the memory used
* by the EQUIL grid can be released.
*
* Depending on the implementation, subsequent accesses to the EQUIL grid lead to
* crashes.
*/
void releaseEquilGrid()
{ /* do nothing: The EQUIL grid is the simulation grid! */ }
/*!
* \brief Distribute the simulation grid over multiple processes
*
* (For parallel simulation runs.)
*/
void loadBalance()
{ /* do nothing: PolyhedralGrid is not parallel! */ }
/*!
* \brief Returns the object which maps a global element index of the simulation grid
* to the corresponding element index of the logically Cartesian index.
*/
const CartesianIndexMapper& cartesianIndexMapper() const
{ return *cartesianIndexMapper_; }
/*!
* \brief Returns mapper from compressed to cartesian indices for the EQUIL grid
*
* Since PolyhedralGrid is not parallel, that's always the same as
* cartesianIndexMapper().
*/
const CartesianIndexMapper& equilCartesianIndexMapper() const
{ return *cartesianIndexMapper_; }
protected:
void createGrids_()
{
const auto& gridProps = this->eclState()->get3DProperties();
const std::vector<double>& porv = gridProps.getDoubleGridProperty("PORV").getData();
grid_ = new Grid(*(this->deck()), porv);
cartesianIndexMapper_ = new CartesianIndexMapper(*grid_);
}
GridPointer grid_;
CartesianIndexMapperPointer cartesianIndexMapper_;
};
} // namespace Ewoms
#endif

355
ebos/eclwriter.hh Normal file
View File

@ -0,0 +1,355 @@
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
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 2 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/>.
Consult the COPYING file in the top-level source directory of this
module for the precise wording of the license and the list of
copyright holders.
*/
/*!
* \file
*
* \copydoc Ewoms::EclWriter
*/
#ifndef EWOMS_ECL_WRITER_HH
#define EWOMS_ECL_WRITER_HH
#include <opm/material/densead/Evaluation.hpp>
#include "ertwrappers.hh"
#include "collecttoiorank.hh"
#include <ewoms/disc/ecfv/ecfvdiscretization.hh>
#include <ewoms/io/baseoutputwriter.hh>
#include <opm/material/common/Valgrind.hpp>
#include <opm/common/ErrorMacros.hpp>
#include <opm/common/Exceptions.hpp>
#include <boost/algorithm/string.hpp>
#include <list>
#include <utility>
#include <string>
#include <limits>
#include <sstream>
#include <fstream>
#include <type_traits>
namespace Ewoms {
namespace Properties {
NEW_PROP_TAG(EnableEclOutput);
}
template <class TypeTag>
class EclWriter;
template <class TypeTag, class GridManagerType>
class EclWriterHelper
{
friend class EclWriter<TypeTag>;
static void writeHeaders_(EclWriter<TypeTag>& writer)
{
typedef typename GET_PROP_TYPE(TypeTag, Discretization) Discretization;
if (!std::is_same<Discretization, Ewoms::EcfvDiscretization<TypeTag> >::value)
OPM_THROW(std::logic_error,
"Ecl binary output only works for the element centered "
"finite volume discretization.");
#if ! HAVE_ERT
OPM_THROW(std::logic_error,
"Ecl binary output requires the ERT libraries");
#else
// set the index of the first time step written to 0...
writer.reportStepIdx_ = 0;
char* egridRawFileName = ecl_util_alloc_filename(/*outputDir=*/"./",
writer.caseName().c_str(),
ECL_EGRID_FILE,
/*formatted=*/false, // -> write binary output
writer.reportStepIdx_);
std::string egridFileName(egridRawFileName);
std::free(egridRawFileName);
ErtGrid ertGrid(*writer.simulator_.gridManager().eclGrid(),
writer.simulator_.gridManager().grid(),
writer.simulator_.gridManager().cartesianIndexMapper(),
writer.simulator_.problem().deckUnits());
ertGrid.write(egridFileName, writer.reportStepIdx_);
#endif
}
};
/*!
* \ingroup EclBlackOilSimulator
*
* \brief Implements writing Ecl binary output files.
*
* Caveats:
* - For this class to do do anything meaningful, you need to have the
* ERT libraries with development headers installed and the ERT
* build system test must pass sucessfully.
* - The only DUNE grid which is currently supported is Dune::CpGrid
* from the OPM module "opm-core". Using another grid won't
* fail at compile time but you will provoke a fatal exception as
* soon as you try to write an ECL output file.
* - This class requires to use the black oil model with the element
* centered finite volume discretization.
* - MPI-parallel computations are not (yet?) supported.
*/
template <class TypeTag>
class EclWriter : public BaseOutputWriter
{
typedef typename GET_PROP_TYPE(TypeTag, VertexMapper) VertexMapper;
typedef typename GET_PROP_TYPE(TypeTag, ElementMapper) ElementMapper;
typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator;
typedef typename GET_PROP_TYPE(TypeTag, GridManager) GridManager;
typedef typename GET_PROP_TYPE(TypeTag, GridView) GridView;
typedef CollectDataToIORank< GridManager > CollectDataToIORankType;
typedef BaseOutputWriter::ScalarBuffer ScalarBuffer;
typedef BaseOutputWriter::VectorBuffer VectorBuffer;
typedef BaseOutputWriter::TensorBuffer TensorBuffer;
friend class EclWriterHelper<TypeTag, GridManager>;
public:
EclWriter(const Simulator& simulator)
: simulator_(simulator)
, gridView_(simulator_.gridView())
, elementMapper_(gridView_)
, vertexMapper_(gridView_)
, collectToIORank_( simulator_.gridManager() )
{
reportStepIdx_ = 0;
}
~EclWriter()
{ }
/*!
* \brief Returns the name of the simulation.
*
* This is the prefix of the files written to disk.
*/
std::string caseName() const
{ return boost::to_upper_copy(simulator_.problem().name()); }
/*!
* \brief Updates the internal data structures after mesh
* refinement.
*
* If the grid changes between two calls of beginWrite(), this
* method _must_ be called before the second beginWrite()!
*/
void gridChanged()
{
elementMapper_.update();
vertexMapper_.update();
}
/*!
* \brief Called whenever a new time step must be written.
*/
void beginWrite(double t)
{
if (enableEclOutput_() && reportStepIdx_ == 0 && collectToIORank_.isIORank() )
EclWriterHelper<TypeTag, GridManager>::writeHeaders_(*this);
}
/*
* \brief Add a vertex-centered scalar field to the output.
*
* For the EclWriter, this method is a no-op which throws a
* std::logic_error exception
*/
void attachScalarVertexData(ScalarBuffer& buf, std::string name)
{
OPM_THROW(std::logic_error,
"The EclWriter can only write element based quantities!");
}
/*
* \brief Add a vertex-centered vector field to the output.
*
* For the EclWriter, this method is a no-op which throws a
* std::logic_error exception
*/
void attachVectorVertexData(VectorBuffer& buf, std::string name)
{
OPM_THROW(std::logic_error,
"The EclWriter can only write element based quantities!");
}
/*
* \brief Add a vertex-centered tensor field to the output.
*/
void attachTensorVertexData(TensorBuffer& buf, std::string name)
{
OPM_THROW(std::logic_error,
"The EclWriter can only write element based quantities!");
}
/*!
* \brief Add a scalar quantity to the output.
*
* The buffer must exist at least until the call to endWrite()
* finishes. Modifying the buffer between the call to this method
* and endWrite() results in _undefined behavior_.
*/
void attachScalarElementData(ScalarBuffer& buf, std::string name)
{
attachedBuffers_.push_back(std::pair<std::string, ScalarBuffer*>(name, &buf));
}
/*
* \brief Add a element-centered vector field to the output.
*
* For the EclWriter, this method is a no-op which throws a
* std::logic_error exception
*/
void attachVectorElementData(VectorBuffer& buf, std::string name)
{
OPM_THROW(std::logic_error,
"Currently, the EclWriter can only write scalar quantities!");
}
/*
* \brief Add a element-centered tensor field to the output.
*/
void attachTensorElementData(TensorBuffer& buf, std::string name)
{
OPM_THROW(std::logic_error,
"Currently, the EclWriter can only write scalar quantities!");
}
/*!
* \brief Finalizes the current writer.
*
* This means that everything will be written to disk, except if
* the onlyDiscard argument is true. In this case, no output is
* written but the 'janitorial' jobs at the end of a time step are
* still done.
*/
void endWrite(bool onlyDiscard = false)
{
if (onlyDiscard || !enableEclOutput_() || !simulator_.episodeWillBeOver()) {
// detach all buffers
attachedBuffers_.clear();
return;
}
#if !HAVE_ERT
OPM_THROW(std::runtime_error,
"The ERT libraries must be available to write ECL output!");
#else
// collect all data to I/O rank and store in attachedBuffers_
// this also reorders the data such that it fits the underlying eclGrid
collectToIORank_.collect( attachedBuffers_ );
// write output on I/O rank
if (collectToIORank_.isIORank()) {
ErtRestartFile restartFile(simulator_, reportStepIdx_);
restartFile.writeHeader(simulator_, reportStepIdx_);
ErtSolution solution(restartFile);
auto bufIt = attachedBuffers_.begin();
const auto& bufEndIt = attachedBuffers_.end();
for (; bufIt != bufEndIt; ++ bufIt) {
const std::string& name = bufIt->first;
const ScalarBuffer& buffer = *bufIt->second;
std::shared_ptr<const ErtKeyword<float>>
bufKeyword(new ErtKeyword<float>(name, buffer));
solution.add(bufKeyword);
}
}
// detach all buffers
attachedBuffers_.clear();
// next time we take the next report step
++ reportStepIdx_;
#endif
}
/*!
* \brief Write the multi-writer's state to a restart file.
*/
template <class Restarter>
void serialize(Restarter& res)
{
res.serializeSectionBegin("EclWriter");
res.serializeStream() << reportStepIdx_ << "\n";
res.serializeSectionEnd();
}
/*!
* \brief Read the multi-writer's state from a restart file.
*/
template <class Restarter>
void deserialize(Restarter& res)
{
res.deserializeSectionBegin("EclWriter");
res.deserializeStream() >> reportStepIdx_;
res.deserializeSectionEnd();
}
private:
static bool enableEclOutput_()
{ return EWOMS_GET_PARAM(TypeTag, bool, EnableEclOutput); }
// make sure the field is well defined if running under valgrind
// and make sure that all values can be displayed by paraview
void sanitizeBuffer_(std::vector<float>& b)
{
static bool warningPrinted = false;
for (size_t i = 0; i < b.size(); ++i) {
Valgrind::CheckDefined(b[i]);
if (!warningPrinted && !std::isfinite(b[i])) {
std::cerr << "WARNING: data field written to disk contains non-finite entries!\n";
warningPrinted = true;
}
// set values which are too small to 0 to avoid possible
// problems
if (std::abs(b[i]) < std::numeric_limits<float>::min()) {
b[i] = 0.0;
}
}
}
const Simulator& simulator_;
const GridView gridView_;
ElementMapper elementMapper_;
VertexMapper vertexMapper_;
CollectDataToIORankType collectToIORank_;
double curTime_;
unsigned reportStepIdx_;
std::list<std::pair<std::string, ScalarBuffer*> > attachedBuffers_;
};
} // namespace Ewoms
#endif