mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
changed: move simulator serialization to separate class
more separation of concerns
This commit is contained in:
parent
eb599af058
commit
f46b6c5a01
@ -51,6 +51,7 @@ list (APPEND MAIN_SOURCE_FILES
|
|||||||
opm/simulators/flow/Main.cpp
|
opm/simulators/flow/Main.cpp
|
||||||
opm/simulators/flow/NonlinearSolverEbos.cpp
|
opm/simulators/flow/NonlinearSolverEbos.cpp
|
||||||
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.cpp
|
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.cpp
|
||||||
|
opm/simulators/flow/SimulatorSerializer.cpp
|
||||||
opm/simulators/flow/ValidationFunctions.cpp
|
opm/simulators/flow/ValidationFunctions.cpp
|
||||||
opm/simulators/flow/partitionCells.cpp
|
opm/simulators/flow/partitionCells.cpp
|
||||||
opm/simulators/linalg/ExtractParallelGridInformationToISTL.cpp
|
opm/simulators/linalg/ExtractParallelGridInformationToISTL.cpp
|
||||||
@ -442,6 +443,7 @@ list (APPEND PUBLIC_HEADER_FILES
|
|||||||
opm/simulators/flow/Main.hpp
|
opm/simulators/flow/Main.hpp
|
||||||
opm/simulators/flow/NonlinearSolverEbos.hpp
|
opm/simulators/flow/NonlinearSolverEbos.hpp
|
||||||
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.hpp
|
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.hpp
|
||||||
|
opm/simulators/flow/SimulatorSerializer.hpp
|
||||||
opm/simulators/flow/KeywordValidation.hpp
|
opm/simulators/flow/KeywordValidation.hpp
|
||||||
opm/simulators/flow/LogOutputHelper.hpp
|
opm/simulators/flow/LogOutputHelper.hpp
|
||||||
opm/simulators/flow/ValidationFunctions.hpp
|
opm/simulators/flow/ValidationFunctions.hpp
|
||||||
|
@ -22,14 +22,12 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||||
#include <opm/common/utility/String.hpp>
|
|
||||||
|
|
||||||
#include <opm/input/eclipse/Units/Units.hpp>
|
#include <opm/input/eclipse/Units/Units.hpp>
|
||||||
#include <opm/simulators/timestepping/SimulatorTimer.hpp>
|
#include <opm/simulators/timestepping/SimulatorTimer.hpp>
|
||||||
|
|
||||||
#include <boost/date_time.hpp>
|
#include <boost/date_time.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -64,75 +62,4 @@ void outputTimestampFIP(const SimulatorTimer& timer,
|
|||||||
OpmLog::note(ss.str());
|
OpmLog::note(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkSerializedCmdLine(const std::string& current,
|
|
||||||
const std::string& stored)
|
|
||||||
{
|
|
||||||
auto filter_strings = [](const std::vector<std::string>& input)
|
|
||||||
{
|
|
||||||
std::vector<std::string> output;
|
|
||||||
output.reserve(input.size());
|
|
||||||
std::copy_if(input.begin(), input.end(), std::back_inserter(output),
|
|
||||||
[](const std::string& line)
|
|
||||||
{
|
|
||||||
return line.compare(0, 11, "EclDeckFile") != 0 &&
|
|
||||||
line.compare(0, 8, "LoadStep") != 0 &&
|
|
||||||
line.compare(0, 9, "OutputDir") != 0 &&
|
|
||||||
line.compare(0, 8, "SaveFile") != 0 &&
|
|
||||||
line.compare(0, 8, "SaveStep") != 0;
|
|
||||||
});
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto curr_strings = split_string(current, '\n');
|
|
||||||
auto stored_strings = split_string(stored, '\n');
|
|
||||||
std::sort(curr_strings.begin(), curr_strings.end());
|
|
||||||
std::sort(stored_strings.begin(), stored_strings.end());
|
|
||||||
curr_strings = filter_strings(curr_strings);
|
|
||||||
stored_strings = filter_strings(stored_strings);
|
|
||||||
|
|
||||||
std::vector<std::string> difference;
|
|
||||||
std::set_symmetric_difference(stored_strings.begin(), stored_strings.end(),
|
|
||||||
curr_strings.begin(), curr_strings.end(),
|
|
||||||
std::back_inserter(difference));
|
|
||||||
|
|
||||||
std::vector<std::string> only_stored, only_curr;
|
|
||||||
if (!difference.empty()) {
|
|
||||||
for (std::size_t i = 0; i < difference.size(); ) {
|
|
||||||
auto stored_it = std::find(stored_strings.begin(),
|
|
||||||
stored_strings.end(), difference[i]);
|
|
||||||
auto pos = difference[i].find_first_of('=');
|
|
||||||
if (i < difference.size() - 1 &&
|
|
||||||
difference[i].compare(0, pos, difference[i+1], 0, pos) == 0) {
|
|
||||||
if (stored_it == stored_strings.end()) {
|
|
||||||
std::swap(difference[i], difference[i+1]);
|
|
||||||
}
|
|
||||||
i += 2;
|
|
||||||
} else {
|
|
||||||
if (stored_it == stored_strings.end()) {
|
|
||||||
only_curr.push_back(difference[i]);
|
|
||||||
} else {
|
|
||||||
only_stored.push_back(difference[i]);
|
|
||||||
}
|
|
||||||
difference.erase(difference.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::stringstream str;
|
|
||||||
str << "Differences:\n";
|
|
||||||
for (std::size_t i = 0; i < difference.size(); ++i) {
|
|
||||||
str << '\t' << (i % 2 == 0 ? '-' : '+') << difference[i] << '\n';
|
|
||||||
}
|
|
||||||
if (!only_stored.empty()) {
|
|
||||||
str << "Only in serialized parameters:\n";
|
|
||||||
for (const std::string& line : only_stored)
|
|
||||||
str << '\t' << line << '\n';
|
|
||||||
}
|
|
||||||
if (!only_curr.empty()) {
|
|
||||||
str << "Only in current parameters:\n";
|
|
||||||
for (const std::string& line : only_curr)
|
|
||||||
str << '\t' << line << '\n';
|
|
||||||
}
|
|
||||||
OpmLog::warning("Command line parameters mismatch:\n" + str.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Opm
|
} // namespace Opm
|
||||||
|
@ -22,15 +22,15 @@
|
|||||||
#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILEBOS_HEADER_INCLUDED
|
#ifndef OPM_SIMULATORFULLYIMPLICITBLACKOILEBOS_HEADER_INCLUDED
|
||||||
#define OPM_SIMULATORFULLYIMPLICITBLACKOILEBOS_HEADER_INCLUDED
|
#define OPM_SIMULATORFULLYIMPLICITBLACKOILEBOS_HEADER_INCLUDED
|
||||||
|
|
||||||
#include <dune/common/hash.hh>
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <opm/simulators/aquifers/BlackoilAquiferModel.hpp>
|
||||||
#include <opm/simulators/flow/BlackoilModelEbos.hpp>
|
#include <opm/simulators/flow/BlackoilModelEbos.hpp>
|
||||||
#include <opm/simulators/flow/BlackoilModelParametersEbos.hpp>
|
#include <opm/simulators/flow/BlackoilModelParametersEbos.hpp>
|
||||||
#include <opm/simulators/flow/ConvergenceOutputConfiguration.hpp>
|
#include <opm/simulators/flow/ConvergenceOutputConfiguration.hpp>
|
||||||
#include <opm/simulators/flow/ExtraConvergenceOutputThread.hpp>
|
#include <opm/simulators/flow/ExtraConvergenceOutputThread.hpp>
|
||||||
#include <opm/simulators/flow/NonlinearSolverEbos.hpp>
|
#include <opm/simulators/flow/NonlinearSolverEbos.hpp>
|
||||||
#include <opm/simulators/aquifers/BlackoilAquiferModel.hpp>
|
#include <opm/simulators/flow/SimulatorSerializer.hpp>
|
||||||
#include <opm/simulators/timestepping/AdaptiveTimeSteppingEbos.hpp>
|
#include <opm/simulators/timestepping/AdaptiveTimeSteppingEbos.hpp>
|
||||||
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
||||||
#include <opm/simulators/utils/moduleVersion.hpp>
|
#include <opm/simulators/utils/moduleVersion.hpp>
|
||||||
@ -149,12 +149,10 @@ void outputReportStep(const SimulatorTimer& timer);
|
|||||||
void outputTimestampFIP(const SimulatorTimer& timer,
|
void outputTimestampFIP(const SimulatorTimer& timer,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
const std::string& version);
|
const std::string& version);
|
||||||
void checkSerializedCmdLine(const std::string& current,
|
|
||||||
const std::string& stored);
|
|
||||||
|
|
||||||
/// a simulator for the blackoil model
|
/// a simulator for the blackoil model
|
||||||
template<class TypeTag>
|
template<class TypeTag>
|
||||||
class SimulatorFullyImplicitBlackoilEbos
|
class SimulatorFullyImplicitBlackoilEbos : private SerializableSim
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
||||||
@ -202,6 +200,13 @@ public:
|
|||||||
/// \param[in] threshold_pressures_by_face if nonempty, threshold pressures that inhibit flow
|
/// \param[in] threshold_pressures_by_face if nonempty, threshold pressures that inhibit flow
|
||||||
SimulatorFullyImplicitBlackoilEbos(Simulator& ebosSimulator)
|
SimulatorFullyImplicitBlackoilEbos(Simulator& ebosSimulator)
|
||||||
: ebosSimulator_(ebosSimulator)
|
: ebosSimulator_(ebosSimulator)
|
||||||
|
, serializer_(*this,
|
||||||
|
EclGenericVanguard::comm(),
|
||||||
|
ebosSimulator_.vanguard().eclState().getIOConfig(),
|
||||||
|
EWOMS_GET_PARAM(TypeTag, std::string, SaveStep),
|
||||||
|
EWOMS_GET_PARAM(TypeTag, int, LoadStep),
|
||||||
|
EWOMS_GET_PARAM(TypeTag, std::string, SaveFile),
|
||||||
|
EWOMS_GET_PARAM(TypeTag, std::string, LoadFile))
|
||||||
{
|
{
|
||||||
phaseUsage_ = phaseUsageFromDeck(eclState());
|
phaseUsage_ = phaseUsageFromDeck(eclState());
|
||||||
|
|
||||||
@ -214,36 +219,6 @@ public:
|
|||||||
OutputExtraConvergenceInfo),
|
OutputExtraConvergenceInfo),
|
||||||
R"(OutputExtraConvergenceInfo (--output-extra-convergence-info))");
|
R"(OutputExtraConvergenceInfo (--output-extra-convergence-info))");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string saveSpec = EWOMS_GET_PARAM(TypeTag, std::string, SaveStep);
|
|
||||||
if (saveSpec == "all") {
|
|
||||||
saveStride_ = 1;
|
|
||||||
} else if (saveSpec == "last") {
|
|
||||||
saveStride_ = -1;
|
|
||||||
} else if (!saveSpec.empty() && saveSpec[0] == ':') {
|
|
||||||
saveStride_ = std::atoi(saveSpec.c_str()+1);
|
|
||||||
} else if (!saveSpec.empty()) {
|
|
||||||
saveStep_ = std::atoi(saveSpec.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
loadStep_ = EWOMS_GET_PARAM(TypeTag, int, LoadStep);
|
|
||||||
|
|
||||||
saveFile_ = EWOMS_GET_PARAM(TypeTag, std::string, SaveFile);
|
|
||||||
loadFile_ = EWOMS_GET_PARAM(TypeTag, std::string, LoadFile);
|
|
||||||
|
|
||||||
if (loadFile_.empty() || saveFile_.empty()) {
|
|
||||||
const auto& ioconfig = ebosSimulator_.vanguard().eclState().getIOConfig();
|
|
||||||
if (saveFile_.empty()) saveFile_ = ioconfig.fullBasePath() + ".OPMRST";
|
|
||||||
if (loadFile_.empty()) loadFile_ = saveFile_;
|
|
||||||
if (loadStep_ != -1 && !std::filesystem::exists(loadFile_)) {
|
|
||||||
std::filesystem::path path(ioconfig.getInputDir() + "/");
|
|
||||||
path.replace_filename(ioconfig.getBaseName() + ".OPMRST");
|
|
||||||
loadFile_ = path;
|
|
||||||
if (!std::filesystem::exists(loadFile_)) {
|
|
||||||
OPM_THROW(std::runtime_error, "Error locating serialized restart file " + loadFile_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~SimulatorFullyImplicitBlackoilEbos()
|
~SimulatorFullyImplicitBlackoilEbos()
|
||||||
@ -363,8 +338,8 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadStep_ > -1) {
|
if (serializer_.shouldLoad()) {
|
||||||
loadTimerInfo(timer);
|
serializer_.loadTimerInfo(timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report timestep.
|
// Report timestep.
|
||||||
@ -404,10 +379,9 @@ public:
|
|||||||
+ schedule().seconds(timer.currentStepNum()),
|
+ schedule().seconds(timer.currentStepNum()),
|
||||||
timer.currentStepLength());
|
timer.currentStepLength());
|
||||||
ebosSimulator_.setEpisodeIndex(timer.currentStepNum());
|
ebosSimulator_.setEpisodeIndex(timer.currentStepNum());
|
||||||
if (loadStep_> -1) {
|
if (serializer_.shouldLoad()) {
|
||||||
wellModel_().prepareDeserialize(loadStep_ - 1);
|
wellModel_().prepareDeserialize(serializer_.loadStep() - 1);
|
||||||
loadSimulatorState();
|
serializer_.loadState();
|
||||||
loadStep_ = -1;
|
|
||||||
ebosSimulator_.model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
|
ebosSimulator_.model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
|
||||||
}
|
}
|
||||||
solver_->model().beginReportStep();
|
solver_->model().beginReportStep();
|
||||||
@ -491,7 +465,7 @@ public:
|
|||||||
OpmLog::debug(msg);
|
OpmLog::debug(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSave(timer);
|
serializer_.save(timer);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -530,6 +504,42 @@ public:
|
|||||||
{ return solver_->model(); }
|
{ return solver_->model(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
//! \brief Load simulator state from hdf5 serializer.
|
||||||
|
void loadState([[maybe_unused]] HDF5Serializer& serializer,
|
||||||
|
[[maybe_unused]] const std::string& groupName) override
|
||||||
|
{
|
||||||
|
#if HAVE_HDF5
|
||||||
|
serializer.read(*this, groupName, "simulator_data");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Save simulator state using hdf5 serializer.
|
||||||
|
void saveState([[maybe_unused]] HDF5Serializer& serializer,
|
||||||
|
[[maybe_unused]] const std::string& groupName) const override
|
||||||
|
{
|
||||||
|
#if HAVE_HDF5
|
||||||
|
serializer.write(*this, groupName, "simulator_data");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Returns header data
|
||||||
|
std::array<std::string,5> getHeader() const override
|
||||||
|
{
|
||||||
|
std::ostringstream str;
|
||||||
|
Parameters::printValues<TypeTag>(str);
|
||||||
|
return {"OPM Flow",
|
||||||
|
moduleVersion(),
|
||||||
|
compileTimestamp(),
|
||||||
|
ebosSimulator_.vanguard().caseName(),
|
||||||
|
str.str()};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Returns local-to-global cell mapping.
|
||||||
|
const std::vector<int>& getCellMapping() const override
|
||||||
|
{
|
||||||
|
return ebosSimulator_.vanguard().globalCell();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Solver> createSolver(WellModel& wellModel)
|
std::unique_ptr<Solver> createSolver(WellModel& wellModel)
|
||||||
{
|
{
|
||||||
@ -637,125 +647,6 @@ protected:
|
|||||||
this->convergenceOutputThread_->join();
|
this->convergenceOutputThread_->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \brief Serialization of simulator data to .OPMRST files at end of report steps.
|
|
||||||
void handleSave(SimulatorTimer& timer)
|
|
||||||
{
|
|
||||||
if (saveStride_ == 0 && saveStep_ == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
||||||
|
|
||||||
int nextStep = timer.currentStepNum();
|
|
||||||
if ((saveStep_ != -1 && nextStep == saveStep_) ||
|
|
||||||
(saveStride_ != 0 && (nextStep % saveStride_) == 0)) {
|
|
||||||
#if !HAVE_HDF5
|
|
||||||
OpmLog::error("Saving of serialized state requested, but no HDF5 support available.");
|
|
||||||
#else
|
|
||||||
const std::string groupName = "/report_step/" + std::to_string(nextStep);
|
|
||||||
if (saveStride_ < 0 || nextStep == saveStride_ || nextStep == saveStep_) {
|
|
||||||
std::filesystem::remove(saveFile_);
|
|
||||||
}
|
|
||||||
HDF5Serializer writer(saveFile_,
|
|
||||||
HDF5File::OpenMode::APPEND,
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
if (saveStride_ < 0 || nextStep == saveStride_ || nextStep == saveStep_) {
|
|
||||||
std::ostringstream str;
|
|
||||||
Parameters::printValues<TypeTag>(str);
|
|
||||||
writer.writeHeader("OPM Flow",
|
|
||||||
moduleVersion(),
|
|
||||||
compileTimestamp(),
|
|
||||||
ebosSimulator_.vanguard().caseName(),
|
|
||||||
str.str(),
|
|
||||||
EclGenericVanguard::comm().size());
|
|
||||||
|
|
||||||
if (EclGenericVanguard::comm().size() > 1) {
|
|
||||||
const auto& cellMapping = ebosSimulator_.vanguard().globalCell();
|
|
||||||
std::size_t hash = Dune::hash_range(cellMapping.begin(), cellMapping.end());
|
|
||||||
writer.write(hash, "/", "grid_checksum");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.write(*this, groupName, "simulator_data");
|
|
||||||
writer.write(timer, groupName, "simulator_timer",
|
|
||||||
HDF5File::DataSetMode::ROOT_ONLY);
|
|
||||||
OpmLog::info("Serialized state written for report step " + std::to_string(nextStep));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
OPM_END_PARALLEL_TRY_CATCH("Error saving serialized state: ",
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \brief Load timer info from serialized state.
|
|
||||||
void loadTimerInfo([[maybe_unused]] SimulatorTimer& timer)
|
|
||||||
{
|
|
||||||
#if !HAVE_HDF5
|
|
||||||
OpmLog::error("Loading of serialized state requested, but no HDF5 support available.");
|
|
||||||
loadStep_ = -1;
|
|
||||||
#else
|
|
||||||
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
||||||
|
|
||||||
HDF5Serializer reader(loadFile_,
|
|
||||||
HDF5File::OpenMode::READ,
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
|
|
||||||
if (loadStep_ == 0) {
|
|
||||||
loadStep_ = reader.lastReportStep();
|
|
||||||
}
|
|
||||||
|
|
||||||
OpmLog::info("Loading serialized state for report step " + std::to_string(loadStep_));
|
|
||||||
const std::string groupName = "/report_step/" + std::to_string(loadStep_);
|
|
||||||
reader.read(timer, groupName, "simulator_timer", HDF5File::DataSetMode::ROOT_ONLY);
|
|
||||||
|
|
||||||
std::tuple<std::array<std::string,5>,int> header;
|
|
||||||
reader.read(header, "/", "simulator_info", HDF5File::DataSetMode::ROOT_ONLY);
|
|
||||||
const auto& [strings, procs] = header;
|
|
||||||
|
|
||||||
if (EclGenericVanguard::comm().size() != procs) {
|
|
||||||
throw std::runtime_error("Number of processes (procs=" +
|
|
||||||
std::to_string(EclGenericVanguard::comm().size()) +
|
|
||||||
") does not match .OPMRST file (procs=" +
|
|
||||||
std::to_string(procs) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EclGenericVanguard::comm().size() > 1) {
|
|
||||||
std::size_t stored_hash;
|
|
||||||
reader.read(stored_hash, "/", "grid_checksum");
|
|
||||||
const auto& cellMapping = ebosSimulator_.vanguard().globalCell();
|
|
||||||
std::size_t hash = Dune::hash_range(cellMapping.begin(), cellMapping.end());
|
|
||||||
if (hash != stored_hash) {
|
|
||||||
throw std::runtime_error("Grid hash mismatch, .OPMRST file cannot be used");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EclGenericVanguard::comm().rank() == 0) {
|
|
||||||
std::ostringstream str;
|
|
||||||
Parameters::printValues<TypeTag>(str);
|
|
||||||
checkSerializedCmdLine(str.str(), strings[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
OPM_END_PARALLEL_TRY_CATCH("Error loading serialized state: ",
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \brief Load simulator state from serialized state.
|
|
||||||
void loadSimulatorState()
|
|
||||||
{
|
|
||||||
#if HAVE_HDF5
|
|
||||||
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
||||||
|
|
||||||
HDF5Serializer reader(loadFile_,
|
|
||||||
HDF5File::OpenMode::READ,
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
const std::string groupName = "/report_step/" + std::to_string(loadStep_);
|
|
||||||
reader.read(*this, groupName, "simulator_data");
|
|
||||||
|
|
||||||
OPM_END_PARALLEL_TRY_CATCH("Error loading serialized state: ",
|
|
||||||
EclGenericVanguard::comm());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data.
|
// Data.
|
||||||
Simulator& ebosSimulator_;
|
Simulator& ebosSimulator_;
|
||||||
|
|
||||||
@ -779,11 +670,7 @@ protected:
|
|||||||
std::optional<ConvergenceOutputThread> convergenceOutputObject_{};
|
std::optional<ConvergenceOutputThread> convergenceOutputObject_{};
|
||||||
std::optional<std::thread> convergenceOutputThread_{};
|
std::optional<std::thread> convergenceOutputThread_{};
|
||||||
|
|
||||||
int saveStride_ = 0; //!< Stride to save serialized state at, negative to only keep last
|
SimulatorSerializer serializer_;
|
||||||
int saveStep_ = -1; //!< Specific step to save serialized state at
|
|
||||||
int loadStep_ = -1; //!< Step to load serialized state from
|
|
||||||
std::string saveFile_; //!< File to save serialized state to
|
|
||||||
std::string loadFile_; //!< File to load serialized state from
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Opm
|
} // namespace Opm
|
||||||
|
255
opm/simulators/flow/SimulatorSerializer.cpp
Normal file
255
opm/simulators/flow/SimulatorSerializer.cpp
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 Equinor ASA.
|
||||||
|
|
||||||
|
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 <opm/simulators/flow/SimulatorSerializer.hpp>
|
||||||
|
|
||||||
|
#include <dune/common/hash.hh>
|
||||||
|
|
||||||
|
#include <opm/common/ErrorMacros.hpp>
|
||||||
|
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||||
|
#include <opm/common/utility/String.hpp>
|
||||||
|
|
||||||
|
#include <opm/input/eclipse/EclipseState/IOConfig/IOConfig.hpp>
|
||||||
|
|
||||||
|
#include <opm/simulators/timestepping/SimulatorTimer.hpp>
|
||||||
|
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
|
||||||
|
|
||||||
|
#if HAVE_HDF5
|
||||||
|
#include <ebos/hdf5serializer.hh>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace Opm {
|
||||||
|
|
||||||
|
SimulatorSerializer::SimulatorSerializer(SerializableSim& simulator,
|
||||||
|
Parallel::Communication& comm,
|
||||||
|
const IOConfig& ioconfig,
|
||||||
|
const std::string& saveSpec,
|
||||||
|
int loadStep,
|
||||||
|
const std::string& saveFile,
|
||||||
|
const std::string& loadFile)
|
||||||
|
: simulator_(simulator)
|
||||||
|
, comm_(comm)
|
||||||
|
, loadStep_(loadStep)
|
||||||
|
, saveFile_(saveFile)
|
||||||
|
, loadFile_(loadFile)
|
||||||
|
{
|
||||||
|
if (saveSpec == "all") {
|
||||||
|
saveStride_ = 1;
|
||||||
|
} else if (saveSpec == "last") {
|
||||||
|
saveStride_ = -1;
|
||||||
|
} else if (!saveSpec.empty() && saveSpec[0] == ':') {
|
||||||
|
saveStride_ = std::atoi(saveSpec.c_str()+1);
|
||||||
|
} else if (!saveSpec.empty()) {
|
||||||
|
saveStep_ = std::atoi(saveSpec.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadFile_.empty() || saveFile_.empty()) {
|
||||||
|
if (saveFile_.empty()) saveFile_ = ioconfig.fullBasePath() + ".OPMRST";
|
||||||
|
if (loadFile_.empty()) loadFile_ = saveFile_;
|
||||||
|
if (loadStep_ != -1 && !std::filesystem::exists(loadFile_)) {
|
||||||
|
std::filesystem::path path(ioconfig.getInputDir() + "/");
|
||||||
|
path.replace_filename(ioconfig.getBaseName() + ".OPMRST");
|
||||||
|
loadFile_ = path;
|
||||||
|
if (!std::filesystem::exists(loadFile_)) {
|
||||||
|
OPM_THROW(std::runtime_error, "Error locating serialized restart file " + loadFile_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorSerializer::save(SimulatorTimer& timer)
|
||||||
|
{
|
||||||
|
if (saveStride_ == 0 && saveStep_ == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
||||||
|
|
||||||
|
int nextStep = timer.currentStepNum();
|
||||||
|
if ((saveStep_ != -1 && nextStep == saveStep_) ||
|
||||||
|
(saveStride_ != 0 && (nextStep % saveStride_) == 0)) {
|
||||||
|
#if !HAVE_HDF5
|
||||||
|
OpmLog::error("Saving of serialized state requested, but no HDF5 support available.");
|
||||||
|
#else
|
||||||
|
const std::string groupName = "/report_step/" + std::to_string(nextStep);
|
||||||
|
if (saveStride_ < 0 || nextStep == saveStride_ || nextStep == saveStep_) {
|
||||||
|
std::filesystem::remove(saveFile_);
|
||||||
|
}
|
||||||
|
HDF5Serializer writer(saveFile_, HDF5File::OpenMode::APPEND, comm_);
|
||||||
|
if (saveStride_ < 0 || nextStep == saveStride_ || nextStep == saveStep_) {
|
||||||
|
const auto data = simulator_.getHeader();
|
||||||
|
writer.writeHeader(data[0], data[1], data[2], data[3], data[4], comm_.size());
|
||||||
|
|
||||||
|
if (comm_.size() > 1) {
|
||||||
|
const auto& cellMapping = simulator_.getCellMapping();
|
||||||
|
std::size_t hash = Dune::hash_range(cellMapping.begin(), cellMapping.end());
|
||||||
|
writer.write(hash, "/", "grid_checksum");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simulator_.saveState(writer, groupName);
|
||||||
|
writer.write(timer, groupName, "simulator_timer",
|
||||||
|
HDF5File::DataSetMode::ROOT_ONLY);
|
||||||
|
OpmLog::info("Serialized state written for report step " + std::to_string(nextStep));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OPM_END_PARALLEL_TRY_CATCH("Error saving serialized state: ", comm_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Load timer info from serialized state.
|
||||||
|
void SimulatorSerializer::loadTimerInfo([[maybe_unused]] SimulatorTimer& timer)
|
||||||
|
{
|
||||||
|
#if !HAVE_HDF5
|
||||||
|
OpmLog::error("Loading of serialized state requested, but no HDF5 support available.");
|
||||||
|
loadStep_ = -1;
|
||||||
|
#else
|
||||||
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
||||||
|
|
||||||
|
HDF5Serializer reader(loadFile_, HDF5File::OpenMode::READ, comm_);
|
||||||
|
|
||||||
|
if (loadStep_ == 0) {
|
||||||
|
loadStep_ = reader.lastReportStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpmLog::info("Loading serialized state for report step " + std::to_string(loadStep_));
|
||||||
|
const std::string groupName = "/report_step/" + std::to_string(loadStep_);
|
||||||
|
reader.read(timer, groupName, "simulator_timer", HDF5File::DataSetMode::ROOT_ONLY);
|
||||||
|
|
||||||
|
std::tuple<std::array<std::string,5>,int> header;
|
||||||
|
reader.read(header, "/", "simulator_info", HDF5File::DataSetMode::ROOT_ONLY);
|
||||||
|
const auto& [strings, procs] = header;
|
||||||
|
|
||||||
|
if (comm_.size() != procs) {
|
||||||
|
throw std::runtime_error("Number of processes (procs=" +
|
||||||
|
std::to_string(comm_.size()) +
|
||||||
|
") does not match .OPMRST file (procs=" +
|
||||||
|
std::to_string(procs) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comm_.size() > 1) {
|
||||||
|
std::size_t stored_hash;
|
||||||
|
reader.read(stored_hash, "/", "grid_checksum");
|
||||||
|
const auto& cellMapping = simulator_.getCellMapping();
|
||||||
|
std::size_t hash = Dune::hash_range(cellMapping.begin(), cellMapping.end());
|
||||||
|
if (hash != stored_hash) {
|
||||||
|
throw std::runtime_error("Grid hash mismatch, .OPMRST file cannot be used");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comm_.rank() == 0) {
|
||||||
|
const auto& curr_header = simulator_.getHeader();
|
||||||
|
checkSerializedCmdLine(curr_header[4], strings[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
OPM_END_PARALLEL_TRY_CATCH("Error loading serialized state: ", comm_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \brief Load simulator state from serialized state.
|
||||||
|
void SimulatorSerializer::loadState()
|
||||||
|
{
|
||||||
|
#if HAVE_HDF5
|
||||||
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
||||||
|
|
||||||
|
HDF5Serializer reader(loadFile_, HDF5File::OpenMode::READ, comm_);
|
||||||
|
const std::string groupName = "/report_step/" + std::to_string(loadStep_);
|
||||||
|
simulator_.loadState(reader, groupName);
|
||||||
|
|
||||||
|
OPM_END_PARALLEL_TRY_CATCH("Error loading serialized state: ", comm_);
|
||||||
|
#endif
|
||||||
|
loadStep_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorSerializer::checkSerializedCmdLine(const std::string& current,
|
||||||
|
const std::string& stored)
|
||||||
|
{
|
||||||
|
auto filter_strings = [](const std::vector<std::string>& input)
|
||||||
|
{
|
||||||
|
std::vector<std::string> output;
|
||||||
|
output.reserve(input.size());
|
||||||
|
std::copy_if(input.begin(), input.end(), std::back_inserter(output),
|
||||||
|
[](const std::string& line)
|
||||||
|
{
|
||||||
|
return line.compare(0, 11, "EclDeckFile") != 0 &&
|
||||||
|
line.compare(0, 8, "LoadStep") != 0 &&
|
||||||
|
line.compare(0, 9, "OutputDir") != 0 &&
|
||||||
|
line.compare(0, 8, "SaveFile") != 0 &&
|
||||||
|
line.compare(0, 8, "SaveStep") != 0;
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto curr_strings = split_string(current, '\n');
|
||||||
|
auto stored_strings = split_string(stored, '\n');
|
||||||
|
std::sort(curr_strings.begin(), curr_strings.end());
|
||||||
|
std::sort(stored_strings.begin(), stored_strings.end());
|
||||||
|
curr_strings = filter_strings(curr_strings);
|
||||||
|
stored_strings = filter_strings(stored_strings);
|
||||||
|
|
||||||
|
std::vector<std::string> difference;
|
||||||
|
std::set_symmetric_difference(stored_strings.begin(), stored_strings.end(),
|
||||||
|
curr_strings.begin(), curr_strings.end(),
|
||||||
|
std::back_inserter(difference));
|
||||||
|
|
||||||
|
std::vector<std::string> only_stored, only_curr;
|
||||||
|
if (!difference.empty()) {
|
||||||
|
for (std::size_t i = 0; i < difference.size(); ) {
|
||||||
|
auto stored_it = std::find(stored_strings.begin(),
|
||||||
|
stored_strings.end(), difference[i]);
|
||||||
|
auto pos = difference[i].find_first_of('=');
|
||||||
|
if (i < difference.size() - 1 &&
|
||||||
|
difference[i].compare(0, pos, difference[i+1], 0, pos) == 0) {
|
||||||
|
if (stored_it == stored_strings.end()) {
|
||||||
|
std::swap(difference[i], difference[i+1]);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
if (stored_it == stored_strings.end()) {
|
||||||
|
only_curr.push_back(difference[i]);
|
||||||
|
} else {
|
||||||
|
only_stored.push_back(difference[i]);
|
||||||
|
}
|
||||||
|
difference.erase(difference.begin() + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::stringstream str;
|
||||||
|
str << "Differences:\n";
|
||||||
|
for (std::size_t i = 0; i < difference.size(); ++i) {
|
||||||
|
str << '\t' << (i % 2 == 0 ? '-' : '+') << difference[i] << '\n';
|
||||||
|
}
|
||||||
|
if (!only_stored.empty()) {
|
||||||
|
str << "Only in serialized parameters:\n";
|
||||||
|
for (const std::string& line : only_stored)
|
||||||
|
str << '\t' << line << '\n';
|
||||||
|
}
|
||||||
|
if (!only_curr.empty()) {
|
||||||
|
str << "Only in current parameters:\n";
|
||||||
|
for (const std::string& line : only_curr)
|
||||||
|
str << '\t' << line << '\n';
|
||||||
|
}
|
||||||
|
OpmLog::warning("Command line parameters mismatch:\n" + str.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Opm
|
99
opm/simulators/flow/SimulatorSerializer.hpp
Normal file
99
opm/simulators/flow/SimulatorSerializer.hpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 Equinor ASA.
|
||||||
|
|
||||||
|
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_SERIALIZER_HEADER_INCLUDED
|
||||||
|
#define OPM_SIMULATOR_SERIALIZER_HEADER_INCLUDED
|
||||||
|
|
||||||
|
#include <opm/simulators/utils/ParallelCommunication.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Opm {
|
||||||
|
|
||||||
|
class HDF5Serializer;
|
||||||
|
class IOConfig;
|
||||||
|
class SimulatorTimer;
|
||||||
|
|
||||||
|
//! \brief Abstract interface for simulator serialization ops.
|
||||||
|
struct SerializableSim {
|
||||||
|
//! \brief Load simulator state from file.
|
||||||
|
virtual void loadState(HDF5Serializer& serializer,
|
||||||
|
const std::string& groupName) = 0;
|
||||||
|
|
||||||
|
//! \brief Save simulator state to file.
|
||||||
|
virtual void saveState(HDF5Serializer& serializer,
|
||||||
|
const std::string& groupName) const = 0;
|
||||||
|
|
||||||
|
//! \brief Get header info to save to file.
|
||||||
|
virtual std::array<std::string,5> getHeader() const = 0;
|
||||||
|
|
||||||
|
//! \brief Obtain local-to-global cell mapping.
|
||||||
|
virtual const std::vector<int>& getCellMapping() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Class handling simulator serialization.
|
||||||
|
class SimulatorSerializer {
|
||||||
|
public:
|
||||||
|
//! \brief Constructor inits parameters.
|
||||||
|
//! \param saveSpec Specification of steps to save
|
||||||
|
//! \param loadStep Step to load
|
||||||
|
//! \paramn saveFile File to save to
|
||||||
|
//! \param loadFile File to load from
|
||||||
|
SimulatorSerializer(SerializableSim& simulator,
|
||||||
|
Parallel::Communication& comm,
|
||||||
|
const IOConfig& ioconfig,
|
||||||
|
const std::string& saveSpec,
|
||||||
|
int loadStep,
|
||||||
|
const std::string& saveFile,
|
||||||
|
const std::string& loadFile);
|
||||||
|
|
||||||
|
//! \brief Returns whether or not a state should be loaded.
|
||||||
|
bool shouldLoad() const { return loadStep_ > -1; }
|
||||||
|
|
||||||
|
//! \brief Returns step to load.
|
||||||
|
int loadStep() const { return loadStep_; }
|
||||||
|
|
||||||
|
//! \brief Save data to file if appropriate.
|
||||||
|
void save(SimulatorTimer& timer);
|
||||||
|
|
||||||
|
//! \brief Loads time step info from file.
|
||||||
|
void loadTimerInfo(SimulatorTimer& timer);
|
||||||
|
|
||||||
|
//! \brief Load state from file.
|
||||||
|
void loadState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! \brief Checks for differences between command line parameters.
|
||||||
|
void checkSerializedCmdLine(const std::string& current,
|
||||||
|
const std::string& stored);
|
||||||
|
|
||||||
|
SerializableSim& simulator_; //!< Reference to simulator to be use
|
||||||
|
Parallel::Communication& comm_; //!< Communication to use
|
||||||
|
int saveStride_ = 0; //!< Stride to save serialized state at, negative to only keep last
|
||||||
|
int saveStep_ = -1; //!< Specific step to save serialized state at
|
||||||
|
int loadStep_ = -1; //!< Step to load serialized state from
|
||||||
|
std::string saveFile_; //!< File to save serialized state to
|
||||||
|
std::string loadFile_; //!< File to load serialized state from
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Opm
|
||||||
|
|
||||||
|
#endif // OPM_SIMULATOR_SERIALIZER_HEADER_INCLUDED
|
Loading…
Reference in New Issue
Block a user