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 #include +#include #include #include #include diff --git a/opm/simulators/flow/NlddReporting.hpp b/opm/simulators/flow/NlddReporting.hpp index 4005bfa9908..9225acdde5e 100644 --- a/opm/simulators/flow/NlddReporting.hpp +++ b/opm/simulators/flow/NlddReporting.hpp @@ -146,7 +146,7 @@ void writeNonlinearIterationsPerCell( // Only rank 0 writes the file if (rank == 0) { - details::writeNlddFile(odir / "ResInsight_nonlinear_iterations.txt", + details::writeNlddFile((odir / "ResInsight_nonlinear_iterations.txt").string(), "NLDD_ITER", full_iterations); } } @@ -189,7 +189,7 @@ void writePartitions( // Only rank 0 writes the file if (rank == 0) { - details::writeNlddFile(odir / "ResInsight_compatible_partition.txt", + details::writeNlddFile((odir / "ResInsight_compatible_partition.txt").string(), "NLDD_DOM", full_partition); } diff --git a/opm/simulators/flow/NonlinearSystemBlackOilReservoir.hpp b/opm/simulators/flow/NonlinearSystemBlackOilReservoir.hpp index 9dedd6b25eb..5a49d9c3d52 100644 --- a/opm/simulators/flow/NonlinearSystemBlackOilReservoir.hpp +++ b/opm/simulators/flow/NonlinearSystemBlackOilReservoir.hpp @@ -127,11 +127,13 @@ class NonlinearSystemBlackOilReservoir : public NonlinearSystem Scalar dRvMax = 0.0; }; - // Output debug flags for which tolerances used + // Output debug flags for which tolerances used. + // NB: not named STRICT/RELAXED because (pulled in transitively + // on Windows) defines STRICT as a macro. enum class DebugFlags { - STRICT = 0, - RELAXED = 1, - TUNINGDP = 2 + Strict = 0, + Relaxed = 1, + TuningDP = 2 }; // --------- Public methods --------- diff --git a/opm/simulators/flow/NonlinearSystemBlackOilReservoir_impl.hpp b/opm/simulators/flow/NonlinearSystemBlackOilReservoir_impl.hpp index 1b73fdbacea..39ea5b909ac 100644 --- a/opm/simulators/flow/NonlinearSystemBlackOilReservoir_impl.hpp +++ b/opm/simulators/flow/NonlinearSystemBlackOilReservoir_impl.hpp @@ -62,9 +62,9 @@ namespace { using F = typename Opm::NonlinearSystemBlackOilReservoir::DebugFlags; switch (f) { - case F::STRICT: return "Strict"; - case F::RELAXED: return "Relaxed"; - case F::TUNINGDP: return "TuningDP"; + case F::Strict: return "Strict"; + case F::Relaxed: return "Relaxed"; + case F::TuningDP: return "TuningDP"; } return "< ??? >"; @@ -928,14 +928,14 @@ getReservoirConvergence(const double reportTime, } const auto mb_flag = use_relaxed_mb - ? DebugFlags::RELAXED - : DebugFlags::STRICT; + ? DebugFlags::Relaxed + : DebugFlags::Strict; const auto cnv_flag = relax_dsol_cnv ? - DebugFlags::TUNINGDP + DebugFlags::TuningDP : (use_relaxed_cnv - ? DebugFlags::RELAXED - : DebugFlags::STRICT); + ? DebugFlags::Relaxed + : DebugFlags::Strict); ss << std::setw(9) << make_string(mb_flag) << std::setw(9) << make_string(cnv_flag); diff --git a/opm/simulators/flow/RegionPhasePVAverage.cpp b/opm/simulators/flow/RegionPhasePVAverage.cpp index 356be125cab..ee5e36a686f 100644 --- a/opm/simulators/flow/RegionPhasePVAverage.cpp +++ b/opm/simulators/flow/RegionPhasePVAverage.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include namespace { diff --git a/opm/simulators/flow/SimulatorSerializer.cpp b/opm/simulators/flow/SimulatorSerializer.cpp index 61a63ff7893..7f36aee2282 100644 --- a/opm/simulators/flow/SimulatorSerializer.cpp +++ b/opm/simulators/flow/SimulatorSerializer.cpp @@ -85,7 +85,7 @@ SimulatorSerializer::SimulatorSerializer([[maybe_unused]] SerializableSim& simul if (loadStep_ != -1 && !std::filesystem::exists(loadFile_)) { std::filesystem::path path(ioconfig.getInputDir() + "/"); path.replace_filename(ioconfig.getBaseName() + ".OPMRST"); - loadFile_ = path; + loadFile_ = path.string(); if (!std::filesystem::exists(loadFile_)) { OPM_THROW(std::runtime_error, "Error locating serialized restart file " + loadFile_); } diff --git a/opm/simulators/flow/TracerModel.hpp b/opm/simulators/flow/TracerModel.hpp index 53054a17a22..094ef33b574 100644 --- a/opm/simulators/flow/TracerModel.hpp +++ b/opm/simulators/flow/TracerModel.hpp @@ -627,7 +627,8 @@ class TracerModel : public GenericTracerModel std::vector ReservoirCouplingSpawnSlaves:: getSlaveArgv_( - const std::filesystem::path &data_file, + // Narrow std::string (not std::filesystem::path): path::c_str() is wchar_t* + // on Windows, but slave_argv holds char*. The caller keeps the backing + // string alive for as long as the returned argv is used. + const std::string &data_file, const std::string &slave_name, std::string &log_filename) const { @@ -475,9 +478,12 @@ spawnSlaveProcesses_() std::filesystem::path dir_path{directory_path}; std::filesystem::path data_file{data_file_name}; std::filesystem::path full_path = dir_path / data_file; + // Keep the narrow path string alive for as long as slave_argv (which + // points into it) is used below in MPI_Comm_spawn. + std::string full_path_str = full_path.string(); std::string log_filename; // the getSlaveArgv() function will set this std::vector slave_argv = this->getSlaveArgv_( - full_path, slave_name, log_filename + full_path_str, slave_name, log_filename ); auto num_procs = slave.numprocs(); std::vector errcodes(num_procs); diff --git a/opm/simulators/flow/rescoup/ReservoirCouplingSpawnSlaves.hpp b/opm/simulators/flow/rescoup/ReservoirCouplingSpawnSlaves.hpp index e1beb0e30fe..798ef7b13f3 100644 --- a/opm/simulators/flow/rescoup/ReservoirCouplingSpawnSlaves.hpp +++ b/opm/simulators/flow/rescoup/ReservoirCouplingSpawnSlaves.hpp @@ -54,7 +54,7 @@ class ReservoirCouplingSpawnSlaves { std::pair, std::size_t> getMasterGroupNamesForSlave_(std::size_t slave_idx) const; std::vector getSlaveArgv_( - const std::filesystem::path &data_file, + const std::string &data_file, const std::string &slave_name, std::string &log_filename) const; void receiveActivationDateFromSlaves_(); diff --git a/opm/simulators/linalg/DILU.hpp b/opm/simulators/linalg/DILU.hpp index 7e4ce5f7a6d..42ce183e735 100644 --- a/opm/simulators/linalg/DILU.hpp +++ b/opm/simulators/linalg/DILU.hpp @@ -207,10 +207,14 @@ class MultithreadDILU : public PreconditionerWithUpdate void parallelUpdate() { + // Hoist the bound and use the '<' canonical form: MSVC's /openmp:llvm + // ICEs (C1001) on 'row != A_.N()' with a re-evaluated function-call + // bound. Equivalent on all platforms. + const std::size_t num_rows = A_.N(); #ifdef _OPENMP #pragma omp parallel for #endif - for (std::size_t row = 0; row != A_.N(); ++row) { + for (std::size_t row = 0; row < num_rows; ++row) { Dinv_[natural_to_reorder_[row]] = A_[row][row]; } diff --git a/opm/simulators/linalg/getQuasiImpesWeights.hpp b/opm/simulators/linalg/getQuasiImpesWeights.hpp index 786faf1d507..a88c3ad186f 100644 --- a/opm/simulators/linalg/getQuasiImpesWeights.hpp +++ b/opm/simulators/linalg/getQuasiImpesWeights.hpp @@ -184,7 +184,8 @@ namespace Amg #ifdef _OPENMP #pragma omp parallel for private(block, bweights, block_transpose, storage) if(enable_thread_parallel) #endif - for (const auto& chunk : element_chunks) { + for (std::size_t ci = 0; ci < element_chunks.size(); ++ci) { + const auto& chunk = element_chunks[ci]; const std::size_t thread_id = ThreadManager::threadId(); ElementContext localElemCtx(elemCtx.simulator()); @@ -257,7 +258,8 @@ namespace Amg #ifdef _OPENMP #pragma omp parallel for private(bweights) if(enable_thread_parallel) #endif - for (const auto& chunk : element_chunks) { + for (std::size_t ci = 0; ci < element_chunks.size(); ++ci) { + const auto& chunk = element_chunks[ci]; // Each thread gets a unique copy of elemCtx ElementContext localElemCtx(elemCtx.simulator()); diff --git a/opm/simulators/utils/ParallelFileMerger.cpp b/opm/simulators/utils/ParallelFileMerger.cpp index 60f161b358f..e6a4ee3a222 100644 --- a/opm/simulators/utils/ParallelFileMerger.cpp +++ b/opm/simulators/utils/ParallelFileMerger.cpp @@ -52,7 +52,8 @@ ParallelFileMerger::ParallelFileMerger(const fs::path& output_dir, void ParallelFileMerger::operator()(const fs::path& file) { std::smatch matches; - std::string filename = file.filename().native(); + // .native() yields std::wstring on Windows; .string() gives std::string. + std::string filename = file.filename().string(); if (std::regex_match(filename, matches, fileWarningRegex_)) { std::string rank = std::regex_replace(filename, fileWarningRegex_, "$1"); diff --git a/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp b/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp index a33c8172e00..3349a3ccf23 100644 --- a/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp +++ b/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.cpp @@ -205,7 +205,7 @@ reportFailures(const ViolationLevel level, // =========================================================================== template -void Opm::SatfuncConsistencyChecks::ViolationSample::clear() +void Opm::Detail::SatfuncConsistencyViolationSample::clear() { this->count.clear(); this->pointID.clear(); diff --git a/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp b/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp index a02e35f1b2c..71f5308c81a 100644 --- a/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp +++ b/opm/simulators/utils/satfunc/SatfuncConsistencyChecks.hpp @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -37,6 +38,30 @@ namespace Opm { namespace Opm { + namespace Detail { + /// Sample of consistency check violations at a single severity level. + /// + /// Defined at namespace scope rather than nested inside + /// SatfuncConsistencyChecks because MSVC fails to treat a nested class + /// as complete when it is used as the element type of a std::array data + /// member of the same enclosing class template (C2079). + template + struct SatfuncConsistencyViolationSample + { + /// Number of consistency check violations (size = number of checks). + std::vector count{}; + + /// Sample of point IDs for violated consistency checks. + std::vector pointID{}; + + /// Scalar values for each sampled point. + std::vector checkValues{}; + + /// Clear contents of all data members. + void clear(); + }; + } // namespace Detail + /// Platform for running sets of consistency checks against collection /// of saturation function end-points /// @@ -251,32 +276,9 @@ namespace Opm { using RandomBitGenerator = std::minstd_rand; /// Sample of consistency check violations at single severity level. - struct ViolationSample - { - /// Number of consistency check violations. - /// - /// Size equal to number of consistency checks. - std::vector count{}; - - /// Sample of point IDs for violated consistency checks. - /// - /// \c numSamplePoints_ allocated for each consistency check. - /// Number of valid entries for check \c i is minimum of \c - /// numSamplePoints_ and \code count[i] \endcode. - std::vector pointID{}; - - /// Scalar values for each sampled point. - /// - /// \c numSamplePoints_ allocated for each individual check, and - /// the number of values per check determined by \code - /// Check::numExportedCheckValues() \endcode. Number of valid - /// entries for check \c i is minimum of \c numSamplePoints_ and - /// \code count[i] \endcode. - std::vector checkValues{}; - - /// Clear contents of all data members. - void clear(); - }; + /// (Defined at namespace scope as Detail::SatfuncConsistencyViolationSample + /// to work around an MSVC nested-type/std::array completeness bug.) + using ViolationSample = Detail::SatfuncConsistencyViolationSample; /// Collection of consistency check violations. /// diff --git a/opm/simulators/wells/GasLiftStage2.cpp b/opm/simulators/wells/GasLiftStage2.cpp index efbfe76ffa3..0cb23de9cc8 100644 --- a/opm/simulators/wells/GasLiftStage2.cpp +++ b/opm/simulators/wells/GasLiftStage2.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index 5eef2885654..f94819913b9 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -40,6 +40,8 @@ #include #include +#include + namespace Opm { diff --git a/tests/models/test_tasklets_failure.cpp b/tests/models/test_tasklets_failure.cpp index 2ce0c31befe..990f5dbff13 100644 --- a/tests/models/test_tasklets_failure.cpp +++ b/tests/models/test_tasklets_failure.cpp @@ -33,9 +33,16 @@ #include #include #include +#include + +#if defined(_WIN32) +#include +#include +#include +#else #include #include -#include +#endif #include "config.h" #include @@ -127,6 +134,32 @@ void execute () { runner->barrier(); } +#if defined(_WIN32) +int main(int argc, char** argv) +{ + // Windows has no fork(); re-run this executable as a child process so the + // parent can verify the child exits with EXIT_FAILURE when a tasklet fails. + if (argc > 1 && std::strcmp(argv[1], "--child") == 0) { + execute(); + // execute() is expected to exit(EXIT_FAILURE) before reaching here; + // returning success makes the parent's check fail if it does not + // (mirrors the _exit(0) "should never reach here" of the POSIX branch). + return EXIT_SUCCESS; + } + std::cout << "Checking failure of child process with parent process" << std::endl; + const intptr_t status = _spawnl(_P_WAIT, argv[0], argv[0], "--child", + static_cast(nullptr)); + // status is the child's exit code, or -1 if it could not be spawned. + // Check explicitly rather than with assert() so the test still fails + // in NDEBUG (Release) builds when the tasklet failure mechanism breaks. + if (status != EXIT_FAILURE) { + std::cerr << "Child process did not exit with EXIT_FAILURE (status: " + << status << ")" << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} +#else int main() { pid_t pid = fork(); // Create a new process, such that this child process can call exit(EXIT_FAILURE) @@ -145,3 +178,4 @@ int main() assert(WEXITSTATUS(status) == EXIT_FAILURE); // Check if the exit status is EXIT_FAILURE } } +#endif diff --git a/tests/test_networkpressure.cpp b/tests/test_networkpressure.cpp index cc09eedefd1..216e2d5bac9 100644 --- a/tests/test_networkpressure.cpp +++ b/tests/test_networkpressure.cpp @@ -28,6 +28,14 @@ #include #include +// The pressure computation uses a Dune parallel Communication, which requires +// MPIHelper::instance() to have been called first when built with MPI. Use the +// shared global fixture (a no-op in a serial build), which also installs an MPI +// error handler that prints a diagnostic string on failure. +#include "MpiFixture.hpp" + +BOOST_GLOBAL_FIXTURE(MPIFixture); + #include #include @@ -382,4 +390,4 @@ BOOST_AUTO_TEST_CASE(production_pressure_computation) BOOST_CHECK_CLOSE(pressures.at("G1"), expected_pressure, 1e-7); } -BOOST_AUTO_TEST_SUITE_END() // NetworkPressureComputationTests \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() // NetworkPressureComputationTests diff --git a/tests/test_outputdir.cpp b/tests/test_outputdir.cpp index c2019e4b141..2fa1247d690 100644 --- a/tests/test_outputdir.cpp +++ b/tests/test_outputdir.cpp @@ -100,9 +100,9 @@ BOOST_FIXTURE_TEST_CASE(WithOutputDir, Fixture) PathPair{input_path / "subdir", input_path / "output2"}, PathPair{input_path / "subdir" / "subdir", input_path / "output3"}}) { const std::string output_path = "--output-dir=" + Case.second.string(); - const std::string input_file_path = (Case.first / "INPUT.DATA"); + const std::string input_file_path = (Case.first / "INPUT.DATA").string(); - const std::string output_dbg_path = (Case.second / "INPUT.DBG"); + const std::string output_dbg_path = (Case.second / "INPUT.DBG").string(); if (createFaultyFileWithDirectory) { std::filesystem::create_directories(Case.second); // Create file with faulty content @@ -143,9 +143,9 @@ BOOST_FIXTURE_TEST_CASE(NoOutputDir, Fixture) for (const auto& Case : {input_path / "subdir" / "subdir", input_path / "subdir"}) { - const std::string input_file_path = (Case / "INPUT.DATA"); + const std::string input_file_path = (Case / "INPUT.DATA").string(); - const std::string output_dbg_path = (Case / "INPUT.DBG"); + const std::string output_dbg_path = (Case / "INPUT.DBG").string(); // Create file with faulty content std::string dummy = R"(dummy)"; std::ofstream of(output_dbg_path); diff --git a/tests/test_parallelwellinfo.cpp b/tests/test_parallelwellinfo.cpp index f7010f0ab45..db02ff9e881 100644 --- a/tests/test_parallelwellinfo.cpp +++ b/tests/test_parallelwellinfo.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #define BOOST_TEST_MODULE ParallelWellInfo #include