diff --git a/CMakeLists.txt b/CMakeLists.txt index cfca587b79c..5943f42df05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -685,6 +685,7 @@ macro(opm-simulators_targets_hook) target_sources(test_outputdir PRIVATE $) target_sources(test_equil PRIVATE $) + target_sources(test_grid_from_file PRIVATE $) target_sources(test_group_higher_constraints PRIVATE $) target_sources(test_injection_topup_phase_validation PRIVATE $) target_sources(test_RestartSerialization PRIVATE $) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 8ff3122806d..b160f88a97b 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -507,6 +507,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_OilSatfuncConsistencyChecks.cpp tests/test_outputdir.cpp tests/test_parametersystem.cpp + tests/test_grid_from_file.cpp tests/test_parallel_wbp_sourcevalues.cpp tests/test_parallelwellinfo.cpp tests/test_partitionCells.cpp diff --git a/opm/simulators/flow/CpGridVanguard.hpp b/opm/simulators/flow/CpGridVanguard.hpp index b67bd51d293..637c5206fae 100644 --- a/opm/simulators/flow/CpGridVanguard.hpp +++ b/opm/simulators/flow/CpGridVanguard.hpp @@ -308,7 +308,25 @@ class CpGridVanguard : public FlowBaseVanguard std::function(int)> cellCentroids() const { - return this->cellCentroids_(this->cartesianIndexMapper(), true); + if (!cellCentroidsFromGrid_) { + return this->cellCentroids_(this->cartesianIndexMapper(), true); + } else { + // Use centroids from the grid file if available + auto centroidIter = this->grid().beginCellCentroids(); + int num_cells = this->gridView().size(0); + return [centroidIter, num_cells](int i) -> std::array { + if (i < 0 || i >= num_cells) return std::array{}; + auto it = centroidIter; + std::advance(it, i); + const auto& centroid = *it; + return {centroid[0], centroid[1], centroid[2]}; + }; + } + } + + bool gridFromFile() const + { + return cellCentroidsFromGrid_; } const std::vector& globalCell() @@ -319,6 +337,11 @@ class CpGridVanguard : public FlowBaseVanguard protected: void createGrids_() { + // Check if grid will be loaded from file + const auto gridFileName = Parameters::Get(); + if (!gridFileName.empty()) { + cellCentroidsFromGrid_ = true; + } this->doCreateGrids_(this->edgeConformal(), this->eclState()); } @@ -333,7 +356,8 @@ class CpGridVanguard : public FlowBaseVanguard getPropValue() == EnergyModules::FullyImplicitThermal || getPropValue() == EnergyModules::SequentialImplicitThermal, getPropValue(), - getPropValue())); + getPropValue(), + cellCentroidsFromGrid_)); globalTrans_->update(false, TransmissibilityType::TransUpdateQuantities::Trans); } @@ -364,6 +388,9 @@ class CpGridVanguard : public FlowBaseVanguard // diffusivity_ abd dispersivity_. The main reason is to reduce the memory usage for rank 0 // during parallel running. std::unique_ptr globalTrans_; + + // Flag to indicate if cell centroids should be read from the grid file + bool cellCentroidsFromGrid_ = false; }; } // namespace Opm diff --git a/opm/simulators/flow/EclGenericWriter_impl.hpp b/opm/simulators/flow/EclGenericWriter_impl.hpp index dc178d77acb..170cce09aec 100644 --- a/opm/simulators/flow/EclGenericWriter_impl.hpp +++ b/opm/simulators/flow/EclGenericWriter_impl.hpp @@ -49,7 +49,10 @@ #include #include +#include + #include +#include #if HAVE_MPI #include @@ -246,10 +249,22 @@ EclGenericWriter(const Schedule& schedule, outputNnc_.resize(1); if (this->collectOnIORank_.isIORank()) { - this->eclIO_ = std::make_unique - (this->eclState_, - UgGridHelpers::createEclipseGrid(*equilGrid, eclState_.getInputGrid()), - this->schedule_, summaryConfig, "", enableEsmry); + // If an external unstructured grid is provided, use input grid as-is. + // Otherwise, build an EclipseGrid that reflects the simulation grid. + const auto gridFileName = Parameters::Get(); + if (!gridFileName.empty()) { + this->eclIO_ = std::make_unique + (this->eclState_, + eclState_.getInputGrid(), + this->schedule_, summaryConfig, "", enableEsmry); + } + else { + assert(equilGrid != nullptr); + this->eclIO_ = std::make_unique + (this->eclState_, + UgGridHelpers::createEclipseGrid(*equilGrid, eclState_.getInputGrid()), + this->schedule_, summaryConfig, "", enableEsmry); + } } // create output thread if enabled and rank is I/O rank diff --git a/opm/simulators/flow/FlowGenericVanguard.cpp b/opm/simulators/flow/FlowGenericVanguard.cpp index 3b65d7e8974..2bc43668f91 100644 --- a/opm/simulators/flow/FlowGenericVanguard.cpp +++ b/opm/simulators/flow/FlowGenericVanguard.cpp @@ -544,8 +544,9 @@ void FlowGenericVanguard::registerParameters_() // register here for the use in the tests without BlackoilModelParameters Parameters::Register ("Use the well model for multi-segment wells instead of the one for single-segment wells"); + Parameters::Register + ("Filename for unstructured grid input. If empty, the grid will be constructed from the ECL deck."); } - template void FlowGenericVanguard::registerParameters_(); #if FLOW_INSTANTIATE_FLOAT diff --git a/opm/simulators/flow/FlowGenericVanguard.hpp b/opm/simulators/flow/FlowGenericVanguard.hpp index 3c13587e6ce..1f5dc3cd505 100644 --- a/opm/simulators/flow/FlowGenericVanguard.hpp +++ b/opm/simulators/flow/FlowGenericVanguard.hpp @@ -88,7 +88,7 @@ struct ZoltanImbalanceTol { static constexpr Scalar value = 1.1; }; struct ZoltanPhgEdgeSizeThreshold { static constexpr auto value = 0.35; }; struct ZoltanParams { static constexpr auto value = "graph"; }; - +struct UnstructuredGridFileName { static constexpr auto* value = ""; }; } // namespace Opm::Parameters namespace Opm { @@ -145,6 +145,11 @@ class FlowGenericVanguard { */ static std::string canonicalDeckPath(const std::string& caseName); + bool gridFromFile() const + { + return false; + } + /*! * \brief Returns the wall time required to set up the simulator before it was born. */ diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index 4ba0a4e662c..900d7e2e843 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -231,7 +231,8 @@ class FlowProblem : public GetPropType (energyModuleType == EnergyModules::FullyImplicitThermal || energyModuleType == EnergyModules::SequentialImplicitThermal), enableDiffusion, - enableDispersion) + enableDispersion, + simulator.vanguard().gridFromFile()) , wellModel_(simulator, this->iterationContext()) , aquiferModel_(simulator) , pffDofData_(simulator.gridView(), this->elementMapper()) diff --git a/opm/simulators/flow/GenericCpGridVanguard.cpp b/opm/simulators/flow/GenericCpGridVanguard.cpp index a6c3589c981..521e2f76ec0 100644 --- a/opm/simulators/flow/GenericCpGridVanguard.cpp +++ b/opm/simulators/flow/GenericCpGridVanguard.cpp @@ -40,6 +40,8 @@ #include #include +#include + #include #include #include @@ -465,46 +467,107 @@ doCreateGrids_(const bool edge_conformal, EclipseState& eclState) OpmLog::info("\nProcessing grid"); } + const auto gridFileName = Parameters::Get(); + // --- Create grid --- OPM_TIMEBLOCK(createGrids); + std::vector removed_cells; + if (gridFileName.empty()) { #if HAVE_MPI - this->grid_ = std::make_unique(FlowGenericVanguard::comm()); + this->grid_ = std::make_unique(FlowGenericVanguard::comm()); #else - this->grid_ = std::make_unique(); + this->grid_ = std::make_unique(); #endif - // Note: removed_cells is guaranteed to be empty on ranks other than 0. - auto removed_cells = this->grid_ - ->processEclipseFormat(input_grid, - &eclState, - /* isPeriodic = */ false, - /* flipNormals = */ false, - /* clipZ = */ false, - edge_conformal); + // Note: removed_cells is guaranteed to be empty on ranks other than 0. + removed_cells = this->grid_ + ->processEclipseFormat(input_grid, + &eclState, + /* isPeriodic = */ false, + /* flipNormals = */ false, + /* clipZ = */ false, + edge_conformal); + + if (isRoot) { + const auto& active_porv = eclState.fieldProps().porv(false); + const auto& unit_system = eclState.getUnits(); + const auto& volume_unit = unit_system.name(UnitSystem::measure::volume); + double total_pore_volume = unit_system.from_si(UnitSystem::measure::volume, + std::accumulate(active_porv.begin(), active_porv.end(), 0.0)); + OpmLog::info(fmt::format("Total number of active cells: {} / total pore volume: {:0.0f} {}", + grid_->numCells(), total_pore_volume , volume_unit)); + + double removed_pore_volume = + std::accumulate(removed_cells.begin(), removed_cells.end(), 0.0, + [&active_porv, &eclState](const auto acc, const auto global_index) + { return acc + active_porv[eclState.getInputGrid().activeIndex(global_index)]; }); + + if (removed_pore_volume > 0) { + removed_pore_volume = unit_system.from_si(UnitSystem::measure::volume, removed_pore_volume); + OpmLog::info(fmt::format("Removed {} cells with a pore volume of {:0.0f} " + "{} ({:5.3f} %) due to MINPV/MINPVV", + removed_cells.size(), + removed_pore_volume, + volume_unit, + 100 * removed_pore_volume / total_pore_volume)); + } + } + } + else { +#if HAVE_MPI + if (FlowGenericVanguard::comm().size() > 1) { + OpmLog::info(fmt::format("Using unstructured grid from file (MPI): {}", gridFileName)); + this->grid_ = std::make_unique(FlowGenericVanguard::comm()); + this->grid_->readUnstructuredGridFile(gridFileName); + + if (isRoot) { + if (!input_grid->allActive()) { + OPM_THROW(std::runtime_error, + fmt::format("Cannot use unstructured grid file '{}': the Eclipse input grid " + "contains inactive cells ({} active out of {} total). " + "Unstructured grid files require all cells to be active.", + gridFileName, + input_grid->getNumActive(), + input_grid->getCartesianSize())); + } - if (isRoot) { - const auto& active_porv = eclState.fieldProps().porv(false); - const auto& unit_system = eclState.getUnits(); - const auto& volume_unit = unit_system.name(UnitSystem::measure::volume); - double total_pore_volume = unit_system.from_si(UnitSystem::measure::volume, - std::accumulate(active_porv.begin(), active_porv.end(), 0.0)); - OpmLog::info(fmt::format("Total number of active cells: {} / total pore volume: {:0.0f} {}", - grid_->numCells(), total_pore_volume , volume_unit)); - - double removed_pore_volume = - std::accumulate(removed_cells.begin(), removed_cells.end(), 0.0, - [&active_porv, &eclState](const auto acc, const auto global_index) - { return acc + active_porv[eclState.getInputGrid().activeIndex(global_index)]; }); - - if (removed_pore_volume > 0) { - removed_pore_volume = unit_system.from_si(UnitSystem::measure::volume, removed_pore_volume); - OpmLog::info(fmt::format("Removed {} cells with a pore volume of {:0.0f} " - "{} ({:5.3f} %) due to MINPV/MINPVV", - removed_cells.size(), - removed_pore_volume, - volume_unit, - 100 * removed_pore_volume / total_pore_volume)); + const int numCellsFile = this->grid_->numCells(); + const int numCellsEcl = static_cast(input_grid->getNumActive()); + if (numCellsFile != numCellsEcl) { + OPM_THROW(std::runtime_error, + fmt::format("Cell count mismatch: unstructured grid file '{}' has {} cells, " + "but the Eclipse input grid has {} active cells.", + gridFileName, numCellsFile, numCellsEcl)); + } + } + } + else +#endif + { + OpmLog::info(fmt::format("Using unstructured grid from file: {}", gridFileName)); + this->grid_ = std::make_unique(gridFileName); + + if (isRoot) { + if (!input_grid->allActive()) { + OPM_THROW(std::runtime_error, + fmt::format("Cannot use unstructured grid file '{}': the Eclipse input grid " + "contains inactive cells ({} active out of {} total). " + "Unstructured grid files require all cells to be active.", + gridFileName, + input_grid->getNumActive(), + input_grid->getCartesianSize())); + } + + const int numCellsFile = this->grid_->numCells(); + const int numCellsEcl = static_cast(input_grid->getNumActive()); + if (numCellsFile != numCellsEcl) { + OPM_THROW(std::runtime_error, + fmt::format("Cell count mismatch: unstructured grid file '{}' has {} cells, " + "but the Eclipse input grid has {} active cells.", + gridFileName, numCellsFile, numCellsEcl)); + } + } } } diff --git a/opm/simulators/flow/PolyhedralGridVanguard.hpp b/opm/simulators/flow/PolyhedralGridVanguard.hpp index e2390e76999..bfc57419ea8 100644 --- a/opm/simulators/flow/PolyhedralGridVanguard.hpp +++ b/opm/simulators/flow/PolyhedralGridVanguard.hpp @@ -35,6 +35,10 @@ #include #include +#include + +#include + #include #include #include @@ -113,11 +117,17 @@ class PolyhedralGridVanguard : public FlowBaseVanguard { this->callImplementationInit(); // add a copy in standard vector format to fullfill new interface - const int* globalcellorg = this->grid().globalCell(); + const int* globalcellorg = this->grid().globalCellPtr(); int num_cells = this->gridView().size(0); globalcell_.resize(num_cells); for(int i=0; i < num_cells; ++i){ - globalcell_[i] = globalcellorg[i]; + // For grids without global cell numbering, globalcellorg is nullptr + // and we just use the local cell index as global cell index. + if (globalcellorg) { + globalcell_[i] = globalcellorg[i]; + } else { + globalcell_[i] = i; + } } } @@ -197,6 +207,11 @@ class PolyhedralGridVanguard : public FlowBaseVanguard return globalcell_; } + bool gridFromFile() const + { + return cellCentroidsFromGrid_; + } + unsigned int gridEquilIdxToGridIdx(unsigned int elemIndex) const { return elemIndex; } @@ -232,7 +247,18 @@ class PolyhedralGridVanguard : public FlowBaseVanguard std::function::dimensionworld>(int)> cellCentroids() const { - return this->cellCentroids_(this->cartesianIndexMapper(), false); + if (!cellCentroidsFromGrid_) { + return this->cellCentroids_(this->cartesianIndexMapper(), false); + } else { + using UnstructuredGridType = Grid::UnstructuredGridType; + UnstructuredGridType ugPtr( *this->grid_ ); + double* centroids = ugPtr.cell_centroids; + int num_cells = this->grid_->size(0); + return [centroids, num_cells](int i) -> std::array::dimensionworld> { + if (i < 0 || i >= num_cells) return std::array::dimensionworld>{}; + return {centroids[i * 3], centroids[i * 3 + 1], centroids[i * 3 + 2]}; + }; + } } std::vector cellPartition() const @@ -244,11 +270,36 @@ class PolyhedralGridVanguard : public FlowBaseVanguard protected: void createGrids_() { - this->grid_ = std::make_unique - (this->eclState().getInputGrid(), - this->eclState().fieldProps().porv(true), - this->edgeConformal()); - + // Read the grid from file if specified, otherwise use the grid from the ECL file. + std::string gridFileName = Parameters::Get(); + if (gridFileName.empty()) { + this->grid_ = std::make_unique + (this->eclState().getInputGrid(), + this->eclState().fieldProps().porv(true), + this->edgeConformal()); + } else { + const auto& input_grid = this->eclState().getInputGrid(); + if (!input_grid.allActive()) { + OPM_THROW(std::runtime_error, + fmt::format("Cannot use unstructured grid file '{}': the Eclipse input grid " + "contains inactive cells ({} active out of {} total). " + "Unstructured grid files require all cells to be active.", + gridFileName, + input_grid.getNumActive(), + input_grid.getCartesianSize())); + } + std::cout << "Using unstructured grid from file: " << gridFileName << std::endl; + this->grid_ = std::make_unique(gridFileName.empty() ? "test.txt" : gridFileName); + cellCentroidsFromGrid_ = true; + const int numCellsFile = static_cast(this->grid_->size(0)); + const int numCellsEcl = static_cast(input_grid.getNumActive()); + if (numCellsFile != numCellsEcl) { + OPM_THROW(std::runtime_error, + fmt::format("Cell count mismatch: unstructured grid file '{}' has {} cells, " + "but the Eclipse input grid has {} active cells.", + gridFileName, numCellsFile, numCellsEcl)); + } + } this->cartesianIndexMapper_ = std::make_unique(*this->grid_); @@ -270,6 +321,7 @@ class PolyhedralGridVanguard : public FlowBaseVanguard std::unordered_set defunctWellNames_; std::vector globalcell_; + bool cellCentroidsFromGrid_ = false; }; } // namespace Opm diff --git a/opm/simulators/flow/Transmissibility.hpp b/opm/simulators/flow/Transmissibility.hpp index 9bc3d6c62bc..9df3c348351 100644 --- a/opm/simulators/flow/Transmissibility.hpp +++ b/opm/simulators/flow/Transmissibility.hpp @@ -66,7 +66,8 @@ class Transmissibility { std::function(int)> centroids, bool enableEnergy, bool enableDiffusivity, - bool enableDispersivity); + bool enableDispersivity, + bool gridFromFile = false); /*! * \brief Return the permeability for an element. @@ -294,6 +295,7 @@ class Transmissibility { bool enableEnergy_; bool enableDiffusivity_; bool enableDispersivity_; + bool gridFromFile_; bool warnEditNNC_ = true; std::unordered_map thermalHalfTrans_; //NB this is based on direction map size is ca 2*trans_ (diffusivity_) std::unordered_map diffusivity_; diff --git a/opm/simulators/flow/Transmissibility_impl.hpp b/opm/simulators/flow/Transmissibility_impl.hpp index c268d2eed91..4fd18a3a295 100644 --- a/opm/simulators/flow/Transmissibility_impl.hpp +++ b/opm/simulators/flow/Transmissibility_impl.hpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -101,7 +102,8 @@ Transmissibility(const EclipseState& eclState, std::function(int)> centroids, bool enableEnergy, bool enableDiffusivity, - bool enableDispersivity) + bool enableDispersivity, + bool gridFromFile) : eclState_(eclState) , gridView_(gridView) , cartMapper_(cartMapper) @@ -110,6 +112,7 @@ Transmissibility(const EclipseState& eclState, , enableEnergy_(enableEnergy) , enableDiffusivity_(enableDiffusivity) , enableDispersivity_(enableDispersivity) + , gridFromFile_(gridFromFile) , lookUpData_(gridView) , lookUpCartesianData_(gridView, cartMapper) { @@ -457,9 +460,7 @@ update(bool global, const TransUpdateQuantities update_quantities, Scalar trans = computeHalfMean(computeHalfTrans_, permeability_); - // apply the full face transmissibility multipliers - // for the inside ... - if (!pinchActive) { + if (!gridFromFile_ && !pinchActive) { if (inside.faceIdx > 3) { // top or bottom auto find_layer = [&cartDims](std::size_t cell) { cell /= cartDims[0]; diff --git a/regressionTests.cmake b/regressionTests.cmake index c64549533e7..7ccad9f911b 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -788,6 +788,25 @@ add_test_compareECLFiles( model1 ) +add_test_compareECLFiles( + CASENAME + base_model_1_gridfile + FILENAME + BASE_MODEL_1_GRIDFILE + SIMULATOR + flow + DEV_SIMULATOR + flow_blackoil + ABS_TOL + ${abs_tol} + REL_TOL + ${rel_tol} + DIR + model1 + TEST_ARGS + --unstructured-grid-file-name=${OPM_TESTS_ROOT}/model1/base.grid +) + add_test_compareECLFiles( CASENAME faults_model_1 diff --git a/tests/SimulatorFixture.hpp b/tests/SimulatorFixture.hpp index 54d60bc5a5b..20275b646d9 100644 --- a/tests/SimulatorFixture.hpp +++ b/tests/SimulatorFixture.hpp @@ -54,6 +54,7 @@ #include #include +#include namespace Opm { @@ -90,17 +91,22 @@ template std::unique_ptr> initSimulator(const char* filename, const char* test_name = "test_simulator", - int threads_per_process = 1) + int threads_per_process = 1, + const std::vector& extra_args = {}) { using Simulator = GetPropType; - std::string filename_arg = "--ecl-deck-file-name="; - filename_arg += filename; + std::vector argv_storage; + argv_storage.reserve(extra_args.size() + 2); + argv_storage.emplace_back(test_name); + argv_storage.emplace_back(std::string{"--ecl-deck-file-name="} + filename); + argv_storage.insert(argv_storage.end(), extra_args.begin(), extra_args.end()); - const char* argv[] = { - test_name, - filename_arg.c_str() - }; + std::vector argv; + argv.reserve(argv_storage.size()); + for (const auto& arg : argv_storage) { + argv.push_back(arg.c_str()); + } Parameters::reset(); registerAllParameters_(false); @@ -109,8 +115,8 @@ initSimulator(const char* filename, Parameters::Register("Do *NOT* use!"); Parameters::SetDefault(threads_per_process); Parameters::endRegistration(); - setupParameters_(/*argc=*/sizeof(argv) / sizeof(argv[0]), - argv, + setupParameters_(/*argc=*/static_cast(argv.size()), + argv.data(), /*registerParams=*/false, /*allowUnused=*/false, /*handleHelp=*/true, diff --git a/tests/test_grid_from_file.cpp b/tests/test_grid_from_file.cpp new file mode 100644 index 00000000000..ca7231bc2e8 --- /dev/null +++ b/tests/test_grid_from_file.cpp @@ -0,0 +1,342 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: + +#include "config.h" + +#define BOOST_TEST_MODULE GridFromFileSimulator + +#include + +#include "SimulatorFixture.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +// These templates are not explicitly instantiated in the library for polyhedral grids. +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Opm::Properties { + +namespace TTag { + +struct TestPolyhedralTypeTag { + using InheritsFrom = std::tuple; +}; + +} // namespace TTag + +template +struct Linearizer +{ + using type = TpfaLinearizer; +}; + +template +struct LocalResidual +{ + using type = BlackOilLocalResidualTPFA; +}; + +template +struct EnableDiffusion +{ + static constexpr bool value = false; +}; + +template +struct Grid +{ + using type = Dune::PolyhedralGrid<3, 3>; +}; + +template +struct EquilGrid +{ + using type = GetPropType; +}; + +template +struct Vanguard +{ + using type = Opm::PolyhedralGridVanguard; +}; + +} // namespace Opm::Properties + +namespace Opm { + +template<> +class SupportsFaceTag> + : public std::bool_constant +{}; + +} // namespace Opm + +namespace { + +class ScopedFile +{ +public: + explicit ScopedFile(std::string path) + : path_(std::move(path)) + {} + + ~ScopedFile() + { + std::remove(path_.c_str()); + } + + const std::string& path() const + { + return path_; + } + +private: + std::string path_; +}; + +template +void writeValues(std::ostream& os, const T* values, std::size_t count) +{ + for (std::size_t i = 0; i < count; ++i) { + if (i > 0) { + os << ' '; + } + os << values[i]; + } + os << '\n'; +} + +void writeTrivialDeckFile(const std::string& deckFileName, int numCells) +{ + std::ofstream deck(deckFileName); + BOOST_REQUIRE(deck); + + deck << "RUNSPEC\n\n" + << "WATER\n" + << "GAS\n" + << "OIL\n\n" + << "METRIC\n\n" + << "DIMENS\n" + << " " << numCells << " 1 1 /\n\n" + << "GRID\n\n" + << "DX\n" + << " " << numCells << "*1 /\n" + << "DY\n" + << " " << numCells << "*1 /\n" + << "DZ\n" + << " " << numCells << "*1 /\n\n" + << "TOPS\n" + << " " << numCells << "*0 /\n\n" + << "PORO\n" + << " " << numCells << "*0.3 /\n\n" + << "PERMX\n" + << " " << numCells << "*500 /\n\n" + << "PROPS\n\n" + << "PVTW\n" + << " 4017.55 1.038 3.22E-6 0.318 0.0 /\n\n" + << "ROCK\n" + << " 14.7 3E-6 /\n\n" + << "SWOF\n" + << "0.12 0 1 0\n" + << "0.30 0.1 0.9 0\n" + << "1.00 1 0 0 /\n\n" + << "SGOF\n" + << "0.00 0 1 0\n" + << "0.30 0.1 0.9 0\n" + << "0.88 1 0 0 /\n\n" + << "DENSITY\n" + << " 53.66 64.49 0.0533 /\n\n" + << "PVDG\n" + << "14.700 166.666 0.008000\n" + << "9014.7 0.38600 0.047000 /\n\n" + << "PVTO\n" + << "0.0010 14.7 1.0620 1.0400 /\n" + << "1.2700 4014.7 1.6950 0.5100\n" + << " 9014.7 1.5790 0.7400 /\n" + << "/\n\n" + << "SOLUTION\n\n" + << "SWAT\n" + << " " << numCells << "*0.12 /\n\n" + << "SGAS\n" + << " " << numCells << "*0 /\n\n" + << "PRESSURE\n" + << " " << numCells << "*300 /\n\n" + << "SUMMARY\n\n" + << "SCHEDULE\n\n" + << "TSTEP\n" + << "1 /\n"; +} + +void writeCartesianUnstructuredGridFile(const std::string& gridFileName, int numCells) +{ + std::unique_ptr ugPtr(create_grid_cart3d(numCells, 1, 1), &destroy_grid); + BOOST_REQUIRE(ugPtr); + + const auto& grid = *ugPtr; + + std::ofstream gridFile(gridFileName); + BOOST_REQUIRE(gridFile); + + const auto numFaceNodes = static_cast(grid.face_nodepos[grid.number_of_faces]); + const auto numCellFaces = static_cast(grid.cell_facepos[grid.number_of_cells]); + const bool hasFaceTags = grid.cell_facetag != nullptr; + const bool hasIndexMap = grid.global_cell != nullptr; + + gridFile << grid.dimensions << ' ' + << grid.number_of_cells << ' ' + << grid.number_of_faces << ' ' + << grid.number_of_nodes << ' ' + << numFaceNodes << ' ' + << numCellFaces << ' ' + << hasFaceTags << ' ' << hasIndexMap << '\n'; + + writeValues(gridFile, grid.cartdims, static_cast(grid.dimensions)); + writeValues(gridFile, grid.node_coordinates, static_cast(grid.dimensions * grid.number_of_nodes)); + writeValues(gridFile, grid.face_nodepos, static_cast(grid.number_of_faces + 1)); + writeValues(gridFile, grid.face_nodes, numFaceNodes); + writeValues(gridFile, grid.face_cells, static_cast(2 * grid.number_of_faces)); + writeValues(gridFile, grid.face_areas, static_cast(grid.number_of_faces)); + writeValues(gridFile, grid.face_centroids, static_cast(grid.dimensions * grid.number_of_faces)); + writeValues(gridFile, grid.face_normals, static_cast(grid.dimensions * grid.number_of_faces)); + writeValues(gridFile, grid.cell_facepos, static_cast(grid.number_of_cells + 1)); + + if (hasFaceTags) { + for (std::size_t i = 0; i < numCellFaces; ++i) { + if (i > 0) { + gridFile << ' '; + } + gridFile << grid.cell_faces[i] << ' ' << grid.cell_facetag[i]; + } + gridFile << '\n'; + } + else { + writeValues(gridFile, grid.cell_faces, numCellFaces); + } + + if (hasIndexMap) { + writeValues(gridFile, grid.global_cell, static_cast(grid.number_of_cells)); + } + + writeValues(gridFile, grid.cell_volumes, static_cast(grid.number_of_cells)); + writeValues(gridFile, grid.cell_centroids, static_cast(grid.dimensions * grid.number_of_cells)); +} + +} // namespace + +using SimulatorFixture = Opm::SimulatorFixture; +BOOST_GLOBAL_FIXTURE(SimulatorFixture); + +BOOST_AUTO_TEST_CASE(init_polyhedral_simulator_from_unstructured_grid_file) +{ + using TypeTag = Opm::Properties::TTag::TestPolyhedralTypeTag; + + constexpr int numCells = 3; + const ScopedFile deckFile{"test_polyhedral_unstructured.DATA"}; + const ScopedFile gridFile{"test_polyhedral_unstructured.grid"}; + + writeTrivialDeckFile(deckFile.path(), numCells); + writeCartesianUnstructuredGridFile(gridFile.path(), numCells); + + auto simulator = Opm::initSimulator(deckFile.path().c_str(), + "test_polyhedral_unstructured", + /*threads_per_process=*/1, + {std::string{"--unstructured-grid-file-name="} + gridFile.path()}); + BOOST_REQUIRE(simulator); + + const auto& grid = simulator->vanguard().grid(); + const auto& eclGrid = simulator->vanguard().eclState().getInputGrid(); + + BOOST_CHECK_EQUAL(eclGrid.getNumActive(), numCells); + BOOST_CHECK_EQUAL(static_cast(grid.size(0)), eclGrid.getNumActive()); + BOOST_CHECK_EQUAL(simulator->vanguard().globalCell().size(), grid.size(0)); +} + +BOOST_AUTO_TEST_CASE(init_cpgrid_simulator_from_unstructured_grid_file) +{ + using TypeTag = Opm::Properties::TTag::TestTypeTag; + + constexpr int numCells = 3; + const ScopedFile deckFile{"test_cpgrid_unstructured.DATA"}; + const ScopedFile gridFile{"test_cpgrid_unstructured.grid"}; + + writeTrivialDeckFile(deckFile.path(), numCells); + writeCartesianUnstructuredGridFile(gridFile.path(), numCells); + + auto simulator = Opm::initSimulator(deckFile.path().c_str(), + "test_cpgrid_unstructured", + /*threads_per_process=*/1, + {std::string{"--unstructured-grid-file-name="} + gridFile.path()}); + BOOST_REQUIRE(simulator); + + const auto& grid = simulator->vanguard().grid(); + const auto& gridView = simulator->vanguard().gridView(); + const auto& eclGrid = simulator->vanguard().eclState().getInputGrid(); + + BOOST_CHECK_EQUAL(eclGrid.getNumActive(), numCells); + BOOST_CHECK_EQUAL(grid.numCells(), numCells); + BOOST_CHECK_EQUAL(static_cast(grid.numCells()), gridView.size(0)); + BOOST_CHECK_EQUAL(grid.globalCell().size(), static_cast(grid.numCells())); +} + +BOOST_AUTO_TEST_CASE(init_polyhedral_simulator_from_deck) +{ + using TypeTag = Opm::Properties::TTag::TestPolyhedralTypeTag; + using Grid = Opm::GetPropType; + using Vanguard = Opm::GetPropType; + + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + + auto simulator = Opm::initSimulator("equil_base.DATA", "test_polyhedral"); + BOOST_REQUIRE(simulator); + + const auto& grid = simulator->vanguard().grid(); + const auto& gridView = simulator->vanguard().gridView(); + const auto& eclGrid = simulator->vanguard().eclState().getInputGrid(); + + BOOST_CHECK_EQUAL(static_cast(grid.size(0)), eclGrid.getNumActive()); + BOOST_CHECK_GT(gridView.size(0), 0u); + BOOST_CHECK_GT(gridView.size(1), 0u); + BOOST_CHECK_GT(gridView.size(3), 0u); + BOOST_CHECK_EQUAL(simulator->vanguard().globalCell().size(), gridView.size(0)); +} + +BOOST_AUTO_TEST_CASE(init_cpgrid_simulator_from_deck) +{ + using TypeTag = Opm::Properties::TTag::TestTypeTag; + + auto simulator = Opm::initSimulator("equil_base.DATA", "test_cpgrid"); + BOOST_REQUIRE(simulator); + + const auto& grid = simulator->vanguard().grid(); + const auto& gridView = simulator->vanguard().gridView(); + const auto& eclGrid = simulator->vanguard().eclState().getInputGrid(); + + BOOST_CHECK_EQUAL(static_cast(grid.numCells()), eclGrid.getNumActive()); + BOOST_CHECK_GT(gridView.size(0), 0u); + BOOST_CHECK_EQUAL(grid.globalCell().size(), gridView.size(0)); +}