Merge remote-tracking branch 'upstream/master' into opm-parser-integrate
This commit is contained in:
commit
0fc56949ef
@ -37,10 +37,11 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/core/grid/cpgpreprocess/geometry.c
|
||||
opm/core/grid/cpgpreprocess/preprocess.c
|
||||
opm/core/grid/cpgpreprocess/uniquepoints.c
|
||||
opm/core/io/eclipse/BlackoilEclipseOutputWriter.cpp
|
||||
opm/core/io/eclipse/EclipseGridInspector.cpp
|
||||
opm/core/io/eclipse/EclipseGridParser.cpp
|
||||
opm/core/io/eclipse/EclipseWriter.cpp
|
||||
opm/core/io/eclipse/writeECLData.cpp
|
||||
opm/core/io/OutputWriter.cpp
|
||||
opm/core/io/vag/vag.cpp
|
||||
opm/core/io/vtk/writeVtkData.cpp
|
||||
opm/core/linalg/LinearSolverFactory.cpp
|
||||
@ -98,6 +99,7 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/core/simulator/BlackoilState.cpp
|
||||
opm/core/simulator/SimulatorCompressibleTwophase.cpp
|
||||
opm/core/simulator/SimulatorIncompTwophase.cpp
|
||||
opm/core/simulator/SimulatorOutput.cpp
|
||||
opm/core/simulator/SimulatorReport.cpp
|
||||
opm/core/simulator/SimulatorState.cpp
|
||||
opm/core/simulator/SimulatorTimer.cpp
|
||||
@ -231,14 +233,15 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/grid/cpgpreprocess/grdecl.h
|
||||
opm/core/grid/cpgpreprocess/preprocess.h
|
||||
opm/core/grid/cpgpreprocess/uniquepoints.h
|
||||
opm/core/io/eclipse/BlackoilEclipseOutputWriter.hpp
|
||||
opm/core/io/eclipse/CornerpointChopper.hpp
|
||||
opm/core/io/eclipse/EclipseGridInspector.hpp
|
||||
opm/core/io/eclipse/EclipseGridParser.hpp
|
||||
opm/core/io/eclipse/EclipseGridParserHelpers.hpp
|
||||
opm/core/io/eclipse/EclipseUnits.hpp
|
||||
opm/core/io/eclipse/EclipseWriter.hpp
|
||||
opm/core/io/eclipse/SpecialEclipseFields.hpp
|
||||
opm/core/io/eclipse/writeECLData.hpp
|
||||
opm/core/io/OutputWriter.hpp
|
||||
opm/core/io/vag/vag.hpp
|
||||
opm/core/io/vtk/writeVtkData.hpp
|
||||
opm/core/linalg/LinearSolverFactory.hpp
|
||||
@ -306,6 +309,7 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/simulator/BlackoilState.hpp
|
||||
opm/core/simulator/SimulatorCompressibleTwophase.hpp
|
||||
opm/core/simulator/SimulatorIncompTwophase.hpp
|
||||
opm/core/simulator/SimulatorOutput.hpp
|
||||
opm/core/simulator/SimulatorReport.hpp
|
||||
opm/core/simulator/SimulatorState.hpp
|
||||
opm/core/simulator/SimulatorTimer.hpp
|
||||
@ -375,6 +379,7 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/utility/parameters/tinyxml/tinystr.h
|
||||
opm/core/utility/parameters/tinyxml/tinyxml.h
|
||||
opm/core/utility/PropertySystem.hpp
|
||||
opm/core/utility/share_obj.hpp
|
||||
opm/core/wells/InjectionSpecification.hpp
|
||||
opm/core/wells/ProductionSpecification.hpp
|
||||
opm/core/wells/WellCollection.hpp
|
||||
|
96
opm/core/io/OutputWriter.cpp
Normal file
96
opm/core/io/OutputWriter.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "OutputWriter.hpp"
|
||||
|
||||
#include <opm/core/io/eclipse/EclipseWriter.hpp>
|
||||
#include <opm/core/utility/parameters/Parameter.hpp>
|
||||
#include <opm/core/utility/parameters/ParameterGroup.hpp>
|
||||
|
||||
#include <forward_list>
|
||||
#include <map>
|
||||
#include <memory> // unique_ptr
|
||||
|
||||
using namespace std;
|
||||
using namespace Opm;
|
||||
using namespace Opm::parameter;
|
||||
|
||||
namespace {
|
||||
|
||||
/// Multiplexer over a list of output writers
|
||||
struct MultiWriter : public OutputWriter {
|
||||
/// Shorthand for a list of owned output writers
|
||||
typedef forward_list <unique_ptr <OutputWriter> > writers_t;
|
||||
typedef writers_t::iterator it_t;
|
||||
typedef unique_ptr <writers_t> ptr_t;
|
||||
|
||||
/// Adopt a list of writers
|
||||
MultiWriter (ptr_t writers) : writers_ (std::move (writers)) { }
|
||||
|
||||
/// Forward the call to all writers
|
||||
virtual void writeInit(const SimulatorTimer &timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState) {
|
||||
for (it_t it = writers_->begin (); it != writers_->end (); ++it) {
|
||||
(*it)->writeInit (timer, reservoirState, wellState);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void writeTimeStep(const SimulatorTimer& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState) {
|
||||
for (it_t it = writers_->begin (); it != writers_->end(); ++it) {
|
||||
(*it)->writeTimeStep (timer, reservoirState, wellState);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ptr_t writers_;
|
||||
};
|
||||
|
||||
/// Psuedo-constructor, can appear in template
|
||||
template <typename Format> unique_ptr <OutputWriter>
|
||||
create (const ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid) {
|
||||
return unique_ptr <OutputWriter> (new Format (params, parser, grid));
|
||||
}
|
||||
|
||||
/// Map between keyword in configuration and the corresponding
|
||||
/// constructor function (type) that should be called when detected.
|
||||
/// The writer must have a constructor which takes params and parser.
|
||||
///
|
||||
/// If you want to add more possible writer formats, just add them
|
||||
/// to the list below!
|
||||
typedef map <const char*, unique_ptr <OutputWriter> (*)(
|
||||
const ParameterGroup&,
|
||||
std::shared_ptr <const EclipseGridParser>,
|
||||
std::shared_ptr <const UnstructuredGrid>)> map_t;
|
||||
map_t FORMATS = {
|
||||
{ "output_ecl", &create <EclipseWriter> },
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
unique_ptr <OutputWriter>
|
||||
OutputWriter::create (const ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid) {
|
||||
// allocate a list which will be filled with writers. this list
|
||||
// is initially empty (no output).
|
||||
MultiWriter::ptr_t list (new MultiWriter::writers_t ());
|
||||
|
||||
// loop through the map and see if we can find the key that is
|
||||
// specified there
|
||||
typedef map_t::iterator map_it_t;
|
||||
for (map_it_t it = FORMATS.begin (); it != FORMATS.end(); ++it) {
|
||||
// keyword which would indicate that this format should be used
|
||||
const std::string name (it->first);
|
||||
|
||||
// invoke the constructor for the type if we found the keyword
|
||||
// and put the pointer to this writer onto the list
|
||||
if (params.getDefault <bool> (name, false)) {
|
||||
list->push_front (it->second (params, parser, grid));
|
||||
}
|
||||
}
|
||||
|
||||
// create a multiplexer from the list of formats we found
|
||||
return unique_ptr <OutputWriter> (new MultiWriter (std::move (list)));
|
||||
}
|
117
opm/core/io/OutputWriter.hpp
Normal file
117
opm/core/io/OutputWriter.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright (c) 2013 Uni Research 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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_OUTPUT_WRITER_HPP
|
||||
#define OPM_OUTPUT_WRITER_HPP
|
||||
|
||||
#include <memory> // unique_ptr, shared_ptr
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
// forward declaration
|
||||
class EclipseGridParser;
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class SimulatorState;
|
||||
class SimulatorTimer;
|
||||
class WellState;
|
||||
|
||||
/*!
|
||||
* Interface for writing non-compositional (blackoil, two-phase) simulation
|
||||
* state to files.
|
||||
*
|
||||
* Use the create() function to setup a chain of writer based on the
|
||||
* configuration values, e.g.
|
||||
*
|
||||
* \example
|
||||
* \code{.cpp}
|
||||
* ParameterGroup params (argc, argv, false);
|
||||
* auto parser = std::make_shared <EclipseGridParser> (
|
||||
* params.get <string> ("deck_filename"));
|
||||
*
|
||||
* std::unique_ptr <OutputWriter> writer =
|
||||
* OutputWriter::create (params, parser);
|
||||
*
|
||||
* // before the first timestep
|
||||
* writer->writeInit (timer);
|
||||
*
|
||||
* // after each timestep
|
||||
* writer->writeTimeStep (timer, state, wellState);
|
||||
*
|
||||
* \endcode
|
||||
*/
|
||||
class OutputWriter {
|
||||
public:
|
||||
/// Allow derived classes to be used in the unique_ptr that is returned
|
||||
/// from the create() method. (Every class that should be delete'd should
|
||||
/// have a proper constructor, and if the base class isn't virtual then
|
||||
/// the compiler won't call the right one when the unique_ptr goes out of
|
||||
/// scope).
|
||||
virtual ~OutputWriter () { }
|
||||
|
||||
/**
|
||||
* Write the static eclipse data (grid, PVT curves, etc) as well as the
|
||||
* initial state to disk.
|
||||
*
|
||||
* This routine should be called before the first timestep (i.e. when
|
||||
* timer.currentStepNum () == 0)
|
||||
*/
|
||||
virtual void writeInit(const SimulatorTimer &timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Write a blackoil reservoir state to disk for later inspection with
|
||||
* visualization tools like ResInsight
|
||||
*
|
||||
* \param[in] reservoirState The thermodynamic state of the reservoir
|
||||
* \param[in] wellState The production/injection data for all wells
|
||||
*
|
||||
* This routine should be called after the timestep has been advanced,
|
||||
* i.e. timer.currentStepNum () > 0.
|
||||
*/
|
||||
virtual void writeTimeStep(const SimulatorTimer& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState) = 0;
|
||||
|
||||
/*!
|
||||
* Create a suitable set of output formats based on configuration.
|
||||
*
|
||||
* @param params Configuration properties. This function will setup a
|
||||
* multiplexer of applicable output formats based on the
|
||||
* desired configuration values.
|
||||
*
|
||||
* @param parser Input deck used to set up the simulation. The lifetime
|
||||
* of this object must exceed the lifetime of the writer
|
||||
* that is returned.
|
||||
*
|
||||
* @return Pointer to a multiplexer to all applicable output formats.
|
||||
*
|
||||
* @see Opm::share_obj
|
||||
*/
|
||||
static std::unique_ptr <OutputWriter>
|
||||
create (const parameter::ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid);
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif /* OPM_OUTPUT_WRITER_HPP */
|
@ -1,459 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Andreas Lauser
|
||||
Copyright 2013 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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 "BlackoilEclipseOutputWriter.hpp"
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/io/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/utility/Units.hpp>
|
||||
#include <opm/core/utility/ErrorMacros.hpp>
|
||||
#include <opm/core/utility/DataMap.hpp>
|
||||
|
||||
#ifdef HAVE_ERT
|
||||
#include <ert/ecl/fortio.h>
|
||||
#include <ert/ecl/ecl_grid.h>
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
#include <ert/ecl/ecl_kw.h>
|
||||
#include <ert/ecl/ecl_sum.h>
|
||||
#include <ert/ecl/ecl_util.h>
|
||||
#include <ert/ecl/ecl_init_file.h>
|
||||
#include <ert/ecl/ecl_file.h>
|
||||
#include <ert/ecl/ecl_rst_file.h>
|
||||
#endif
|
||||
|
||||
namespace Opm {
|
||||
void BlackoilEclipseOutputWriter::writeInitFile(const SimulatorTimer &timer)
|
||||
{
|
||||
tm tm = boost::posix_time::to_tm(timer.currentDateTime());
|
||||
startTime_ = mktime(&tm);
|
||||
|
||||
#if HAVE_ERT
|
||||
writeGridInitFile_(timer);
|
||||
writeSummaryHeaderFile_(timer);
|
||||
#else
|
||||
OPM_THROW(std::runtime_error,
|
||||
"The ERT libraries are required to write ECLIPSE output files.");
|
||||
#endif // HAVE_ERT
|
||||
}
|
||||
|
||||
void BlackoilEclipseOutputWriter::writeReservoirState(const BlackoilState& reservoirState, const SimulatorTimer& timer)
|
||||
{
|
||||
#if HAVE_ERT
|
||||
ecl_file_enum file_type = ECL_UNIFIED_RESTART_FILE; // Alternatively ECL_RESTART_FILE for multiple restart files.
|
||||
bool fmt_file = false;
|
||||
|
||||
char *fileName = ecl_util_alloc_filename(outputDir_.c_str(),
|
||||
baseName_.c_str(),
|
||||
/*file_type=*/ECL_UNIFIED_RESTART_FILE,
|
||||
fmt_file,
|
||||
timer.currentStepNum());
|
||||
int phases = ECL_OIL_PHASE + ECL_GAS_PHASE + ECL_WATER_PHASE;
|
||||
double days = Opm::unit::convert::to(timer.currentTime(), Opm::unit::day);
|
||||
int nx = grid_.cartdims[0];
|
||||
int ny = grid_.cartdims[1];
|
||||
int nz = grid_.cartdims[2];
|
||||
int nactive = grid_.number_of_cells;
|
||||
ecl_rst_file_type* rst_file;
|
||||
|
||||
time_t curTime;
|
||||
tm tm = boost::posix_time::to_tm(timer.currentDateTime());
|
||||
curTime = mktime(&tm);
|
||||
|
||||
if (timer.currentStepNum() > 0 && file_type == ECL_UNIFIED_RESTART_FILE)
|
||||
rst_file = ecl_rst_file_open_append(fileName);
|
||||
else
|
||||
rst_file = ecl_rst_file_open_write(fileName);
|
||||
|
||||
ecl_rst_file_fwrite_header(rst_file, timer.currentStepNum(), curTime, days, nx, ny, nz, nactive, phases);
|
||||
ecl_rst_file_start_solution(rst_file);
|
||||
|
||||
{
|
||||
// convert the pressures from Pascals to bar because eclipse
|
||||
// seems to write bars
|
||||
std::vector<double> pressureBar(reservoirState.pressure());
|
||||
auto it = pressureBar.begin();
|
||||
const auto &endIt = pressureBar.end();
|
||||
for (; it != endIt; ++it)
|
||||
(*it) /= 1e5;
|
||||
|
||||
ecl_kw_type* pressure_kw = newEclDoubleKeyword_("PRESSURE", pressureBar);
|
||||
ecl_rst_file_add_kw(rst_file, pressure_kw);
|
||||
ecl_kw_free(pressure_kw);
|
||||
}
|
||||
|
||||
{
|
||||
ecl_kw_type* swat_kw = newEclDoubleKeyword_("SWAT", reservoirState.saturation(), /*offset=*/0, /*stride=*/3);
|
||||
ecl_rst_file_add_kw(rst_file, swat_kw);
|
||||
ecl_kw_free(swat_kw);
|
||||
}
|
||||
|
||||
{
|
||||
ecl_kw_type* soil_kw = newEclDoubleKeyword_("SOIL", reservoirState.saturation(), /*offset=*/1, /*stride=*/3);
|
||||
ecl_rst_file_add_kw(rst_file, soil_kw);
|
||||
ecl_kw_free(soil_kw);
|
||||
}
|
||||
|
||||
{
|
||||
ecl_kw_type* sgas_kw = newEclDoubleKeyword_("SGAS", reservoirState.saturation(), /*offset=*/2, /*stride=*/3);
|
||||
ecl_rst_file_add_kw(rst_file, sgas_kw);
|
||||
ecl_kw_free(sgas_kw);
|
||||
}
|
||||
|
||||
ecl_rst_file_end_solution(rst_file);
|
||||
ecl_rst_file_close(rst_file);
|
||||
free(fileName);
|
||||
#else
|
||||
OPM_THROW(std::runtime_error,
|
||||
"The ERT libraries are required to write ECLIPSE output files.");
|
||||
#endif // HAVE_ERT
|
||||
}
|
||||
|
||||
void BlackoilEclipseOutputWriter::writeWellState(const WellState& wellState, const SimulatorTimer& timer)
|
||||
{
|
||||
#if HAVE_ERT
|
||||
tm tm = boost::posix_time::to_tm(timer.currentDateTime());
|
||||
time_t curTime = mktime(&tm);
|
||||
|
||||
// create a new timestep for the summary file (at least if the
|
||||
// timer was advanced since the last call to writeWellState())
|
||||
ecl_sum_tstep_type* tstep=
|
||||
ecl_sum_add_tstep(sumWriter_,
|
||||
timer.currentStepNum() + 1,
|
||||
(curTime - startTime_)/(24*60*60));
|
||||
|
||||
int numWells = accumulatedProducedFluids_.size();
|
||||
for (int wellIdx = 0; wellIdx < numWells; ++wellIdx) {
|
||||
// set the value for the well oil production rate. For this,
|
||||
// be aware that the rates in the well state are _surface_
|
||||
// volume rates...
|
||||
double woprValue = wellState.wellRates()[wellIdx*3 + BlackoilPhases::Liquid];
|
||||
woprValue *= - 1 * (24 * 60 * 60); // convert m^3/s of injected fluid to m^3/d of produced fluid
|
||||
woprValue = std::max(0.0, woprValue);
|
||||
|
||||
int woprIdx = smspec_node_get_params_index(woprSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, woprIdx, woprValue);
|
||||
|
||||
// set the value for the well gas production rate
|
||||
double wgprValue = wellState.wellRates()[wellIdx*3 + BlackoilPhases::Vapour];
|
||||
wgprValue *= - 1 * (24 * 60 * 60); // convert m^3/s of injected fluid to m^3/d of produced fluid
|
||||
wgprValue = std::max(0.0, wgprValue);
|
||||
|
||||
int wgprIdx = smspec_node_get_params_index(wgprSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wgprIdx, wgprValue);
|
||||
|
||||
// water injection rate
|
||||
double wwirValue = wellState.wellRates()[wellIdx*3 + BlackoilPhases::Aqua];
|
||||
wwirValue *= 1 * (24 * 60 * 60); // convert m^3/s to m^3/d
|
||||
wwirValue = std::max(0.0, wwirValue);
|
||||
|
||||
int wwirIdx = smspec_node_get_params_index(wwirSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wwirIdx, wwirValue);
|
||||
|
||||
// gas injection rate
|
||||
double wgirValue = wellState.wellRates()[wellIdx*3 + BlackoilPhases::Vapour];
|
||||
wgirValue *= - 1 * (24 * 60 * 60); // convert m^3/s of injected fluid to m^3/d of produced fluid
|
||||
wgirValue = std::max(0.0, wgirValue);
|
||||
|
||||
int wgirIdx = smspec_node_get_params_index(wgirSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wgirIdx, wgirValue);
|
||||
|
||||
// accumulate injected produced fluids
|
||||
for (int phaseIdx = 0; phaseIdx < /*numPhases=*/3; ++phaseIdx) {
|
||||
// accumulate the produced/injected surface volumes
|
||||
double injectedVolume = wellState.wellRates()[wellIdx*3 + phaseIdx];
|
||||
injectedVolume *= timer.currentStepLength();
|
||||
|
||||
if (injectedVolume < 0)
|
||||
accumulatedProducedFluids_[wellIdx][phaseIdx] += -injectedVolume;
|
||||
else
|
||||
accumulatedInjectedFluids_[wellIdx][phaseIdx] += injectedVolume;
|
||||
|
||||
int woptIdx = smspec_node_get_params_index(woptSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, woptIdx, accumulatedProducedFluids_[wellIdx][BlackoilPhases::Liquid]);
|
||||
|
||||
int wgptIdx = smspec_node_get_params_index(wgptSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wgptIdx, accumulatedProducedFluids_[wellIdx][BlackoilPhases::Vapour]);
|
||||
|
||||
int wwitIdx = smspec_node_get_params_index(wwitSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wwitIdx, accumulatedInjectedFluids_[wellIdx][BlackoilPhases::Aqua]);
|
||||
|
||||
int wgitIdx = smspec_node_get_params_index(wgitSmspec_[wellIdx]);
|
||||
ecl_sum_tstep_iset(tstep, wgitIdx, accumulatedProducedFluids_[wellIdx][BlackoilPhases::Vapour]);
|
||||
}
|
||||
}
|
||||
|
||||
ecl_sum_fwrite(sumWriter_);
|
||||
#else
|
||||
OPM_THROW(std::runtime_error,
|
||||
"The ERT libraries are required to write ECLIPSE output files.");
|
||||
#endif // HAVE_ERT
|
||||
}
|
||||
|
||||
#if HAVE_ERT
|
||||
void BlackoilEclipseOutputWriter::writeGridInitFile_(const SimulatorTimer &timer)
|
||||
{
|
||||
int phases = ECL_OIL_PHASE + ECL_GAS_PHASE + ECL_WATER_PHASE;
|
||||
bool endian_flip = true;//ECL_ENDIAN_FLIP;
|
||||
bool fmt_file = false;
|
||||
ecl_file_enum file_type = ECL_EGRID_FILE;
|
||||
|
||||
ecl_grid_type* ecl_grid = newEclGrid_();
|
||||
char* gridFileName = ecl_util_alloc_filename(outputDir_.c_str(), baseName_.c_str(), file_type, fmt_file, timer.currentStepNum());
|
||||
fortio_type* fortio;
|
||||
ecl_grid_fwrite_EGRID(ecl_grid, gridFileName);
|
||||
free(gridFileName);
|
||||
|
||||
char* initFileName = ecl_util_alloc_filename(outputDir_.c_str(), baseName_.c_str(), /*file_type=*/ECL_INIT_FILE, fmt_file, timer.currentStepNum());
|
||||
if (!ecl_util_fmt_file(initFileName, &fmt_file)) {
|
||||
OPM_THROW(std::runtime_error,
|
||||
"Could not determine formatted/unformatted status of file:" << initFileName << " non-standard name?" << std::endl);
|
||||
}
|
||||
fortio = fortio_open_writer(initFileName, fmt_file, endian_flip);
|
||||
{
|
||||
time_t start_date;
|
||||
|
||||
{
|
||||
boost::posix_time::ptime start_date_(timer.currentDateTime());
|
||||
|
||||
tm td_tm = boost::posix_time::to_tm(start_date_);
|
||||
start_date = mktime(&td_tm);
|
||||
}
|
||||
|
||||
ecl_kw_type* poro_kw = newEclDoubleKeyword_(PORO_KW, eclipseParser_.getFloatingPointValue("PORO"));
|
||||
ecl_init_file_fwrite_header(fortio, ecl_grid, poro_kw, phases, start_date);
|
||||
ecl_kw_free(poro_kw);
|
||||
}
|
||||
|
||||
/* This collection of keywords is somewhat arbitrary and random. */
|
||||
saveEclKeyword_(fortio, "PERMX", ECL_FLOAT_TYPE);
|
||||
saveEclKeyword_(fortio, "PERMY", ECL_FLOAT_TYPE);
|
||||
saveEclKeyword_(fortio, "PERMZ", ECL_FLOAT_TYPE);
|
||||
|
||||
fortio_fclose(fortio);
|
||||
free(initFileName);
|
||||
}
|
||||
|
||||
void BlackoilEclipseOutputWriter::writeSummaryHeaderFile_(const SimulatorTimer &timer)
|
||||
{
|
||||
std::string caseName;
|
||||
if (!outputDir_.empty())
|
||||
caseName += outputDir_ + "/";
|
||||
caseName += baseName_;
|
||||
|
||||
if (sumWriter_)
|
||||
ecl_sum_free(sumWriter_);
|
||||
|
||||
// allocate the data structure for the writer
|
||||
sumWriter_ =
|
||||
ecl_sum_alloc_writer(caseName.c_str(),
|
||||
/*formattedOutput=*/false,
|
||||
/*unifiedOutput=*/true,
|
||||
/*joinString=*/":",
|
||||
startTime_,
|
||||
grid_.cartdims[0],grid_.cartdims[1],grid_.cartdims[2]);
|
||||
|
||||
// initialize the accumulated masses to zero
|
||||
const auto &wellSpecs = eclipseParser_.getWELSPECS().welspecs;
|
||||
int numWells = wellSpecs.size();
|
||||
accumulatedProducedFluids_.resize(numWells);
|
||||
accumulatedInjectedFluids_.resize(numWells);
|
||||
for (int wellIdx = 0; wellIdx < numWells; ++wellIdx) {
|
||||
for (int phaseIdx = 0; phaseIdx < /*numPhases=*/3; ++phaseIdx) {
|
||||
accumulatedProducedFluids_[wellIdx][phaseIdx] = 0;
|
||||
accumulatedInjectedFluids_[wellIdx][phaseIdx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
woprSmspec_.resize(numWells);
|
||||
woptSmspec_.resize(numWells);
|
||||
wgprSmspec_.resize(numWells);
|
||||
wgptSmspec_.resize(numWells);
|
||||
wwirSmspec_.resize(numWells);
|
||||
wwitSmspec_.resize(numWells);
|
||||
wgirSmspec_.resize(numWells);
|
||||
wgitSmspec_.resize(numWells);
|
||||
auto wellIt = wellSpecs.begin();
|
||||
const auto &wellEndIt = wellSpecs.end();
|
||||
for (int wellIdx = 0; wellIt != wellEndIt; ++wellIt, ++wellIdx) {
|
||||
// add the variables which ought to be included in the summary
|
||||
// file
|
||||
woprSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WOPR",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3/DAY",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
woptSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WOPT",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wgprSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WGPR",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3/DAY",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wgptSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WGPT",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wwirSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WWIR",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3/DAY",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wwitSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WWIT",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wgirSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WGIR",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3/DAY",
|
||||
/*defaultValue=*/0.0);
|
||||
|
||||
wgitSmspec_[wellIdx] = ecl_sum_add_var(sumWriter_,
|
||||
/*varName=*/"WGIT",
|
||||
/*wellGroupName=*/wellIt->name_.c_str(),
|
||||
/*num=*/0,
|
||||
/*unit=*/"SM3",
|
||||
/*defaultValue=*/0.0);
|
||||
}
|
||||
|
||||
ecl_sum_fwrite(sumWriter_);
|
||||
}
|
||||
|
||||
ecl_grid_type* BlackoilEclipseOutputWriter::newEclGrid_()
|
||||
{
|
||||
if (eclipseParser_.hasField("DXV")) {
|
||||
// make sure that the DYV and DZV keywords are present if the
|
||||
// DXV keyword is used in the deck...
|
||||
assert(eclipseParser_.hasField("DYV"));
|
||||
assert(eclipseParser_.hasField("DZV"));
|
||||
|
||||
const auto &dxv = eclipseParser_.getFloatingPointValue("DXV");
|
||||
const auto &dyv = eclipseParser_.getFloatingPointValue("DYV");
|
||||
const auto &dzv = eclipseParser_.getFloatingPointValue("DZV");
|
||||
|
||||
// creating a C array out of std::vector like this is pretty
|
||||
// hacky and might even be unportable. having said that, it
|
||||
// probably works with all currently known STL
|
||||
// implementations...
|
||||
return ecl_grid_alloc_dxv_dyv_dzv(dxv.size(), dyv.size(), dzv.size(),
|
||||
&dxv[0], &dyv[0], &dzv[0],
|
||||
/*actnum=*/NULL);
|
||||
}
|
||||
if (eclipseParser_.hasField("ZCORN")) {
|
||||
struct grdecl grdecl = eclipseParser_.get_grdecl();
|
||||
|
||||
ecl_kw_type * coord_kw = newEclDoubleKeyword_(COORD_KW, eclipseParser_.getFloatingPointValue("COORD"));
|
||||
ecl_kw_type * zcorn_kw = newEclDoubleKeyword_(ZCORN_KW, eclipseParser_.getFloatingPointValue("ZCORN"));
|
||||
ecl_kw_type * actnum_kw = newEclIntKeyword_(ACTNUM_KW, eclipseParser_.getIntegerValue("ACTNUM"));
|
||||
ecl_kw_type * mapaxes_kw = NULL;
|
||||
|
||||
ecl_grid_type * grid ;
|
||||
if (grdecl.mapaxes != NULL)
|
||||
mapaxes_kw = newEclDoubleKeyword_(MAPAXES_KW, eclipseParser_.getFloatingPointValue("MAPAXES"));
|
||||
|
||||
grid = ecl_grid_alloc_GRDECL_kw(grdecl.dims[0], grdecl.dims[1], grdecl.dims[2], zcorn_kw, coord_kw, actnum_kw, mapaxes_kw);
|
||||
|
||||
ecl_kw_free(coord_kw);
|
||||
ecl_kw_free(zcorn_kw);
|
||||
ecl_kw_free(actnum_kw);
|
||||
if (mapaxes_kw != NULL)
|
||||
ecl_kw_free(mapaxes_kw);
|
||||
|
||||
return grid;
|
||||
}
|
||||
OPM_THROW(std::runtime_error,
|
||||
"Can't create an ERT grid (no supported keywords found in deck)");
|
||||
}
|
||||
|
||||
ecl_kw_type* BlackoilEclipseOutputWriter::newEclIntKeyword_(const std::string& kwName,
|
||||
const std::vector<int> &data,
|
||||
int offset,
|
||||
int stride)
|
||||
{
|
||||
assert(offset >= 0 && offset < data.size());
|
||||
assert(stride > 0 && stride < data.size() - offset);
|
||||
|
||||
ecl_kw_type* eclKw =
|
||||
ecl_kw_alloc(kwName.c_str(),
|
||||
grid_.number_of_cells, ECL_INT_TYPE);
|
||||
for (int i=0; i < grid_.number_of_cells; i++)
|
||||
ecl_kw_iset_float(eclKw, i, data[i*stride + offset]);
|
||||
return eclKw;
|
||||
}
|
||||
|
||||
ecl_kw_type* BlackoilEclipseOutputWriter::newEclDoubleKeyword_(const std::string& kwName,
|
||||
const std::vector<double> &data,
|
||||
int offset,
|
||||
int stride)
|
||||
{
|
||||
assert(offset >= 0 && offset < data.size());
|
||||
assert(stride > 0 && stride < data.size() - offset);
|
||||
|
||||
ecl_kw_type* eclKw =
|
||||
ecl_kw_alloc(kwName.c_str(),
|
||||
grid_.number_of_cells, ECL_FLOAT_TYPE);
|
||||
for (int i=0; i < grid_.number_of_cells; i++)
|
||||
ecl_kw_iset_float(eclKw, i, static_cast<float>(data[i*stride + offset]));
|
||||
return eclKw;
|
||||
}
|
||||
|
||||
|
||||
void BlackoilEclipseOutputWriter::saveEclKeyword_(fortio_type* fortio, const std::string& kw, ecl_type_enum eclType)
|
||||
{
|
||||
ecl_kw_type* eclKw;
|
||||
if (eclType == ECL_INT_TYPE)
|
||||
eclKw = newEclIntKeyword_(kw, eclipseParser_.getIntegerValue(kw));
|
||||
else if (eclType == ECL_FLOAT_TYPE)
|
||||
eclKw = newEclDoubleKeyword_(kw, eclipseParser_.getFloatingPointValue(kw));
|
||||
else
|
||||
OPM_THROW(std::logic_error,
|
||||
"Not implemented: ECL keywords of type " << ECL_FLOAT_TYPE);
|
||||
if (eclKw != NULL) {
|
||||
ecl_kw_fwrite(eclKw, fortio);
|
||||
ecl_kw_free(eclKw);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_ERT
|
||||
|
||||
} // namespace Opm
|
@ -1,154 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Andreas Lauser
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_BLACKOIL_ECLIPSE_OUTPUT_WRITER_HPP
|
||||
#define OPM_BLACKOIL_ECLIPSE_OUTPUT_WRITER_HPP
|
||||
|
||||
#include <opm/core/grid.h>
|
||||
#include <opm/core/io/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/simulator/BlackoilState.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#ifdef HAVE_ERT
|
||||
#include <ert/ecl/fortio.h>
|
||||
#include <ert/ecl/ecl_file.h>
|
||||
#include <ert/ecl/ecl_grid.h>
|
||||
#include <ert/ecl/ecl_init_file.h>
|
||||
#include <ert/ecl/ecl_kw_magic.h>
|
||||
#include <ert/ecl/ecl_kw.h>
|
||||
#include <ert/ecl/ecl_sum.h>
|
||||
#include <ert/ecl/ecl_util.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Opm {
|
||||
/*!
|
||||
* \brief A class to write the reservoir state and the well state of a
|
||||
* blackoil simulation to disk using the Eclipse binary format.
|
||||
*
|
||||
* This class only writes files if the 'write_output' parameter is set
|
||||
* to 1. It needs the ERT libraries to write to disk, so if the
|
||||
* 'write_output' parameter is set but ERT is not available, all
|
||||
* methods throw a std::runtime_error.
|
||||
*/
|
||||
class BlackoilEclipseOutputWriter
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets the common attributes required to write eclipse
|
||||
* binary files using ERT.
|
||||
*/
|
||||
BlackoilEclipseOutputWriter(const EclipseGridParser& eclipseParser,
|
||||
const UnstructuredGrid& grid,
|
||||
const std::string &outputDir,
|
||||
const std::string &baseName)
|
||||
: eclipseParser_(eclipseParser)
|
||||
, grid_(grid)
|
||||
, outputDir_(outputDir)
|
||||
, baseName_(baseName)
|
||||
{
|
||||
#if HAVE_ERT
|
||||
sumWriter_ = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
~BlackoilEclipseOutputWriter()
|
||||
{
|
||||
#if HAVE_ERT
|
||||
if (sumWriter_) {
|
||||
// clean after ourselfs
|
||||
ecl_sum_free(sumWriter_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Write the static eclipse data (grid, PVT curves, etc) to disk
|
||||
*/
|
||||
void writeInitFile(const SimulatorTimer &timer);
|
||||
|
||||
/*!
|
||||
* \brief Write a blackoil reservoir state to disk for later inspection with
|
||||
* visualization tools like ResInsight
|
||||
*
|
||||
* \param[in] reservoirState The thermodynamic state of the reservoir
|
||||
*/
|
||||
void writeReservoirState(const BlackoilState& reservoirState,
|
||||
const SimulatorTimer& timer);
|
||||
|
||||
/*!
|
||||
* \brief Write a well state to disk for later inspection with
|
||||
* visualization tools
|
||||
*
|
||||
* \param[in] wellState The production/injection data for all wells
|
||||
*/
|
||||
void writeWellState(const WellState& wellState, const SimulatorTimer& timer);
|
||||
|
||||
private:
|
||||
const EclipseGridParser& eclipseParser_;
|
||||
const UnstructuredGrid& grid_;
|
||||
std::string outputDir_;
|
||||
std::string baseName_;
|
||||
|
||||
time_t startTime_;
|
||||
|
||||
#if HAVE_ERT
|
||||
void writeSummaryHeaderFile_(const SimulatorTimer &timer);
|
||||
void writeGridInitFile_(const SimulatorTimer &timer);
|
||||
|
||||
ecl_grid_type* newEclGrid_();
|
||||
ecl_kw_type* newEclIntKeyword_(const std::string& kwName,
|
||||
const std::vector<int> &data,
|
||||
int offset = 0,
|
||||
int stride = 1);
|
||||
|
||||
ecl_kw_type* newEclDoubleKeyword_(const std::string& kwName,
|
||||
const std::vector<double> &data,
|
||||
int offset = 0,
|
||||
int stride = 1);
|
||||
|
||||
void saveEclKeyword_(fortio_type* fortio, const std::string& keyword, ecl_type_enum ecl_type);
|
||||
|
||||
// keyword handles per well each
|
||||
std::vector<smspec_node_type*> woprSmspec_;
|
||||
std::vector<smspec_node_type*> woptSmspec_;
|
||||
std::vector<smspec_node_type*> wgprSmspec_;
|
||||
std::vector<smspec_node_type*> wgptSmspec_;
|
||||
std::vector<smspec_node_type*> wwirSmspec_;
|
||||
std::vector<smspec_node_type*> wwitSmspec_;
|
||||
std::vector<smspec_node_type*> wgirSmspec_;
|
||||
std::vector<smspec_node_type*> wgitSmspec_;
|
||||
|
||||
ecl_sum_type* sumWriter_;
|
||||
|
||||
std::vector<std::array<double, /*numPhases=*/3> > accumulatedProducedFluids_;
|
||||
std::vector<std::array<double, /*numPhases=*/3> > accumulatedInjectedFluids_;
|
||||
#endif
|
||||
};
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_BLACKOIL_ECLIPSE_OUTPUT_WRITER_HPP
|
@ -1178,4 +1178,16 @@ void EclipseGridParser::getNumericErtFields(const string& filename)
|
||||
#endif // HAVE_ERT
|
||||
}
|
||||
|
||||
// specializations for those types that can be provided; attempts
|
||||
// to access other types than these will result in linker error
|
||||
template <> const std::vector<int>&
|
||||
EclipseGridParser::getValue<int> (const std::string& keyword) const {
|
||||
return this->getIntegerValue(keyword);
|
||||
}
|
||||
|
||||
template <> const std::vector<double>&
|
||||
EclipseGridParser::getValue<double> (const std::string& keyword) const {
|
||||
return this->getFloatingPointValue(keyword);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -138,6 +138,15 @@ namespace Opm
|
||||
/// corresponding to the given floating-point keyword.
|
||||
const std::vector<double>& getFloatingPointValue(const std::string& keyword) const;
|
||||
|
||||
/// Returns a reference to a vector containing the values
|
||||
/// corresponding to the given keyword of a type only known
|
||||
/// indirectly (through a template)
|
||||
///
|
||||
/// \tparam T Type of the keyword's value. Currently only int
|
||||
/// and double are supported.
|
||||
template <typename T>
|
||||
const std::vector<T>& getValue(const std::string& keyword) const;
|
||||
|
||||
typedef std::shared_ptr<SpecialBase> SpecialFieldPtr;
|
||||
|
||||
/// Returns a reference to a vector containing pointers to the values
|
||||
|
1016
opm/core/io/eclipse/EclipseWriter.cpp
Normal file
1016
opm/core/io/eclipse/EclipseWriter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
opm/core/io/eclipse/EclipseWriter.hpp
Normal file
102
opm/core/io/eclipse/EclipseWriter.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright (c) 2013 Andreas Lauser
|
||||
Copyright (c) 2013 Uni Research 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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_ECLIPSE_WRITER_HPP
|
||||
#define OPM_ECLIPSE_WRITER_HPP
|
||||
|
||||
#include <opm/core/io/OutputWriter.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory> // std::unique_ptr
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
// forward declarations
|
||||
class EclipseGridParser;
|
||||
class SimulatorState;
|
||||
class SimulatorTimer;
|
||||
class WellState;
|
||||
|
||||
namespace parameter { class ParameterGroup; }
|
||||
|
||||
/*!
|
||||
* \brief A class to write the reservoir state and the well state of a
|
||||
* blackoil simulation to disk using the Eclipse binary format.
|
||||
*
|
||||
* This class only writes files if the 'write_output' parameter is set
|
||||
* to 1. It needs the ERT libraries to write to disk, so if the
|
||||
* 'write_output' parameter is set but ERT is not available, all
|
||||
* methods throw a std::runtime_error.
|
||||
*/
|
||||
class EclipseWriter : public OutputWriter
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* \brief Sets the common attributes required to write eclipse
|
||||
* binary files using ERT.
|
||||
*/
|
||||
EclipseWriter(const parameter::ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid);
|
||||
|
||||
/**
|
||||
* We need a destructor in the compilation unit to avoid the
|
||||
* EclipseSummary being a complete type here.
|
||||
*/
|
||||
virtual ~EclipseWriter ();
|
||||
|
||||
/**
|
||||
* Write the static eclipse data (grid, PVT curves, etc) as well as the
|
||||
* initial state to disk.
|
||||
*/
|
||||
virtual void writeInit(const SimulatorTimer &timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState);
|
||||
|
||||
/*!
|
||||
* \brief Write a blackoil reservoir state to disk for later inspection with
|
||||
* visualization tools like ResInsight
|
||||
*
|
||||
* \param[in] reservoirState The thermodynamic state of the reservoir
|
||||
* \param[in] wellState The production/injection data for all wells
|
||||
*/
|
||||
virtual void writeTimeStep(const SimulatorTimer& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState);
|
||||
|
||||
private:
|
||||
std::shared_ptr <const EclipseGridParser> parser_;
|
||||
std::shared_ptr <const UnstructuredGrid> grid_;
|
||||
std::string outputDir_;
|
||||
std::string baseName_;
|
||||
PhaseUsage uses_; // active phases in the input deck
|
||||
|
||||
/// Write solution field variables (pressure and saturation)
|
||||
void writeSolution (const SimulatorTimer& timer,
|
||||
const SimulatorState& reservoirState,
|
||||
const WellState& wellState);
|
||||
};
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_ECLIPSE_WRITER_HPP
|
@ -452,7 +452,7 @@ namespace Opm
|
||||
std::string filename = output_dir_ + "/step_timing.param";
|
||||
tstep_os.open(filename.c_str(), std::fstream::out | std::fstream::app);
|
||||
}
|
||||
for (; !timer.done(); ++timer) {
|
||||
while (!timer.done()) {
|
||||
// Report timestep and (optionally) write state to disk.
|
||||
step_timer.start();
|
||||
timer.report(*log_);
|
||||
@ -605,6 +605,10 @@ namespace Opm
|
||||
sreport.reportParam(tstep_os);
|
||||
}
|
||||
|
||||
// advance the timer to the end of the timestep *before* notifying
|
||||
// the client that the timestep is done
|
||||
++timer;
|
||||
|
||||
// notify all clients that we are done with the timestep
|
||||
callback_timer.start ();
|
||||
timestep_completed_.signal ();
|
||||
|
106
opm/core/simulator/SimulatorOutput.cpp
Normal file
106
opm/core/simulator/SimulatorOutput.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (c) 2013 Uni Research 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 "SimulatorOutput.hpp"
|
||||
|
||||
// we need complete definitions for these types
|
||||
#include <opm/core/io/eclipse/EclipseGridParser.hpp>
|
||||
#include <opm/core/io/OutputWriter.hpp>
|
||||
#include <opm/core/simulator/SimulatorTimer.hpp>
|
||||
|
||||
#include <numeric> // partial_sum
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
SimulatorOutputBase::SimulatorOutputBase (
|
||||
const parameter::ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid,
|
||||
std::shared_ptr <const SimulatorTimer> timer,
|
||||
std::shared_ptr <const SimulatorState> state,
|
||||
std::shared_ptr <const WellState> wellState)
|
||||
|
||||
// store all parameters passed into the object, making them curried
|
||||
// parameters to the writeOutput function.
|
||||
: timer_ (timer )
|
||||
, reservoirState_ (state )
|
||||
, wellState_ (wellState)
|
||||
|
||||
// process parameters into a writer. we don't setup a new chain in
|
||||
// every timestep!
|
||||
, writer_ (std::move (OutputWriter::create (params, parser, grid)))
|
||||
|
||||
// always start from the first timestep
|
||||
, next_ (0) {
|
||||
|
||||
// make a list of times to dump. since the original list are relative
|
||||
// timesteps, we make a list of accumulated such to compare with
|
||||
// current time. add an extra zero at the beginning so that the
|
||||
// initial state is also written
|
||||
const std::vector <double>& tstep = parser->getTSTEP ().tstep_;
|
||||
times_.resize (tstep.size (), 0.);
|
||||
std::partial_sum (tstep.begin(), tstep.end(), times_.begin());
|
||||
|
||||
// write the static initialization files, even before simulation starts
|
||||
writer_->writeInit (*timer, *state, *wellState);
|
||||
}
|
||||
|
||||
// default destructor is OK, just need to be defined
|
||||
SimulatorOutputBase::~SimulatorOutputBase() { }
|
||||
|
||||
SimulatorOutputBase::operator std::function <void ()> () {
|
||||
// return (a pointer to) the writeOutput() function as an object
|
||||
// which can be passed to the event available from the simulator
|
||||
return std::bind (&SimulatorOutputBase::writeOutput, std::ref (*this));
|
||||
}
|
||||
|
||||
void
|
||||
SimulatorOutputBase::writeOutput () {
|
||||
const int this_time = timer_->currentTime ();
|
||||
|
||||
// if the simulator signals for timesteps that aren't reporting
|
||||
// times, then ignore them
|
||||
if (next_ < times_.size () && times_[next_] <= this_time) {
|
||||
// uh-oh, the simulator has skipped reporting timesteps that
|
||||
// occurred before this timestep (it doesn't honor the TSTEP setting)
|
||||
while (next_ < times_.size () && times_[next_] < this_time) {
|
||||
++next_;
|
||||
}
|
||||
|
||||
// report this timestep if it matches
|
||||
if (next_ < times_.size () && times_[next_] == this_time) {
|
||||
// make sure the simulator has spilled all necessary internal
|
||||
// state. notice that this calls *our* sync, which is overridden
|
||||
// in the template companion to call the simulator
|
||||
sync ();
|
||||
|
||||
// relay the request to the handlers (setup in the constructor
|
||||
// from parameters)
|
||||
writer_->writeTimeStep (*timer_, *reservoirState_, *wellState_);
|
||||
|
||||
// advance to the next reporting time
|
||||
++next_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SimulatorOutputBase::sync () {
|
||||
// no-op in base class (overridden by simulator-specific template)
|
||||
}
|
195
opm/core/simulator/SimulatorOutput.hpp
Normal file
195
opm/core/simulator/SimulatorOutput.hpp
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright (c) 2013 Uni Research 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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SIMULATOR_OUTPUT_HPP
|
||||
#define OPM_SIMULATOR_OUTPUT_HPP
|
||||
|
||||
// need complete def. of this since we use it in template
|
||||
#include <opm/core/utility/Event.hpp>
|
||||
#include <opm/core/utility/share_obj.hpp>
|
||||
|
||||
#include <memory> // unique_ptr, shared_ptr
|
||||
#include <vector>
|
||||
|
||||
struct UnstructuredGrid;
|
||||
|
||||
namespace Opm {
|
||||
|
||||
// forward definitions
|
||||
class EclipseGridParser;
|
||||
class OutputWriter;
|
||||
namespace parameter { class ParameterGroup; }
|
||||
class SimulatorState;
|
||||
class SimulatorTimer;
|
||||
class WellState;
|
||||
|
||||
/**
|
||||
* Encapsulate output writing from simulators. This is essentially
|
||||
* a function object holding curried arguments to the writing backend
|
||||
* which is used when invoked through the event handler (which passes
|
||||
* on no arguments on it own).
|
||||
*/
|
||||
class SimulatorOutputBase {
|
||||
protected:
|
||||
/**
|
||||
* Curry arguments for the output writer. These arguments are passed
|
||||
* to the simulator, but is not passed on to the event handler so it
|
||||
* need to pick them up from the object members.
|
||||
*/
|
||||
SimulatorOutputBase (const parameter::ParameterGroup& p,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid,
|
||||
std::shared_ptr <const SimulatorTimer> timer,
|
||||
std::shared_ptr <const SimulatorState> state,
|
||||
std::shared_ptr <const WellState> wellState);
|
||||
|
||||
/**
|
||||
* We need a destructor in the compilation unit to avoid the
|
||||
* OutputWriter being a complete type here.
|
||||
*/
|
||||
virtual ~SimulatorOutputBase ();
|
||||
|
||||
/**
|
||||
* Conversion operator which allows the object to be directly passed
|
||||
* into an Event and used as a handler.
|
||||
*
|
||||
* @see Opm::SimulatorIncompTwophase::timestep_completed
|
||||
*/
|
||||
operator std::function <void ()> ();
|
||||
|
||||
/// Just hold a reference to these objects that are owned elsewhere.
|
||||
std::shared_ptr <const SimulatorTimer> timer_;
|
||||
std::shared_ptr <const SimulatorState> reservoirState_;
|
||||
std::shared_ptr <const WellState> wellState_;
|
||||
|
||||
/// Created locally and destructed together with us
|
||||
std::unique_ptr <OutputWriter> writer_;
|
||||
|
||||
/// Call the writers that were created based on the parameters
|
||||
virtual void writeOutput ();
|
||||
|
||||
/// Make sure that the simulator state is up to date before writing
|
||||
virtual void sync ();
|
||||
|
||||
private:
|
||||
/// Index of the upcoming reporting time
|
||||
std::vector <double>::size_type next_;
|
||||
|
||||
/// Array of times when to write report
|
||||
std::vector <double> times_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an output writer that is coupled to a simulator capable
|
||||
* of reading Eclipse deck files. Output will be written only when
|
||||
* specified in the deck file.
|
||||
*
|
||||
* @note
|
||||
* This class is a template since there is no fixed interface for
|
||||
* simulators, only an implied type class of common method signatures.
|
||||
*
|
||||
* @example
|
||||
* @code{.cpp}
|
||||
* // configuration
|
||||
* ParameterGroup params (argc, argv, false);
|
||||
*
|
||||
* // input file
|
||||
* auto deck = make_shared <EclipseGridParser> ( ... );
|
||||
* const GridManager manager (*parser);
|
||||
* auto grid = share_obj (*manager.c_grid ());
|
||||
*
|
||||
* // timestep ends up here
|
||||
* auto timer = make_shared <SimulatorTimer> ();
|
||||
*
|
||||
* // state ends up here
|
||||
* auto state = make_shared <TwophaseState> ();
|
||||
* auto wellState = make_shared <WellState> ();
|
||||
*
|
||||
* // set up simulation
|
||||
* auto sim = make_shared <SimulatorIncompTwophase> (params, *grid, ... );
|
||||
*
|
||||
* // use this to dump state to disk
|
||||
* auto output = make_shared <SimulatorOutput> (
|
||||
* params, deck, grid, timer, state, wellState, sim);
|
||||
*
|
||||
* // start simulation
|
||||
* sim.run (timer, state, ... )
|
||||
* @endcode
|
||||
*
|
||||
* @todo
|
||||
* This functionality could be incorporated directly into a simulator
|
||||
* object.
|
||||
*/
|
||||
template <typename Simulator>
|
||||
struct SimulatorOutput : public SimulatorOutputBase {
|
||||
SimulatorOutput (const parameter::ParameterGroup& params,
|
||||
std::shared_ptr <const EclipseGridParser> parser,
|
||||
std::shared_ptr <const UnstructuredGrid> grid,
|
||||
std::shared_ptr <const SimulatorTimer> timer,
|
||||
std::shared_ptr <const SimulatorState> state,
|
||||
std::shared_ptr <const WellState> wellState,
|
||||
std::shared_ptr <Simulator> sim)
|
||||
// send all other parameters to base class
|
||||
: SimulatorOutputBase (params, parser, grid, timer, state, wellState)
|
||||
|
||||
// store reference to simulator in derived class
|
||||
, sim_ (sim) {
|
||||
|
||||
// connect simulation with output writer
|
||||
sim->timestep_completed ().add (*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility constructor for clients written in C++03-style:
|
||||
* The client provide an informal guarantee that the lifetime of
|
||||
* the arguments passed exceeds the lifetime of this object.
|
||||
*/
|
||||
SimulatorOutput (const parameter::ParameterGroup& params,
|
||||
const EclipseGridParser& parser,
|
||||
const UnstructuredGrid& grid,
|
||||
const SimulatorTimer& timer,
|
||||
const SimulatorState& state,
|
||||
const WellState& wellState,
|
||||
Simulator& sim)
|
||||
// send all other parameters to base class
|
||||
: SimulatorOutputBase (params,
|
||||
share_obj (parser),
|
||||
share_obj (grid),
|
||||
share_obj (timer),
|
||||
share_obj (state),
|
||||
share_obj (wellState))
|
||||
|
||||
// store reference to simulator in derived class
|
||||
, sim_ (share_obj (sim)) {
|
||||
|
||||
// connect simulation with output writer
|
||||
sim_->timestep_completed ().add (*this);
|
||||
}
|
||||
protected:
|
||||
// forward this request to the simulator
|
||||
virtual void sync () { sim_->sync (); }
|
||||
|
||||
private:
|
||||
/// Reference to the simulator class; needed to ask it to synchronize
|
||||
std::shared_ptr <Simulator> sim_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* OPM_SIMULATOR_OUTPUT_HPP */
|
@ -89,6 +89,12 @@ namespace Opm
|
||||
return timesteps_[current_step_];
|
||||
}
|
||||
|
||||
double SimulatorTimer::stepLengthTaken() const
|
||||
{
|
||||
assert(current_step_ > 0);
|
||||
return timesteps_[current_step_ - 1];
|
||||
}
|
||||
|
||||
/// Current time.
|
||||
double SimulatorTimer::currentTime() const
|
||||
{
|
||||
|
@ -50,16 +50,29 @@ namespace Opm
|
||||
/// Total number of steps.
|
||||
int numSteps() const;
|
||||
|
||||
/// Current step number.
|
||||
/// Current step number. This is the number of timesteps that
|
||||
/// has been completed from the start of the run. The time
|
||||
/// after initialization but before the simulation has started
|
||||
/// is timestep number zero.
|
||||
int currentStepNum() const;
|
||||
|
||||
/// Set current step number.
|
||||
void setCurrentStepNum(int step);
|
||||
|
||||
/// Current step length.
|
||||
/// Note: if done(), it is an error to call currentStepLength().
|
||||
/// Current step length. This is the length of the step
|
||||
/// the simulator will take in the next iteration.
|
||||
///
|
||||
/// @note if done(), it is an error to call currentStepLength().
|
||||
double currentStepLength() const;
|
||||
|
||||
/// Previous step length. This is the length of the step that
|
||||
/// was taken to arrive at this time.
|
||||
///
|
||||
/// @note if no increments have been done (i.e. the timer is
|
||||
/// still in its constructed state and currentStepNum() == 0),
|
||||
/// it is an error to call stepLengthTaken().
|
||||
double stepLengthTaken () const;
|
||||
|
||||
/// Current time.
|
||||
double currentTime() const;
|
||||
|
||||
|
49
opm/core/utility/share_obj.hpp
Normal file
49
opm/core/utility/share_obj.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (c) 2013 Uni Research 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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_SHARE_OBJ_HPP
|
||||
#define OPM_SHARE_OBJ_HPP
|
||||
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// Custom deleter that does nothing
|
||||
inline void no_delete (void const *) { }
|
||||
|
||||
/*!
|
||||
* Share pointer of a local object.
|
||||
*
|
||||
* Use this wrapper when an interface needs a shared_ptr, but you
|
||||
* want to pass an object that has local storage (and you know
|
||||
* that the shared_ptr client doesn't need it outside of the scope).
|
||||
*
|
||||
* \example
|
||||
* \code{.cpp}
|
||||
* Foo obj;
|
||||
* std::shared_ptr <Foo> ptr = share_obj (obj);
|
||||
* \endcode
|
||||
*/
|
||||
template <typename T> std::shared_ptr <T> share_obj (T& t) {
|
||||
return std::shared_ptr <T> (&t, no_delete);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif /* OPM_SHARE_OBJ_HPP */
|
Loading…
Reference in New Issue
Block a user