diff --git a/opm/models/io/vtkmultiwriter.hh b/opm/models/io/vtkmultiwriter.hh
index 032320c96e8..8867e6658ba 100644
--- a/opm/models/io/vtkmultiwriter.hh
+++ b/opm/models/io/vtkmultiwriter.hh
@@ -90,7 +90,7 @@ class VtkMultiWriter : public BaseOutputWriter
// current time step
// The file names in the pvd file are relative, the path should therefore be stripped.
const std::filesystem::path fullPath{fileName};
- const std::string localFileName = fullPath.filename();
+ const std::string localFileName = fullPath.filename().string();
multiWriter_.multiFile_.precision(16);
multiWriter_.multiFile_ << " \n";
diff --git a/opm/models/pvs/pvsprimaryvariables.hh b/opm/models/pvs/pvsprimaryvariables.hh
index a6afa9ec937..b8e3a773142 100644
--- a/opm/models/pvs/pvsprimaryvariables.hh
+++ b/opm/models/pvs/pvsprimaryvariables.hh
@@ -41,6 +41,7 @@
#include
#include
+#include
#include
#include
#include
@@ -216,7 +217,18 @@ public:
* \brief Returns the phase with the lowest index that is present.
*/
unsigned lowestPresentPhaseIdx() const
- { return static_cast(ffs(phasePresence_) - 1); }
+ {
+ // No phase present: fail fast instead of returning a bogus phase index.
+ // The previous ffs()-based code returned ffs(0)-1, a huge index whose
+ // out-of-bounds access surfaced the corrupt state immediately.
+ if (phasePresence_ == 0) [[unlikely]] {
+ throw NumericalProblem("Phase presence is 0, i.e., no fluid is present");
+ }
+
+ // std::countr_zero requires an unsigned argument; its count of trailing
+ // zeros is the index of the lowest set bit, i.e. the lowest phase present.
+ return static_cast(std::countr_zero(static_cast(phasePresence_)));
+ }
/*!
* \brief Assignment operator from an other primary variables object
diff --git a/opm/models/tpsa/tpsamodel.hpp b/opm/models/tpsa/tpsamodel.hpp
index aa5d793eaf3..ea92bd34b60 100644
--- a/opm/models/tpsa/tpsamodel.hpp
+++ b/opm/models/tpsa/tpsamodel.hpp
@@ -246,7 +246,8 @@ class TpsaModel
#ifdef _OPENMP
#pragma omp parallel for
#endif
- for (const auto& chunk : element_chunks_) {
+ for (std::size_t ci = 0; ci < element_chunks_.size(); ++ci) {
+ const auto& chunk = element_chunks_[ci];
for (const auto& elem : chunk) {
const unsigned globalIdx = elementMapper.index(elem);
auto& currSol = solution(/*timeIdx=*/0)[globalIdx];
diff --git a/opm/models/utils/alignedallocator.hh b/opm/models/utils/alignedallocator.hh
index f173f5c60b1..36c0c599048 100644
--- a/opm/models/utils/alignedallocator.hh
+++ b/opm/models/utils/alignedallocator.hh
@@ -27,6 +27,10 @@
#include
#include
+#if defined(_MSC_VER)
+#include // _aligned_malloc / _aligned_free
+#endif
+
namespace Opm {
namespace detail {
@@ -80,16 +84,26 @@ inline void* aligned_alloc(std::size_t alignment,
alignment = sizeof(void*);
}
void* p;
+#if defined(_MSC_VER)
+ // MSVC has no posix_memalign; _aligned_malloc takes (size, alignment).
+ p = _aligned_malloc(size, alignment);
+#else
if (::posix_memalign(&p, alignment, size) != 0) {
p = 0;
}
+#endif
return p;
}
inline void aligned_free(void* ptr)
noexcept
{
+#if defined(_MSC_VER)
+ // Memory from _aligned_malloc must be released with _aligned_free.
+ _aligned_free(ptr);
+#else
::free(ptr);
+#endif
}
template
diff --git a/opm/models/utils/parametersystem.hpp b/opm/models/utils/parametersystem.hpp
index ca71902efa9..3ad3fbe2f9b 100644
--- a/opm/models/utils/parametersystem.hpp
+++ b/opm/models/utils/parametersystem.hpp
@@ -61,7 +61,16 @@ auto getParamName()
return Parameter::name;
} else {
std::string paramName = Dune::className();
- paramName.replace(0, std::strlen("Opm::Parameters::"), "");
+ // Strip the enclosing namespace. Do NOT assume the string starts with
+ // "Opm::Parameters::": MSVC's Dune::className prefixes the type with
+ // "struct "/"class " (e.g. "struct Opm::Parameters::EclDeckFileName"),
+ // so find the qualifier instead of erasing a fixed number of leading
+ // characters (which previously produced names like "-eters::...").
+ constexpr std::string_view nsQualifier = "Opm::Parameters::";
+ const auto nsPos = paramName.find(nsQualifier);
+ if (nsPos != std::string::npos) {
+ paramName.erase(0, nsPos + nsQualifier.size());
+ }
const auto pos = paramName.find_first_of('<');
if (pos != std::string::npos) {
paramName.erase(pos);
diff --git a/opm/models/utils/simulatorutils.cpp b/opm/models/utils/simulatorutils.cpp
index 499cc13a2f1..b3f14d0eca4 100644
--- a/opm/models/utils/simulatorutils.cpp
+++ b/opm/models/utils/simulatorutils.cpp
@@ -33,9 +33,19 @@
#include
#include
#include
-#include
#include
+#if defined(_WIN32)
+#include // _access
+#else
+#include
+#endif
+
+// MSVC's defines _S_IFMT/_S_IFDIR but not the POSIX S_ISDIR macro.
+#if defined(_MSC_VER) && !defined(S_ISDIR)
+# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
+#endif
+
#if HAVE_QUAD
#include
#endif
@@ -134,7 +144,12 @@ std::string simulatorOutputDir()
"' exists but is not a directory");
}
+#if defined(_WIN32)
+ // _access mode 2 checks for write permission, like access(..., W_OK).
+ if (_access(outputDir.c_str(), 2) != 0) {
+#else
if (access(outputDir.c_str(), W_OK) != 0) {
+#endif
throw std::runtime_error("Output directory '" + outputDir +
"' exists but is not writeable");
}
diff --git a/opm/models/utils/terminal.cpp b/opm/models/utils/terminal.cpp
index 3f585e2187e..0ff81d6c2dc 100644
--- a/opm/models/utils/terminal.cpp
+++ b/opm/models/utils/terminal.cpp
@@ -28,14 +28,31 @@
#include
#endif
+#include
#include
+#include
#include
+#include
#include
+
+#if defined(_WIN32)
+#include // _isatty, _fileno
+#else
#include
#include
+#endif
namespace {
+bool isTty(std::FILE* stream)
+{
+#if defined(_WIN32)
+ return _isatty(_fileno(stream)) != 0;
+#else
+ return isatty(fileno(stream)) != 0;
+#endif
+}
+
const char* getSignalAbbrev(int sig)
{
const auto sig_map = std::unordered_map{
@@ -43,7 +60,9 @@ const char* getSignalAbbrev(int sig)
{SIGILL, "ILL"},
{SIGABRT, "ABRT"},
{SIGFPE, "FPE"},
- {SIGKILL, "KILL"},
+#ifdef SIGKILL
+ {SIGKILL, "KILL"}, // not defined by MSVC's
+#endif
{SIGSEGV, "SEGV"},
{SIGTERM, "TERM"},
};
@@ -110,7 +129,7 @@ std::string breakLines(const std::string& msg,
int getTtyWidth()
{
int ttyWidth = 10*1000; // effectively do not break lines at all.
- if (isatty(STDOUT_FILENO) != 0) {
+ if (isTty(stdout)) {
#if defined TIOCGWINSZ
// This is a bit too linux specific, IMO. let's do it anyway
struct winsize ttySize;
@@ -129,13 +148,17 @@ void assignResetTerminalSignalHandlers()
{
// set the signal handlers to reset the TTY to a well defined state on unexpected
// program aborts
- if (isatty(STDIN_FILENO) != 0) {
+ if (isTty(stdin)) {
signal(SIGINT, resetTerminal);
- signal(SIGHUP, resetTerminal);
+#ifdef SIGHUP
+ signal(SIGHUP, resetTerminal); // not on Windows
+#endif
signal(SIGABRT, resetTerminal);
signal(SIGFPE, resetTerminal);
signal(SIGSEGV, resetTerminal);
- signal(SIGPIPE, resetTerminal);
+#ifdef SIGPIPE
+ signal(SIGPIPE, resetTerminal); // not on Windows
+#endif
signal(SIGTERM, resetTerminal);
}
}
@@ -151,8 +174,9 @@ void resetTerminal()
// it seems like some terminals sometimes takes their time to react, so let's
// accommodate them.
- usleep(/*usec=*/500*1000);
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+#if !defined(_WIN32)
// this requires the 'stty' command to be available in the command search path. on
// most linux systems, is the case. (but even if the system() function fails, the
// worst thing which can happen is that the TTY stays potentially choked up...)
@@ -160,6 +184,7 @@ void resetTerminal()
std::cout << "Executing the 'stty' command failed."
<< " Terminal might be left in an undefined state!\n";
}
+#endif
}
void resetTerminal(int signum)
@@ -178,7 +203,7 @@ void resetTerminal(int signum)
}
#endif
- if (isatty(fileno(stdout)) != 0 && isatty(fileno(stdin)) != 0) {
+ if (isTty(stdout) && isTty(stdin)) {
std::cout << "\n\nReceived signal " << signum
<< " (\"" << getSignalAbbrev(signum) << "\")."
<< " Trying to reset the terminal.\n";
diff --git a/opm/simulators/flow/Banners.cpp b/opm/simulators/flow/Banners.cpp
index b164e47eb78..1e39fd829c6 100644
--- a/opm/simulators/flow/Banners.cpp
+++ b/opm/simulators/flow/Banners.cpp
@@ -32,18 +32,66 @@
#include
#include
#include
+#include
#include
-#include
+#include
#include
+
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include
+#include // getenv
+#else
+#include
#include
+#endif
namespace {
unsigned long long getTotalSystemMemory()
{
+#if defined(_WIN32)
+ // Windows has no sysconf(_SC_PHYS_PAGES); query the total physical memory.
+ MEMORYSTATUSEX status;
+ status.dwLength = sizeof(status);
+ if (GlobalMemoryStatusEx(&status)) {
+ return static_cast(status.ullTotalPhys);
+ }
+ return 0;
+#else
long pages = sysconf(_SC_PHYS_PAGES);
long page_size = sysconf(_SC_PAGE_SIZE);
return pages * page_size;
+#endif
+}
+
+struct SystemDescription
+{
+ std::string nodename;
+ std::string os; // empty when no operating-system description is available
+};
+
+std::optional getSystemDescription()
+{
+#if defined(_WIN32)
+ // Windows has no uname()/utsname; query the machine name via the Win32 API.
+ char computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {};
+ DWORD computer_name_len = sizeof(computer_name);
+ return SystemDescription {
+ GetComputerNameA(computer_name, &computer_name_len) ? computer_name : "unknown",
+ ""
+ };
+#else
+ struct utsname arch;
+ if (uname(&arch) != 0) {
+ return std::nullopt;
+ }
+ std::ostringstream os;
+ os << arch.sysname << " " << arch.machine << " (Kernel: " << arch.release
+ << ", " << arch.version << " )";
+ return SystemDescription { arch.nodename, os.str() };
+#endif
}
}
@@ -57,8 +105,11 @@ void printPRTHeader(const int nprocs, const int nthreads,
{
const double megabyte = 1024 * 1024;
unsigned num_cpu = std::thread::hardware_concurrency();
- struct utsname arch;
+#if defined(_WIN32)
+ const char* user = getenv("USERNAME"); // no getlogin() on Windows
+#else
const char* user = getlogin();
+#endif
std::time_t now = std::time(0);
struct std::tm tstruct;
char tmstr[80];
@@ -75,12 +126,13 @@ void printPRTHeader(const int nprocs, const int nthreads,
ss << "Flow is a simulator for fully implicit three-phase black-oil flow,";
ss << " and is part of OPM.\nFor more information visit: https://opm-project.org \n\n";
ss << "Flow Version = " << moduleVersion << "\n";
- if (uname(&arch) == 0) {
- ss << "Machine name = " << arch.nodename << " (Number of logical cores: " << num_cpu;
- ss << ", Memory size: " << std::fixed << std::setprecision (2) << mem_size << " MB) \n";
- ss << "Operating system = " << arch.sysname << " " << arch.machine << " (Kernel: " << arch.release;
- ss << ", " << arch.version << " )\n";
- ss << "Build time = " << compileTimestamp << "\n";
+ if (const auto sys = getSystemDescription()) {
+ ss << "Machine name = " << sys->nodename << " (Number of logical cores: " << num_cpu;
+ ss << ", Memory size: " << std::fixed << std::setprecision(2) << mem_size << " MB) \n";
+ if (!sys->os.empty()) {
+ ss << "Operating system = " << sys->os << "\n";
+ }
+ ss << "Build time = " << compileTimestamp << "\n";
}
if (user) {
ss << "User = " << user << std::endl;
diff --git a/opm/simulators/flow/ConvergenceOutputConfiguration.cpp b/opm/simulators/flow/ConvergenceOutputConfiguration.cpp
index 74a246ff526..6b06268813e 100644
--- a/opm/simulators/flow/ConvergenceOutputConfiguration.cpp
+++ b/opm/simulators/flow/ConvergenceOutputConfiguration.cpp
@@ -36,12 +36,18 @@ namespace {
std::vector 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 {}
- };
+ // Construct from the iterator range explicitly: a braced {first, last}
+ // is interpreted by MSVC as an initializer_list
+ // rather than the (first, last) range constructor.
+ // Use data()/data()+size() raw pointers: std::cregex_token_iterator needs
+ // const char* iterators, but MSVC's std::string_view::begin() is a checked
+ // iterator class (not const char*), which does not convert.
+ return std::vector(
+ std::cregex_token_iterator(
+ options.data(), options.data() + options.size(), split, -1
+ ),
+ std::cregex_token_iterator()
+ );
}
void reportUnsupportedOptionValuesAndThrow(std::vector unsupp,
diff --git a/opm/simulators/flow/FIBlackoilModel.hpp b/opm/simulators/flow/FIBlackoilModel.hpp
index 5a56ed016f8..80a784fa88a 100644
--- a/opm/simulators/flow/FIBlackoilModel.hpp
+++ b/opm/simulators/flow/FIBlackoilModel.hpp
@@ -93,7 +93,8 @@ class FIBlackOilModel : public BlackOilModel
#ifdef _OPENMP
#pragma omp parallel for
#endif
- for (const auto& chunk : element_chunks_) {
+ for (std::size_t ci = 0; ci < element_chunks_.size(); ++ci) {
+ const auto& chunk = element_chunks_[ci];
ElementContext elemCtx(this->simulator_);
for (const auto& elem : chunk) {
elemCtx.updatePrimaryStencil(elem);
@@ -257,7 +258,8 @@ class FIBlackOilModel : public BlackOilModel
#ifdef _OPENMP
#pragma omp parallel for
#endif
- for (const auto& chunk : element_chunks_) {
+ for (std::size_t ci = 0; ci < element_chunks_.size(); ++ci) {
+ const auto& chunk = element_chunks_[ci];
for (const auto& elem : chunk) {
this->template updateSingleCachedIntQuantUnchecked(elementMapper.index(elem), timeIdx);
}
diff --git a/opm/simulators/flow/FacePropertiesTPSA_impl.hpp b/opm/simulators/flow/FacePropertiesTPSA_impl.hpp
index 4338b78e0fa..7ce2d1772aa 100644
--- a/opm/simulators/flow/FacePropertiesTPSA_impl.hpp
+++ b/opm/simulators/flow/FacePropertiesTPSA_impl.hpp
@@ -150,11 +150,13 @@ update()
ThreadSafeMapBuilder distanceBoundaryMap(distanceBoundary_, num_threads, MapBuilderInsertionMode::Insert_Or_Assign);
ThreadSafeMapBuilder faceNormalBoundaryMap(faceNormalBoundary_, num_threads, MapBuilderInsertionMode::Insert_Or_Assign);
+ // Loop over grid element an compute face properties
+ const auto element_chunks = ElementChunks(gridView_, Dune::Partitions::all, num_threads);
#ifdef _OPENMP
#pragma omp parallel for
#endif
- // Loop over grid element an compute face properties
- for (const auto& chunk : ElementChunks(gridView_, Dune::Partitions::all, num_threads)) {
+ for (std::size_t ci = 0; ci < element_chunks.size(); ++ci) {
+ const auto& chunk = element_chunks[ci];
for (const auto& elem : chunk) {
// Init. face info for inside/outside cells
FaceInfo inside;
diff --git a/opm/simulators/flow/GenericOutputBlackoilModule.cpp b/opm/simulators/flow/GenericOutputBlackoilModule.cpp
index f872a770f7e..4e1690f22d3 100644
--- a/opm/simulators/flow/GenericOutputBlackoilModule.cpp
+++ b/opm/simulators/flow/GenericOutputBlackoilModule.cpp
@@ -590,6 +590,25 @@ regionSum(const ScalarBuffer& property,
return totals;
}
+namespace {
+// How the phase is encoded in an output keyword. Defined at namespace scope
+// (not locally inside doAllocBuffers) because MSVC otherwise treats this local
+// enum as a distinct type per template instantiation and fails to resolve
+// EntryPhaseType::GasWatOil in the default member initializer below (C2439/C2440).
+enum class EntryPhaseType {
+ // one kw for all fields (RESIDUAL)
+ None,
+ // append GWO to kw name (KR(G|W|O))
+ GWO,
+ // pure kw controls all, additionally append GWO per phase (DEN, DENG, ...)
+ NGWO,
+ // append gas/wat/oil to kw name (SGAS, SWAT, SOIL)
+ GasWatOil,
+ // pure kw controls all, then first letter + gas/wat/oil per phase (VISC, VGAS, ...)
+ NGasWatOil,
+};
+} // anonymous namespace
+
template
void GenericOutputBlackoilModule::
doAllocBuffers(const unsigned bufferSize,
@@ -676,28 +695,7 @@ doAllocBuffers(const unsigned bufferSize,
return;
}
- enum class EntryPhaseType {
- // one kw for all fields
- // RESIDUAL
- None,
-
- // append GWO to kw name
- // KR(G|W|O)
- GWO,
-
- // Pure kw controls all, additionally append GWO to kw name for each phase
- // DEN, DENG, DENW, DENO
- NGWO,
-
- // append gas/wat/oil to kw name
- // SGAS, SWAT, SOIL
- GasWatOil,
-
- // Pure kw controls all, then first letter of kw and apply gas/wat/oil for each phase.
- // VISC, VGAS, VWAT, VOIL
- NGasWatOil,
- };
-
+ // EntryPhaseType is defined at namespace scope above (MSVC workaround).
struct Entry {
std::variant*,
diff --git a/opm/simulators/flow/KeywordValidation.hpp b/opm/simulators/flow/KeywordValidation.hpp
index 196bad627d9..fafe34f16dd 100644
--- a/opm/simulators/flow/KeywordValidation.hpp
+++ b/opm/simulators/flow/KeywordValidation.hpp
@@ -30,6 +30,7 @@
#include
#include