Merge pull request #4322 from bska/conditional-infostep-output

Don't Output INFOSTEP File by Default
This commit is contained in:
Bård Skaflestad 2022-12-15 17:46:03 +01:00 committed by GitHub
commit 4827de23ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 434 additions and 18 deletions

View File

@ -41,6 +41,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/core/props/satfunc/RelpermDiagnostics.cpp
opm/simulators/timestepping/SimulatorReport.cpp
opm/simulators/flow/countGlobalCells.cpp
opm/simulators/flow/ConvergenceOutputConfiguration.cpp
opm/simulators/flow/KeywordValidation.cpp
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.cpp
opm/simulators/flow/ValidationFunctions.cpp
@ -171,6 +172,7 @@ endif()
list (APPEND TEST_SOURCE_FILES
tests/test_ALQState.cpp
tests/test_blackoil_amg.cpp
tests/test_convergenceoutputconfiguration.cpp
tests/test_convergencereport.cpp
tests/test_deferredlogger.cpp
tests/test_eclinterregflows.cpp
@ -271,6 +273,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/flow/countGlobalCells.hpp
opm/simulators/flow/BlackoilModelEbos.hpp
opm/simulators/flow/BlackoilModelParametersEbos.hpp
opm/simulators/flow/ConvergenceOutputConfiguration.hpp
opm/simulators/flow/FlowMainEbos.hpp
opm/simulators/flow/Main.hpp
opm/simulators/flow/NonlinearSolverEbos.hpp

View File

@ -0,0 +1,128 @@
/*
Copyright 2022 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 <opm/simulators/flow/ConvergenceOutputConfiguration.hpp>
#include <algorithm>
#include <cstddef>
#include <regex>
#include <stdexcept>
#include <string_view>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <fmt/format.h>
namespace {
std::vector<std::string> tokenizeOptionValues(std::string_view options)
{
const auto split = std::regex { R"(\s*,\s*)" };
return {
std::cregex_token_iterator {
options.begin(), options.end(), split, -1
},
std::cregex_token_iterator {}
};
}
void reportUnsupportedOptionValuesAndThrow(std::vector<std::string> unsupp,
std::string_view optionName)
{
std::sort(unsupp.begin(), unsupp.end());
auto u = std::unique(unsupp.begin(), unsupp.end());
const auto pl = (std::distance(unsupp.begin(), u) != 1) ? "s" : "";
if (optionName.empty()) {
throw std::invalid_argument {
fmt::format("Unsupported convergence output "
"option value{}: {}\n"
"Supported values are \"none\", "
"\"steps\", and \"iterations\"",
pl, fmt::join(unsupp.begin(), u, ", "))
};
}
throw std::invalid_argument {
fmt::format("Option {}:\n - Unsupported value{}: {}\n"
" - Supported values are \"none\", "
"\"steps\", and \"iterations\"",
optionName, pl,
fmt::join(unsupp.begin(), u, ", "))
};
}
std::vector<Opm::ConvergenceOutputConfiguration::Option>
getOptions(std::string_view options, std::string_view optionName)
{
using Option = Opm::ConvergenceOutputConfiguration::Option;
auto opt = std::vector<Option>{};
const auto values = std::unordered_map<std::string, Option> {
{ "none" , Option::None },
{ "step" , Option::Steps }, // Alias for 'steps' (plural)
{ "steps" , Option::Steps },
{ "iteration" , Option::Iterations }, // Alias for 'iterations' (plural)
{ "iterations", Option::Iterations },
};
auto unsupp = std::vector<std::string>{};
for (const auto& value : tokenizeOptionValues(options)) {
if (auto option = values.find(value); option != values.end()) {
opt.push_back(option->second);
}
else {
unsupp.push_back(value);
}
}
if (! unsupp.empty()) {
reportUnsupportedOptionValuesAndThrow(std::move(unsupp), optionName);
}
return opt;
}
} // Anonymous namespace
// ===========================================================================
// Public Interface Below Separator
// ===========================================================================
Opm::ConvergenceOutputConfiguration::
ConvergenceOutputConfiguration(std::string_view options,
std::string_view optionName)
{
auto is_none = false;
for (const auto& option : getOptions(options, optionName)) {
if (option == Option::None) {
is_none = true;
break;
}
this->flag_ |= static_cast<std::byte>(option);
}
if (is_none) {
// Recall: "none" overrides all other options.
this->flag_ = std::byte{0};
}
}

View File

@ -0,0 +1,93 @@
/*
Copyright 2022 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 CONVERGENCE_OUTPUT_CONFIGURATION_HPP
#define CONVERGENCE_OUTPUT_CONFIGURATION_HPP
#include <cstddef>
#include <string_view>
namespace Opm {
/// Parse comma separated option strings into a runtime configuration object
/// for whether to output additional convergence information and, if so,
/// what information to output.
///
/// Supported option string values are
///
/// * "none" -- Dont want any additional convergence output.
///
/// * "steps" -- Want additional convergence output pertaining to the
/// converged solution at the end of each timestep.
///
/// * "iterations" -- Want additional convergence output pertaining to each
/// non-linar ieration in each timestep.
///
/// Option value "none" overrides all other options. In other words, if the
/// user requests "none", then there will be no additional convergence
/// output, even if there are other options in the option string.
class ConvergenceOutputConfiguration
{
public:
/// Option values.
///
/// None overrides all other options. In other words, if the user
/// requests None, then there will be no additional convergence output,
/// even if there are other options in the option string.
enum class Option : unsigned char {
None = 0,
Steps = 1 << 1,
Iterations = 1 << 2,
};
/// Constructor
///
/// Parses comma separated option string into runtime configuration
/// option.
///
/// \param[in] options Comma separated option string.
///
/// \param[in] optionName Name of command line option whose value is \p
/// options. Used as diagnostic information only, and only if
/// specified.
explicit ConvergenceOutputConfiguration(std::string_view options,
std::string_view optionName = "");
/// Whether or not user wants any additional convergence output at all.
bool any() const
{
return this->flag_ != std::byte{0};
}
/// Whether or not user wants specific convergence output.
///
/// \param[in] opt Specific convergence output type.
bool want(const Option opt) const
{
return std::to_integer<int>(this->flag_ & static_cast<std::byte>(opt)) != 0;
}
private:
/// Option flags. Treated as a small bitset.
std::byte flag_{0};
};
} // namespace Opm
#endif // CONVERGENCE_OUTPUT_CONFIGURATION_HPP

View File

@ -25,6 +25,7 @@
#include <sys/utsname.h>
#include <opm/simulators/flow/ConvergenceOutputConfiguration.hpp>
#include <opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.hpp>
#include <opm/simulators/utils/ParallelFileMerger.hpp>
#include <opm/simulators/utils/moduleVersion.hpp>
@ -574,27 +575,37 @@ namespace Opm
// Output summary after simulation has completed
void runSimulatorAfterSim_(SimulatorReport &report)
{
if (this->output_cout_) {
std::ostringstream ss;
ss << "\n\n================ End of simulation ===============\n\n";
ss << fmt::format("Number of MPI processes: {:9}\n", mpi_size_ );
#if _OPENMP
int threads = omp_get_max_threads();
if (! this->output_cout_) {
return;
}
const int threads
#if !defined(_OPENMP) || !_OPENMP
= 1;
#else
int threads = 1;
= omp_get_max_threads();
#endif
ss << fmt::format("Threads per MPI process: {:9}\n", threads);
report.reportFullyImplicit(ss);
OpmLog::info(ss.str());
const std::string dir = eclState().getIOConfig().getOutputDir();
std::ostringstream ss;
ss << "\n\n================ End of simulation ===============\n\n";
ss << fmt::format("Number of MPI processes: {:9}\n", mpi_size_ );
ss << fmt::format("Threads per MPI process: {:9}\n", threads);
report.reportFullyImplicit(ss);
OpmLog::info(ss.str());
const auto extraConvOutput = ConvergenceOutputConfiguration {
EWOMS_GET_PARAM(TypeTag, std::string, ExtraConvergenceOutput),
R"(ExtraConvergenceOutput (--extra-convergence-output))"
};
if (extraConvOutput.want(ConvergenceOutputConfiguration::Option::Steps)) {
namespace fs = ::std::filesystem;
fs::path output_dir(dir);
{
std::string filename = eclState().getIOConfig().getBaseName() + ".INFOSTEP";
fs::path fullpath = output_dir / filename;
std::ofstream os(fullpath.string());
report.fullReports(os);
}
const auto infostep = fs::path { eclState().getIOConfig().getOutputDir() } /
fs::path { eclState().getIOConfig().getBaseName() }.concat(".INFOSTEP");
std::ofstream os(infostep);
report.fullReports(os);
}
}

View File

@ -44,6 +44,12 @@ struct EnableTuning {
using type = UndefinedProperty;
};
template <class TypeTag, class MyTypeTag>
struct ExtraConvergenceOutput
{
using type = UndefinedProperty;
};
template<class TypeTag>
struct EnableTerminalOutput<TypeTag, TTag::EclFlowProblem> {
static constexpr bool value = true;
@ -57,6 +63,12 @@ struct EnableTuning<TypeTag, TTag::EclFlowProblem> {
static constexpr bool value = false;
};
template <class TypeTag>
struct ExtraConvergenceOutput<TypeTag, TTag::EclFlowProblem>
{
static constexpr auto* value = "none";
};
} // namespace Opm::Properties
namespace Opm {
@ -137,6 +149,15 @@ public:
"Use adaptive time stepping between report steps");
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableTuning,
"Honor some aspects of the TUNING keyword.");
EWOMS_REGISTER_PARAM(TypeTag, std::string, ExtraConvergenceOutput,
"Provide additional convergence output "
"files for diagnostic purposes. "
"\"none\" gives no extra output and "
"overrides all other options, "
"\"steps\" generates an INFOSTEP file, "
"\"iterations\" generates an INFOITER file. "
"Combine options with commas, e.g., "
"\"steps,iterations\" for multiple outputs.");
}
/// Run the simulation.

View File

@ -0,0 +1,160 @@
/*
Copyright 2022 Equinor.
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>
#define BOOST_TEST_MODULE TestConvergenceOutputConfiguration
#include <boost/test/unit_test.hpp>
#include <opm/simulators/flow/ConvergenceOutputConfiguration.hpp>
#include <cstddef>
#include <stdexcept>
#include <string_view>
#include <string>
BOOST_AUTO_TEST_SUITE(Common_Operations)
BOOST_AUTO_TEST_CASE(None)
{
const auto config = Opm::ConvergenceOutputConfiguration{"none"};
BOOST_CHECK_MESSAGE(! config.any(),
"Configuration object with option value "
"\"none\" must NOT activate output");
}
BOOST_AUTO_TEST_CASE(Steps)
{
const auto config = Opm::ConvergenceOutputConfiguration{"steps"};
BOOST_CHECK_MESSAGE(config.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(config.want(Opm::ConvergenceOutputConfiguration::Option::Steps),
"Configuration object with \"steps\" "
"option value must activate Steps option");
}
BOOST_AUTO_TEST_CASE(Steps_Alias)
{
const auto config = Opm::ConvergenceOutputConfiguration{"step"};
BOOST_CHECK_MESSAGE(config.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(config.want(Opm::ConvergenceOutputConfiguration::Option::Steps),
"Configuration object with \"step\" "
"option value must activate Steps option");
}
BOOST_AUTO_TEST_CASE(Iterations)
{
const auto config = Opm::ConvergenceOutputConfiguration{"iterations"};
BOOST_CHECK_MESSAGE(config.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(config.want(Opm::ConvergenceOutputConfiguration::Option::Iterations),
"Configuration object with \"iterations\" "
"option value must activate Steps option");
}
BOOST_AUTO_TEST_CASE(Iterations_Alias)
{
const auto config = Opm::ConvergenceOutputConfiguration{"iteration"};
BOOST_CHECK_MESSAGE(config.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(config.want(Opm::ConvergenceOutputConfiguration::Option::Iterations),
"Configuration object with \"iterations\" "
"option value must activate Steps option");
}
BOOST_AUTO_TEST_CASE(Combinations)
{
const auto steps_iter = Opm::ConvergenceOutputConfiguration{"steps,iterations"};
BOOST_CHECK_MESSAGE(steps_iter.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(steps_iter.want(Opm::ConvergenceOutputConfiguration::Option::Steps),
"Configuration object with \"steps\" "
"option value must activate Steps option");
BOOST_CHECK_MESSAGE(steps_iter.want(Opm::ConvergenceOutputConfiguration::Option::Iterations),
"Configuration object with \"iterations\" "
"option value must activate Steps option");
const auto iter_steps = Opm::ConvergenceOutputConfiguration{"iterations,steps"};
BOOST_CHECK_MESSAGE(iter_steps.any(),
"Configuration object with supported "
"option value must activate option");
BOOST_CHECK_MESSAGE(iter_steps.want(Opm::ConvergenceOutputConfiguration::Option::Steps),
"Configuration object with \"steps\" "
"option value must activate Steps option");
BOOST_CHECK_MESSAGE(iter_steps.want(Opm::ConvergenceOutputConfiguration::Option::Iterations),
"Configuration object with \"iterations\" "
"option value must activate Steps option");
const auto none_iter_steps = Opm::ConvergenceOutputConfiguration{"none,iterations,steps"};
BOOST_CHECK_MESSAGE(! none_iter_steps.any(),
"Configuration object with any option "
"value \"none\" must NOT activate output");
const auto iter_none_steps = Opm::ConvergenceOutputConfiguration{"iterations,none,steps"};
BOOST_CHECK_MESSAGE(! iter_none_steps.any(),
"Configuration object with any option "
"value \"none\" must NOT activate output");
const auto steps_iter_none = Opm::ConvergenceOutputConfiguration{"steps,iterations, none"};
BOOST_CHECK_MESSAGE(! steps_iter_none.any(),
"Configuration object with any option "
"value \"none\" must NOT activate output");
}
BOOST_AUTO_TEST_SUITE_END() // Common_Operations
// ---------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(Failed_Construction)
BOOST_AUTO_TEST_CASE(Misprint)
{
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"nonce"},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"stepS"},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"steps, iter"},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"steps, iterations, non"},
std::invalid_argument);
}
BOOST_AUTO_TEST_CASE(Unknown)
{
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"Hello"},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"meow"},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{""},
std::invalid_argument);
BOOST_CHECK_THROW(Opm::ConvergenceOutputConfiguration{"xyz,zy;;;"},
std::invalid_argument);
}
BOOST_AUTO_TEST_SUITE_END()