mirror of
https://github.com/OPM/opm-simulators.git
synced 2024-11-25 10:40:21 -06:00
477 lines
18 KiB
C++
477 lines
18 KiB
C++
/*
|
|
Copyright (c) 2014 SINTEF ICT, Applied Mathematics.
|
|
Copyright (c) 2015-2016 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include "SimulatorFullyImplicitBlackoilOutput.hpp"
|
|
|
|
#include <opm/common/data/SimulationDataContainer.hpp>
|
|
|
|
#include <opm/parser/eclipse/EclipseState/InitConfig/InitConfig.hpp>
|
|
#include <opm/output/data/Cells.hpp>
|
|
#include <opm/core/simulator/BlackoilState.hpp>
|
|
#include <opm/core/utility/DataMap.hpp>
|
|
#include <opm/autodiff/Compat.hpp>
|
|
#include <opm/simulators/vtk/writeVtkData.hpp>
|
|
#include <opm/common/ErrorMacros.hpp>
|
|
#include <opm/core/utility/miscUtilities.hpp>
|
|
#include <opm/parser/eclipse/Units/Units.hpp>
|
|
|
|
#include <opm/autodiff/GridHelpers.hpp>
|
|
#include <opm/autodiff/BackupRestore.hpp>
|
|
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
//For OutputWriterHelper
|
|
#include <map>
|
|
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
|
|
|
|
|
|
#ifdef HAVE_OPM_GRID
|
|
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
|
#include <dune/common/version.hh>
|
|
#include <dune/grid/io/file/vtk/vtkwriter.hh>
|
|
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
|
#endif
|
|
namespace Opm
|
|
{
|
|
|
|
|
|
|
|
void outputStateVtk(const UnstructuredGrid& grid,
|
|
const SimulationDataContainer& state,
|
|
const int step,
|
|
const std::string& output_dir)
|
|
{
|
|
// Write data in VTK format.
|
|
std::ostringstream vtkfilename;
|
|
vtkfilename << output_dir << "/vtk_files";
|
|
boost::filesystem::path fpath(vtkfilename.str());
|
|
try {
|
|
create_directories(fpath);
|
|
}
|
|
catch (...) {
|
|
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
|
}
|
|
vtkfilename << "/output-" << std::setw(3) << std::setfill('0') << step << ".vtu";
|
|
std::ofstream vtkfile(vtkfilename.str().c_str());
|
|
if (!vtkfile) {
|
|
OPM_THROW(std::runtime_error, "Failed to open " << vtkfilename.str());
|
|
}
|
|
Opm::DataMap dm;
|
|
dm["saturation"] = &state.saturation();
|
|
dm["pressure"] = &state.pressure();
|
|
std::vector<double> cell_velocity;
|
|
Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid),
|
|
AutoDiffGrid::numFaces(grid),
|
|
AutoDiffGrid::beginFaceCentroids(grid),
|
|
AutoDiffGrid::faceCells(grid),
|
|
AutoDiffGrid::beginCellCentroids(grid),
|
|
AutoDiffGrid::beginCellVolumes(grid),
|
|
AutoDiffGrid::dimensions(grid),
|
|
state.faceflux(), cell_velocity);
|
|
dm["velocity"] = &cell_velocity;
|
|
Opm::writeVtkData(grid, dm, vtkfile);
|
|
}
|
|
|
|
void outputWellStateMatlab(const Opm::WellState& well_state,
|
|
const int step,
|
|
const std::string& output_dir)
|
|
{
|
|
Opm::DataMap dm;
|
|
dm["bhp"] = &well_state.bhp();
|
|
dm["wellrates"] = &well_state.wellRates();
|
|
|
|
// 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<double>& d = *(it->second);
|
|
std::copy(d.begin(), d.end(), std::ostream_iterator<double>(file, "\n"));
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
void outputWaterCut(const Opm::Watercut& watercut,
|
|
const std::string& output_dir)
|
|
{
|
|
// Write water cut curve.
|
|
std::string fname = output_dir + "/watercut.txt";
|
|
std::ofstream os(fname.c_str());
|
|
if (!os) {
|
|
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
|
}
|
|
watercut.write(os);
|
|
}
|
|
|
|
void outputWellReport(const Opm::WellReport& wellreport,
|
|
const std::string& output_dir)
|
|
{
|
|
// Write well report.
|
|
std::string fname = output_dir + "/wellreport.txt";
|
|
std::ofstream os(fname.c_str());
|
|
if (!os) {
|
|
OPM_THROW(std::runtime_error, "Failed to open " << fname);
|
|
}
|
|
wellreport.write(os);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_OPM_GRID
|
|
void outputStateVtk(const Dune::CpGrid& grid,
|
|
const Opm::SimulationDataContainer& state,
|
|
const int step,
|
|
const std::string& output_dir)
|
|
{
|
|
// Write data in VTK format.
|
|
std::ostringstream vtkfilename;
|
|
std::ostringstream vtkpath;
|
|
vtkpath << output_dir << "/vtk_files";
|
|
vtkpath << "/output-" << std::setw(3) << std::setfill('0') << step;
|
|
boost::filesystem::path fpath(vtkpath.str());
|
|
try {
|
|
create_directories(fpath);
|
|
}
|
|
catch (...) {
|
|
OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath);
|
|
}
|
|
vtkfilename << "output-" << std::setw(3) << std::setfill('0') << step;
|
|
#if DUNE_VERSION_NEWER(DUNE_GRID, 2, 3)
|
|
Dune::VTKWriter<Dune::CpGrid::LeafGridView> writer(grid.leafGridView(), Dune::VTK::nonconforming);
|
|
#else
|
|
Dune::VTKWriter<Dune::CpGrid::LeafGridView> writer(grid.leafView(), Dune::VTK::nonconforming);
|
|
#endif
|
|
writer.addCellData(state.saturation(), "saturation", state.numPhases());
|
|
writer.addCellData(state.pressure(), "pressure", 1);
|
|
|
|
std::vector<double> cell_velocity;
|
|
Opm::estimateCellVelocity(AutoDiffGrid::numCells(grid),
|
|
AutoDiffGrid::numFaces(grid),
|
|
AutoDiffGrid::beginFaceCentroids(grid),
|
|
AutoDiffGrid::faceCells(grid),
|
|
AutoDiffGrid::beginCellCentroids(grid),
|
|
AutoDiffGrid::beginCellVolumes(grid),
|
|
AutoDiffGrid::dimensions(grid),
|
|
state.faceflux(), cell_velocity);
|
|
writer.addCellData(cell_velocity, "velocity", Dune::CpGrid::dimension);
|
|
writer.pwrite(vtkfilename.str(), vtkpath.str(), std::string("."), Dune::VTK::ascii);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
struct WriterCall : public ThreadHandle :: ObjectInterface
|
|
{
|
|
BlackoilOutputWriter& writer_;
|
|
std::unique_ptr< SimulatorTimerInterface > timer_;
|
|
const SimulationDataContainer state_;
|
|
const WellStateFullyImplicitBlackoil wellState_;
|
|
data::Solution simProps_;
|
|
std::map<std::string, std::vector<double>> extraData_;
|
|
const bool substep_;
|
|
|
|
explicit WriterCall( BlackoilOutputWriter& writer,
|
|
const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& state,
|
|
const WellStateFullyImplicitBlackoil& wellState,
|
|
const data::Solution& simProps,
|
|
const std::map<std::string, std::vector<double>>& extraData,
|
|
bool substep )
|
|
: writer_( writer ),
|
|
timer_( timer.clone() ),
|
|
state_( state ),
|
|
wellState_( wellState ),
|
|
simProps_( simProps ),
|
|
extraData_( extraData ),
|
|
substep_( substep )
|
|
{
|
|
}
|
|
|
|
// callback to writer's serial writeTimeStep method
|
|
void run ()
|
|
{
|
|
// write data
|
|
writer_.writeTimeStepSerial( *timer_, state_, wellState_, simProps_, extraData_, substep_ );
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
writeTimeStepWithoutCellProperties(
|
|
const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& localState,
|
|
const WellStateFullyImplicitBlackoil& localWellState,
|
|
const std::map<std::string, std::vector<double>>& extraData,
|
|
bool substep)
|
|
{
|
|
data::Solution localCellData{};
|
|
if( output_ )
|
|
{
|
|
localCellData = simToSolution(localState, restart_double_si_, phaseUsage_); // Get "normal" data (SWAT, PRESSURE, ...);
|
|
}
|
|
writeTimeStepWithCellProperties(timer, localState, localCellData ,
|
|
localWellState, extraData, substep);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
writeTimeStepWithCellProperties(
|
|
const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& localState,
|
|
const data::Solution& localCellData,
|
|
const WellStateFullyImplicitBlackoil& localWellState,
|
|
const std::map<std::string, std::vector<double>>& extraData,
|
|
bool substep)
|
|
{
|
|
// VTK output (is parallel if grid is parallel)
|
|
if( vtkWriter_ ) {
|
|
vtkWriter_->writeTimeStep( timer, localState, localWellState, false );
|
|
}
|
|
|
|
bool isIORank = output_ ;
|
|
if( parallelOutput_ && parallelOutput_->isParallel() )
|
|
{
|
|
// If this is not the initial write and no substep, then the well
|
|
// state used in the computation is actually the one of the last
|
|
// step. We need that well state for the gathering. Otherwise
|
|
// It an exception with a message like "global state does not
|
|
// contain well ..." might be thrown.
|
|
int wellStateStepNumber = ( ! substep && timer.reportStepNum() > 0) ?
|
|
(timer.reportStepNum() - 1) : timer.reportStepNum();
|
|
// collect all solutions to I/O rank
|
|
isIORank = parallelOutput_->collectToIORank( localState, localWellState,
|
|
localCellData,
|
|
wellStateStepNumber );
|
|
// Note that at this point the extraData are assumed to be global, i.e. identical across all processes.
|
|
}
|
|
|
|
const data::Solution& cellData = ( parallelOutput_ && parallelOutput_->isParallel() ) ? parallelOutput_->globalCellData() : localCellData;
|
|
const SimulationDataContainer& state = (parallelOutput_ && parallelOutput_->isParallel() ) ? parallelOutput_->globalReservoirState() : localState;
|
|
const WellStateFullyImplicitBlackoil& wellState = (parallelOutput_ && parallelOutput_->isParallel() ) ? parallelOutput_->globalWellState() : localWellState;
|
|
|
|
// serial output is only done on I/O rank
|
|
int err = 0;
|
|
std::string emsg;
|
|
if( isIORank )
|
|
{
|
|
if( asyncOutput_ ) {
|
|
// dispatch the write call to the extra thread
|
|
asyncOutput_->dispatch( detail::WriterCall( *this, timer, state, wellState, cellData, extraData, substep ) );
|
|
}
|
|
else {
|
|
// just write the data to disk
|
|
try {
|
|
writeTimeStepSerial( timer, state, wellState, cellData, extraData, substep );
|
|
} catch (std::runtime_error& msg) {
|
|
err = 1;
|
|
emsg = msg.what();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!asyncOutput_) {
|
|
#if HAVE_MPI
|
|
MPI_Bcast(&err, 1, MPI_INT, 0, MPI_COMM_WORLD);
|
|
#endif
|
|
if (err) {
|
|
if (isIORank) {
|
|
throw std::runtime_error(emsg);
|
|
} else {
|
|
throw std::runtime_error("I/O process encountered problems.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
writeTimeStepSerial(const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& state,
|
|
const WellStateFullyImplicitBlackoil& wellState,
|
|
const data::Solution& simProps,
|
|
const std::map<std::string, std::vector<double>>& extraData,
|
|
bool substep)
|
|
{
|
|
// Matlab output
|
|
if( matlabWriter_ ) {
|
|
matlabWriter_->writeTimeStep( timer, state, wellState, substep );
|
|
}
|
|
|
|
// ECL output
|
|
if ( eclIO_ )
|
|
{
|
|
const auto& initConfig = eclipseState_.getInitConfig();
|
|
if (initConfig.restartRequested() && ((initConfig.getRestartStep()) == (timer.currentStepNum()))) {
|
|
std::cout << "Skipping restart write in start of step " << timer.currentStepNum() << std::endl;
|
|
} else {
|
|
// ... insert "extra" data (KR, VISC, ...)
|
|
eclIO_->writeTimeStep(timer.reportStepNum(),
|
|
substep,
|
|
timer.simulationTimeElapsed(),
|
|
simProps,
|
|
wellState.report(phaseUsage_),
|
|
extraData,
|
|
restart_double_si_);
|
|
}
|
|
}
|
|
|
|
// write backup file
|
|
if( backupfile_.is_open() )
|
|
{
|
|
int reportStep = timer.reportStepNum();
|
|
int currentTimeStep = timer.currentStepNum();
|
|
if( (reportStep == currentTimeStep || // true for SimulatorTimer
|
|
currentTimeStep == 0 || // true for AdaptiveSimulatorTimer at reportStep
|
|
timer.done() ) // true for AdaptiveSimulatorTimer at reportStep
|
|
&& lastBackupReportStep_ != reportStep ) // only backup report step once
|
|
{
|
|
// store report step
|
|
lastBackupReportStep_ = reportStep;
|
|
// write resport step number
|
|
backupfile_.write( (const char *) &reportStep, sizeof(int) );
|
|
|
|
try {
|
|
backupfile_ << state;
|
|
|
|
const WellStateFullyImplicitBlackoil& boWellState = static_cast< const WellStateFullyImplicitBlackoil& > (wellState);
|
|
backupfile_ << boWellState;
|
|
}
|
|
catch ( const std::bad_cast& e )
|
|
{
|
|
}
|
|
|
|
backupfile_ << std::flush;
|
|
}
|
|
} // end backup
|
|
}
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
restore(SimulatorTimerInterface& timer,
|
|
BlackoilState& state,
|
|
WellStateFullyImplicitBlackoil& wellState,
|
|
const std::string& filename,
|
|
const int desiredResportStep )
|
|
{
|
|
std::ifstream restorefile( filename.c_str() );
|
|
if( restorefile )
|
|
{
|
|
std::cout << "============================================================================"<<std::endl;
|
|
std::cout << "Restoring from ";
|
|
if( desiredResportStep < 0 ) {
|
|
std::cout << "last";
|
|
}
|
|
else {
|
|
std::cout << desiredResportStep;
|
|
}
|
|
std::cout << " report step! filename = " << filename << std::endl << std::endl;
|
|
|
|
int reportStep;
|
|
restorefile.read( (char *) &reportStep, sizeof(int) );
|
|
|
|
const int readReportStep = (desiredResportStep < 0) ?
|
|
std::numeric_limits<int>::max() : desiredResportStep;
|
|
|
|
while( reportStep <= readReportStep && ! timer.done() && restorefile )
|
|
{
|
|
restorefile >> state;
|
|
restorefile >> wellState;
|
|
|
|
// No per cell data is written for restore steps, but will be
|
|
// for subsequent steps, when we have started simulating
|
|
writeTimeStepWithoutCellProperties( timer, state, wellState, {} );
|
|
|
|
// some output
|
|
std::cout << "Restored step " << timer.reportStepNum() << " at day "
|
|
<< unit::convert::to(timer.simulationTimeElapsed(),unit::day) << std::endl;
|
|
|
|
if( readReportStep == reportStep ) {
|
|
break;
|
|
}
|
|
|
|
// if the stream is not valid anymore we just use the last state read
|
|
if( ! restorefile ) {
|
|
std::cerr << "Reached EOF, using last state read!" << std::endl;
|
|
break;
|
|
}
|
|
|
|
// try to read next report step
|
|
restorefile.read( (char *) &reportStep, sizeof(int) );
|
|
|
|
// if read failed, exit loop
|
|
if( ! restorefile ) {
|
|
break;
|
|
}
|
|
|
|
// next step
|
|
timer.advance();
|
|
|
|
if( timer.reportStepNum() != reportStep ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Warning: Couldn't open restore file '" << filename << "'" << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
bool BlackoilOutputWriter::isRestart() const {
|
|
const auto& initconfig = eclipseState_.getInitConfig();
|
|
return initconfig.restartRequested();
|
|
}
|
|
|
|
|
|
bool BlackoilOutputWriter::requireFIPNUM() const {
|
|
return eclipseState_.getSummaryConfig().requireFIPNUM();
|
|
}
|
|
}
|