/*
Copyright (c) 2014 SINTEF ICT, Applied Mathematics.
Copyright (c) 2015 IRIS 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 .
*/
#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILOUTPUT_HEADER_INCLUDED
#define OPM_SIMULATORFULLYIMPLICITBLACKOILOUTPUT_HEADER_INCLUDED
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_OPM_GRID
#include
#endif
namespace Opm
{
class SimulationDataContainer;
class BlackoilState;
void outputStateVtk(const UnstructuredGrid& grid,
const Opm::SimulationDataContainer& state,
const int step,
const std::string& output_dir);
void outputStateMatlab(const UnstructuredGrid& grid,
const Opm::SimulationDataContainer& state,
const int step,
const std::string& output_dir);
void outputWellStateMatlab(const Opm::WellState& well_state,
const int step,
const std::string& output_dir);
#ifdef HAVE_OPM_GRID
void outputStateVtk(const Dune::CpGrid& grid,
const Opm::SimulationDataContainer& state,
const int step,
const std::string& output_dir);
#endif
template
void outputStateMatlab(const Grid& grid,
const Opm::SimulationDataContainer& state,
const int step,
const std::string& output_dir)
{
Opm::DataMap dm;
dm["saturation"] = &state.saturation();
dm["pressure"] = &state.pressure();
for (const auto& pair : state.cellData())
{
const std::string& name = pair.first;
std::string key;
if( name == "SURFACEVOL" ) {
key = "surfvolume";
}
else if( name == "RV" ) {
key = "rv";
}
else if( name == "GASOILRATIO" ) {
key = "rs";
}
else { // otherwise skip entry
continue;
}
// set data to datmap
dm[ key ] = &pair.second;
}
std::vector cell_velocity;
Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid),
AutoDiffGrid::numFaces(grid),
AutoDiffGrid::beginFaceCentroids(grid),
UgGridHelpers::faceCells(grid),
AutoDiffGrid::beginCellCentroids(grid),
AutoDiffGrid::beginCellVolumes(grid),
AutoDiffGrid::dimensions(grid),
state.faceflux(), cell_velocity);
dm["velocity"] = &cell_velocity;
// Write data (not grid) in Matlab format
for (Opm::DataMap::const_iterator it = dm.begin(); it != dm.end(); ++it) {
std::ostringstream fname;
fname << output_dir << "/" << it->first;
boost::filesystem::path fpath = fname.str();
try {
create_directories(fpath);
}
catch (...) {
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
}
fname << "/" << std::setw(3) << std::setfill('0') << step << ".txt";
std::ofstream file(fname.str().c_str());
if (!file) {
OPM_THROW(std::runtime_error, "Failed to open " << fname.str());
}
file.precision(15);
const std::vector& d = *(it->second);
std::copy(d.begin(), d.end(), std::ostream_iterator(file, "\n"));
}
}
class BlackoilSubWriter {
public:
BlackoilSubWriter( const std::string& outputDir )
: outputDir_( outputDir )
{}
virtual void writeTimeStep(const SimulatorTimerInterface& timer,
const SimulationDataContainer& state,
const WellState&,
bool /*substep*/ = false) = 0;
protected:
const std::string outputDir_;
};
template< class Grid >
class BlackoilVTKWriter : public BlackoilSubWriter {
public:
BlackoilVTKWriter( const Grid& grid,
const std::string& outputDir )
: BlackoilSubWriter( outputDir )
, grid_( grid )
{}
void writeTimeStep(const SimulatorTimerInterface& timer,
const SimulationDataContainer& state,
const WellState&,
bool /*substep*/ = false) override
{
outputStateVtk(grid_, state, timer.currentStepNum(), outputDir_);
}
protected:
const Grid& grid_;
};
template< typename Grid >
class BlackoilMatlabWriter : public BlackoilSubWriter
{
public:
BlackoilMatlabWriter( const Grid& grid,
const std::string& outputDir )
: BlackoilSubWriter( outputDir )
, grid_( grid )
{}
void writeTimeStep(const SimulatorTimerInterface& timer,
const SimulationDataContainer& reservoirState,
const WellState& wellState,
bool /*substep*/ = false) override
{
outputStateMatlab(grid_, reservoirState, timer.currentStepNum(), outputDir_);
outputWellStateMatlab(wellState, timer.currentStepNum(), outputDir_);
}
protected:
const Grid& grid_;
};
/** \brief Wrapper class for VTK, Matlab, and ECL output. */
class BlackoilOutputWriter
{
public:
// constructor creating different sub writers
template
BlackoilOutputWriter(const Grid& grid,
const parameter::ParameterGroup& param,
const Opm::EclipseState& eclipseState,
const Opm::PhaseUsage &phaseUsage,
const double* permeability );
/** \copydoc Opm::OutputWriter::writeInit */
void writeInit(const data::Solution& simProps, const NNC& nnc);
/*!
* \brief Write a blackoil reservoir state to disk for later inspection with
* visualization tools like ResInsight. This function will extract the
* requested output cell properties specified by the RPTRST keyword
* and write these to file.
*/
template
void writeTimeStep(const SimulatorTimerInterface& timer,
const SimulationDataContainer& reservoirState,
const Opm::WellState& wellState,
const Model& physicalModel,
bool substep = false);
/*!
* \brief Write a blackoil reservoir state to disk for later inspection with
* visualization tools like ResInsight. This function will write all
* CellData in simProps to the file as well.
*/
void writeTimeStepWithCellProperties(
const SimulatorTimerInterface& timer,
const SimulationDataContainer& reservoirState,
const Opm::WellState& wellState,
const data::Solution& solution,
bool substep = false);
/*!
* \brief Write a blackoil reservoir state to disk for later inspection with
* visualization tools like ResInsight. This function will not write
* any cell properties (e.g., those requested by RPTRST keyword)
*/
void writeTimeStepWithoutCellProperties(
const SimulatorTimerInterface& timer,
const SimulationDataContainer& reservoirState,
const Opm::WellState& wellState,
bool substep = false);
/*!
* \brief Write a blackoil reservoir state to disk for later inspection with
* visualization tools like ResInsight. This is the function which does
* the actual write to file.
*/
void writeTimeStepSerial(const SimulatorTimerInterface& timer,
const SimulationDataContainer& reservoirState,
const Opm::WellState& wellState,
const data::Solution& cellData,
bool substep);
/** \brief return output directory */
const std::string& outputDirectory() const { return outputDir_; }
/** \brief return true if output is enabled */
bool output () const { return output_; }
/** \brief Whether this process does write to disk */
bool isIORank () const
{
return parallelOutput_->isIORank();
}
void restore(SimulatorTimerInterface& timer,
BlackoilState& state,
WellStateFullyImplicitBlackoil& wellState,
const std::string& filename,
const int desiredReportStep);
template
void initFromRestartFile(const PhaseUsage& phaseusage,
const double* permeability,
const Grid& grid,
SimulationDataContainer& simulatorstate,
WellStateFullyImplicitBlackoil& wellstate);
bool isRestart() const;
protected:
const bool output_;
std::unique_ptr< ParallelDebugOutputInterface > parallelOutput_;
// Parameters for output.
const std::string outputDir_;
const int output_interval_;
int lastBackupReportStep_;
std::ofstream backupfile_;
Opm::PhaseUsage phaseUsage_;
std::unique_ptr< BlackoilSubWriter > vtkWriter_;
std::unique_ptr< BlackoilSubWriter > matlabWriter_;
std::unique_ptr< EclipseWriter > eclWriter_;
const EclipseState& eclipseState_;
std::unique_ptr< ThreadHandle > asyncOutput_;
};
//////////////////////////////////////////////////////////////
//
// Implementation
//
//////////////////////////////////////////////////////////////
template
inline
BlackoilOutputWriter::
BlackoilOutputWriter(const Grid& grid,
const parameter::ParameterGroup& param,
const Opm::EclipseState& eclipseState,
const Opm::PhaseUsage &phaseUsage,
const double* permeability )
: output_( param.getDefault("output", true) ),
parallelOutput_( output_ ? new ParallelDebugOutput< Grid >( grid, eclipseState, phaseUsage.num_phases, permeability ) : 0 ),
outputDir_( output_ ? param.getDefault("output_dir", std::string("output")) : "." ),
output_interval_( output_ ? param.getDefault("output_interval", 1): 0 ),
lastBackupReportStep_( -1 ),
phaseUsage_( phaseUsage ),
vtkWriter_( output_ && param.getDefault("output_vtk",false) ?
new BlackoilVTKWriter< Grid >( grid, outputDir_ ) : 0 ),
matlabWriter_( output_ && parallelOutput_->isIORank() &&
param.getDefault("output_matlab", false) ?
new BlackoilMatlabWriter< Grid >( grid, outputDir_ ) : 0 ),
eclWriter_( output_ && parallelOutput_->isIORank() &&
param.getDefault("output_ecl", true) ?
new EclipseWriter(eclipseState,UgGridHelpers::createEclipseGrid( grid , eclipseState.getInputGrid()))
: 0 ),
eclipseState_(eclipseState),
asyncOutput_()
{
// For output.
if (output_ && parallelOutput_->isIORank() ) {
// Ensure that output dir exists
boost::filesystem::path fpath(outputDir_);
try {
create_directories(fpath);
}
catch (...) {
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
}
// create output thread if enabled and rank is I/O rank
// async output is enabled by default if pthread are enabled
#if HAVE_PTHREAD
const bool asyncOutputDefault = false;
#else
const bool asyncOutputDefault = false;
#endif
if( param.getDefault("async_output", asyncOutputDefault ) )
{
#if HAVE_PTHREAD
asyncOutput_.reset( new ThreadHandle() );
#else
OPM_THROW(std::runtime_error,"Pthreads were not found, cannot enable async_output");
#endif
}
std::string backupfilename = param.getDefault("backupfile", std::string("") );
if( ! backupfilename.empty() )
{
backupfile_.open( backupfilename.c_str() );
}
}
}
template
inline void
BlackoilOutputWriter::
initFromRestartFile( const PhaseUsage& phaseusage,
const double* permeability,
const Grid& grid,
SimulationDataContainer& simulatorstate,
WellStateFullyImplicitBlackoil& wellstate)
{
// gives a dummy dynamic_list_econ_limited
DynamicListEconLimited dummy_list_econ_limited;
WellsManager wellsmanager(eclipseState_,
eclipseState_.getInitConfig().getRestartStep(),
Opm::UgGridHelpers::numCells(grid),
Opm::UgGridHelpers::globalCell(grid),
Opm::UgGridHelpers::cartDims(grid),
Opm::UgGridHelpers::dimensions(grid),
Opm::UgGridHelpers::cell2Faces(grid),
Opm::UgGridHelpers::beginFaceCentroids(grid),
permeability,
dummy_list_econ_limited
// We need to pass the optionaly arguments
// as we get the following error otherwise
// with c++ (Debian 4.9.2-10) 4.9.2 and -std=c++11
// converting to ‘const std::unordered_set >’ from initializer list would use explicit constructo
, false,
std::vector(),
std::unordered_set());
const Wells* wells = wellsmanager.c_wells();
wellstate.resize(wells, simulatorstate); //Resize for restart step
auto restarted = Opm::init_from_restart_file(
eclipseState_,
Opm::UgGridHelpers::numCells(grid) );
solutionToSim( restarted.first, phaseusage, simulatorstate );
wellsToState( restarted.second, phaseusage, wellstate );
}
namespace detail {
/**
* Converts an ADB::V into a standard vector by copy
*/
inline std::vector adbVToDoubleVector(const Opm::AutoDiffBlock::V& adb_v) {
std::vector vec(adb_v.data(), adb_v.data() + adb_v.size());
return vec;
}
/**
* Converts an ADB into a standard vector by copy
*/
inline std::vector adbToDoubleVector(const Opm::AutoDiffBlock& adb) {
return adbVToDoubleVector(adb.value());
}
/**
* Returns the data requested in the restartConfig
*/
template
void getRestartData(data::Solution& output,
const Opm::PhaseUsage& phaseUsage,
const Model& physicalModel,
const RestartConfig& restartConfig,
const int reportStepNum,
const bool log) {
typedef Opm::AutoDiffBlock ADB;
const typename Model::SimulatorData& sd = physicalModel.getSimulatorData();
//Get the value of each of the keys for the restart keywords
std::map rstKeywords = restartConfig.getRestartKeywords(reportStepNum);
for (auto& keyValue : rstKeywords) {
keyValue.second = restartConfig.getKeyword(keyValue.first, reportStepNum);
}
//Get shorthands for water, oil, gas
const int aqua_active = phaseUsage.phase_used[Opm::PhaseUsage::Aqua];
const int liquid_active = phaseUsage.phase_used[Opm::PhaseUsage::Liquid];
const int vapour_active = phaseUsage.phase_used[Opm::PhaseUsage::Vapour];
const int aqua_idx = phaseUsage.phase_pos[Opm::PhaseUsage::Aqua];
const int liquid_idx = phaseUsage.phase_pos[Opm::PhaseUsage::Liquid];
const int vapour_idx = phaseUsage.phase_pos[Opm::PhaseUsage::Vapour];
/**
* Formation volume factors for water, oil, gas
*/
if (aqua_active && rstKeywords["BW"] > 0) {
rstKeywords["BW"] = 0;
output.insert("1OVERBW",
Opm::UnitSystem::measure::water_inverse_formation_volume_factor,
adbToDoubleVector(sd.rq[aqua_idx].b),
data::TargetType::RESTART_AUXILLARY);
}
if (liquid_active && rstKeywords["BO"] > 0) {
rstKeywords["BO"] = 0;
output.insert("1OVERBO",
Opm::UnitSystem::measure::oil_inverse_formation_volume_factor,
adbToDoubleVector(sd.rq[liquid_idx].b),
data::TargetType::RESTART_AUXILLARY);
}
if (vapour_active && rstKeywords["BG"] > 0) {
rstKeywords["BG"] = 0;
output.insert("1OVERBG",
Opm::UnitSystem::measure::gas_inverse_formation_volume_factor,
adbToDoubleVector(sd.rq[vapour_idx].b),
data::TargetType::RESTART_AUXILLARY);
}
/**
* Densities for water, oil gas
*/
if (rstKeywords["DEN"] > 0) {
rstKeywords["DEN"] = 0;
if (aqua_active) {
output.insert("WAT_DEN",
Opm::UnitSystem::measure::density,
adbToDoubleVector(sd.rq[aqua_idx].rho),
data::TargetType::RESTART_AUXILLARY);
}
if (liquid_active) {
output.insert("OIL_DEN",
Opm::UnitSystem::measure::density,
adbToDoubleVector(sd.rq[liquid_idx].rho),
data::TargetType::RESTART_AUXILLARY);
}
if (vapour_active) {
output.insert("GAS_DEN",
Opm::UnitSystem::measure::density,
adbToDoubleVector(sd.rq[vapour_idx].rho),
data::TargetType::RESTART_AUXILLARY);
}
}
/**
* Viscosities for water, oil gas
*/
if (rstKeywords["VISC"] > 0) {
rstKeywords["VISC"] = 0;
if (aqua_active) {
output.insert("WAT_VISC",
Opm::UnitSystem::measure::viscosity,
adbToDoubleVector(sd.rq[aqua_idx].mu),
data::TargetType::RESTART_AUXILLARY);
}
if (liquid_active) {
output.insert("OIL_VISC",
Opm::UnitSystem::measure::viscosity,
adbToDoubleVector(sd.rq[liquid_idx].mu),
data::TargetType::RESTART_AUXILLARY);
}
if (vapour_active) {
output.insert("GAS_VISC",
Opm::UnitSystem::measure::viscosity,
adbToDoubleVector(sd.rq[vapour_idx].mu),
data::TargetType::RESTART_AUXILLARY);
}
}
/**
* Relative permeabilities for water, oil, gas
*/
if (aqua_active && rstKeywords["KRW"] > 0) {
if (sd.rq[aqua_idx].kr.size() > 0) {
rstKeywords["KRW"] = 0;
output.insert("WATKR",
Opm::UnitSystem::measure::permeability,
adbToDoubleVector(sd.rq[aqua_idx].kr),
data::TargetType::RESTART_AUXILLARY);
}
else {
if ( log )
{
Opm::OpmLog::warning("Empty:WATKR",
"Not emitting empty Water Rel-Perm");
}
}
}
if (liquid_active && rstKeywords["KRO"] > 0) {
if (sd.rq[liquid_idx].kr.size() > 0) {
rstKeywords["KRO"] = 0;
output.insert("OILKR",
Opm::UnitSystem::measure::permeability,
adbToDoubleVector(sd.rq[liquid_idx].kr),
data::TargetType::RESTART_AUXILLARY);
}
else {
if ( log )
{
Opm::OpmLog::warning("Empty:OILKR",
"Not emitting empty Oil Rel-Perm");
}
}
}
if (vapour_active && rstKeywords["KRG"] > 0) {
if (sd.rq[vapour_idx].kr.size() > 0) {
rstKeywords["KRG"] = 0;
output.insert("GASKR",
Opm::UnitSystem::measure::permeability,
adbToDoubleVector(sd.rq[vapour_idx].kr),
data::TargetType::RESTART_AUXILLARY);
}
else {
if ( log )
{
Opm::OpmLog::warning("Empty:GASKR",
"Not emitting empty Gas Rel-Perm");
}
}
}
/**
* Vaporized and dissolved gas/oil ratio
*/
if (vapour_active && liquid_active && rstKeywords["RSSAT"] > 0) {
rstKeywords["RSSAT"] = 0;
output.insert("RSSAT",
Opm::UnitSystem::measure::gas_oil_ratio,
adbToDoubleVector(sd.rsSat),
data::TargetType::RESTART_AUXILLARY);
}
if (vapour_active && liquid_active && rstKeywords["RVSAT"] > 0) {
rstKeywords["RVSAT"] = 0;
output.insert("RVSAT",
Opm::UnitSystem::measure::oil_gas_ratio,
adbToDoubleVector(sd.rvSat),
data::TargetType::RESTART_AUXILLARY);
}
/**
* Bubble point and dew point pressures
*/
if (log && vapour_active &&
liquid_active && rstKeywords["PBPD"] > 0) {
rstKeywords["PBPD"] = 0;
Opm::OpmLog::warning("Bubble/dew point pressure output unsupported",
"Writing bubble points and dew points (PBPD) to file is unsupported, "
"as the simulator does not use these internally.");
}
//Warn for any unhandled keyword
if (log) {
for (auto& keyValue : rstKeywords) {
if (keyValue.second > 0) {
std::string logstring = "Keyword '";
logstring.append(keyValue.first);
logstring.append("' is unhandled for output to file.");
Opm::OpmLog::warning("Unhandled output keyword", logstring);
}
}
}
}
/**
* Checks if the summaryConfig has a keyword with the standardized field, region, or block prefixes.
*/
inline bool hasFRBKeyword(const SummaryConfig& summaryConfig, const std::string keyword) {
std::string field_kw = "F" + keyword;
std::string region_kw = "R" + keyword;
std::string block_kw = "B" + keyword;
return summaryConfig.hasKeyword(field_kw)
|| summaryConfig.hasKeyword(region_kw)
|| summaryConfig.hasKeyword(block_kw);
}
/**
* Returns the data as asked for in the summaryConfig
*/
template
void getSummaryData(data::Solution& output,
const Opm::PhaseUsage& phaseUsage,
const Model& physicalModel,
const SummaryConfig& summaryConfig) {
typedef Opm::AutoDiffBlock ADB;
const typename Model::SimulatorData& sd = physicalModel.getSimulatorData();
//Get shorthands for water, oil, gas
const int aqua_active = phaseUsage.phase_used[Opm::PhaseUsage::Aqua];
const int liquid_active = phaseUsage.phase_used[Opm::PhaseUsage::Liquid];
const int vapour_active = phaseUsage.phase_used[Opm::PhaseUsage::Vapour];
/**
* Now process all of the summary config files
*/
// Water in place
if (aqua_active && hasFRBKeyword(summaryConfig, "WIP")) {
output.insert("WIP",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(sd.fip[Model::SimulatorData::FIP_AQUA]),
data::TargetType::SUMMARY );
}
if (liquid_active) {
const ADB::V& oipl = sd.fip[Model::SimulatorData::FIP_LIQUID];
const ADB::V& oipg = vapour_active ? sd.fip[Model::SimulatorData::FIP_VAPORIZED_OIL] : ADB::V();
const ADB::V& oip = vapour_active ? oipl + oipg : oipl;
//Oil in place (liquid phase only)
if (hasFRBKeyword(summaryConfig, "OIPL")) {
output.insert("OIPL",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(oipl),
data::TargetType::SUMMARY );
}
//Oil in place (gas phase only)
if (hasFRBKeyword(summaryConfig, "OIPG")) {
output.insert("OIPG",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(oipg),
data::TargetType::SUMMARY );
}
// Oil in place (in liquid and gas phases)
if (hasFRBKeyword(summaryConfig, "OIP")) {
output.insert("OIP",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(oip),
data::TargetType::SUMMARY );
}
}
if (vapour_active) {
const ADB::V& gipg = sd.fip[Model::SimulatorData::FIP_VAPOUR];
const ADB::V& gipl = liquid_active ? sd.fip[Model::SimulatorData::FIP_DISSOLVED_GAS] : ADB::V();
const ADB::V& gip = liquid_active ? gipg + gipl : gipg;
// Gas in place (gas phase only)
if (hasFRBKeyword(summaryConfig, "GIPG")) {
output.insert("GIPG",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(gipg),
data::TargetType::SUMMARY );
}
// Gas in place (liquid phase only)
if (hasFRBKeyword(summaryConfig, "GIPL")) {
output.insert("GIPL",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(gipl),
data::TargetType::SUMMARY );
}
// Gas in place (in both liquid and gas phases)
if (hasFRBKeyword(summaryConfig, "GIP")) {
output.insert("GIP",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(gip),
data::TargetType::SUMMARY );
}
}
// Cell pore volume in reservoir conditions
if (hasFRBKeyword(summaryConfig, "RPV")) {
output.insert("RPV",
Opm::UnitSystem::measure::volume,
adbVToDoubleVector(sd.fip[Model::SimulatorData::FIP_PV]),
data::TargetType::SUMMARY );
}
// Pressure averaged value (hydrocarbon pore volume weighted)
if (summaryConfig.hasKeyword("FPRH") || summaryConfig.hasKeyword("RPRH")) {
output.insert("PRH",
Opm::UnitSystem::measure::pressure,
adbVToDoubleVector(sd.fip[Model::SimulatorData::FIP_WEIGHTED_PRESSURE]),
data::TargetType::SUMMARY );
}
}
}
template
inline void
BlackoilOutputWriter::
writeTimeStep(const SimulatorTimerInterface& timer,
const SimulationDataContainer& localState,
const WellState& localWellState,
const Model& physicalModel,
bool substep)
{
const RestartConfig& restartConfig = eclipseState_.getRestartConfig();
const SummaryConfig& summaryConfig = eclipseState_.getSummaryConfig();
const int reportStepNum = timer.reportStepNum();
bool logMessages = output_ && parallelOutput_->isIORank();
data::Solution cellData;
detail::getRestartData( cellData, phaseUsage_, physicalModel,
restartConfig, reportStepNum, logMessages );
detail::getSummaryData( cellData, phaseUsage_, physicalModel, summaryConfig );
writeTimeStepWithCellProperties(timer, localState, localWellState, cellData, substep);
}
}
#endif