mirror of
https://github.com/OPM/opm-simulators.git
synced 2024-12-01 13:29:08 -06:00
60fe6b2687
It appears summary files by convention expect all substep data points to be chunked together with the data point at the end of the report step.
472 lines
18 KiB
C++
472 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";
|
|
ensureDirectoryExists(vtkfilename.str());
|
|
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;
|
|
ensureDirectoryExists(fname.str());
|
|
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;
|
|
ensureDirectoryExists(vtkpath.str());
|
|
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, double> miscSummaryData_;
|
|
std::map<std::string, std::vector<double>> extraRestartData_;
|
|
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, double>& miscSummaryData,
|
|
const std::map<std::string, std::vector<double>>& extraRestartData,
|
|
bool substep)
|
|
: writer_( writer ),
|
|
timer_( timer.clone() ),
|
|
state_( state ),
|
|
wellState_( wellState ),
|
|
simProps_( simProps ),
|
|
miscSummaryData_( miscSummaryData ),
|
|
extraRestartData_( extraRestartData ),
|
|
substep_( substep )
|
|
{
|
|
}
|
|
|
|
// callback to writer's serial writeTimeStep method
|
|
void run ()
|
|
{
|
|
// write data
|
|
writer_.writeTimeStepSerial( *timer_, state_, wellState_, simProps_, miscSummaryData_, extraRestartData_, substep_ );
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
writeTimeStepWithoutCellProperties(
|
|
const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& localState,
|
|
const WellStateFullyImplicitBlackoil& localWellState,
|
|
const std::map<std::string, double>& miscSummaryData,
|
|
const std::map<std::string, std::vector<double>>& extraRestartData,
|
|
bool substep)
|
|
{
|
|
data::Solution localCellData{};
|
|
if( output_ )
|
|
{
|
|
localCellData = simToSolution(localState, restart_double_si_, phaseUsage_); // Get "normal" data (SWAT, PRESSURE, ...);
|
|
}
|
|
writeTimeStepWithCellProperties(timer, localState, localCellData ,
|
|
localWellState, miscSummaryData, extraRestartData, substep);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
BlackoilOutputWriter::
|
|
writeTimeStepWithCellProperties(
|
|
const SimulatorTimerInterface& timer,
|
|
const SimulationDataContainer& localState,
|
|
const data::Solution& localCellData,
|
|
const WellStateFullyImplicitBlackoil& localWellState,
|
|
const std::map<std::string, double>& miscSummaryData,
|
|
const std::map<std::string, std::vector<double>>& extraRestartData,
|
|
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, miscSummaryData, extraRestartData, substep ) );
|
|
}
|
|
else {
|
|
// just write the data to disk
|
|
try {
|
|
writeTimeStepSerial( timer, state, wellState, cellData, miscSummaryData, extraRestartData, 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, double>& miscSummaryData,
|
|
const std::map<std::string, std::vector<double>>& extraRestartData,
|
|
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 {
|
|
if ( timer.initialStep() )
|
|
{
|
|
// Set the initial OIP
|
|
eclIO_->overwriteInitialOIP(simProps);
|
|
}
|
|
// ... insert "extra" data (KR, VISC, ...)
|
|
const int reportStepForOutput = substep ? timer.reportStepNum() + 1 : timer.reportStepNum();
|
|
eclIO_->writeTimeStep(reportStepForOutput,
|
|
substep,
|
|
timer.simulationTimeElapsed(),
|
|
simProps,
|
|
wellState.report(phaseUsage_),
|
|
miscSummaryData,
|
|
extraRestartData,
|
|
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();
|
|
}
|
|
}
|