void restoreTemperatureData(const ecl_file_type* file, EclipseStateConstPtr eclipse_state, int numcells, SimulatorState& simulator_state) { const char* temperature = "TEMP"; if (ecl_file_has_kw(file , temperature)) { ecl_kw_type* temperature_kw = ecl_file_iget_named_kw(file, temperature, 0); if (ecl_kw_get_size(temperature_kw) != numcells) { throw std::runtime_error("Read of restart file: Could not restore temperature data, length of data from file not equal number of cells"); } float* temperature_data = ecl_kw_get_float_ptr(temperature_kw); // factor and offset from the temperature values given in the deck to Kelvin double scaling = eclipse_state->getDeckUnitSystem().parse("Temperature")->getSIScaling(); double offset = eclipse_state->getDeckUnitSystem().parse("Temperature")->getSIOffset(); for (size_t index = 0; index < simulator_state.temperature().size(); ++index) { simulator_state.temperature()[index] = unit::convert::from((double)temperature_data[index] - offset, scaling); } } else { throw std::runtime_error("Read of restart file: File does not contain TEMP data\n"); } }
/// \brief Get a vector of pressure thresholds from either EclipseState /// or maxDp (for defaulted values) for all Non-neighbour connections (NNCs). /// \param[in] nnc The NNCs, /// \param[in] eclipseState Processed eclipse state, EQLNUM is accessed from it. /// \param[in] maxDp The maximum gravity corrected pressure differences between /// the equilibration regions. /// \return A vector of pressure thresholds, one /// for each NNC in the grid. A value /// of zero means no threshold for that /// particular connection. An empty vector is /// returned if there is no THPRES /// feature used in the deck. std::vector<double> thresholdPressuresNNC(EclipseStateConstPtr eclipseState, const NNC& nnc, const std::map<std::pair<int, int>, double>& maxDp) { const SimulationConfig& simulationConfig = eclipseState->getSimulationConfig(); std::vector<double> thpres_vals; if (simulationConfig.hasThresholdPressure()) { std::shared_ptr<const ThresholdPressure> thresholdPressure = simulationConfig.getThresholdPressure(); const auto& eqlnum = eclipseState->get3DProperties().getIntGridProperty("EQLNUM"); const auto& eqlnumData = eqlnum.getData(); // Set values for each NNC thpres_vals.resize(nnc.numNNC(), 0.0); for (size_t i = 0 ; i < nnc.numNNC(); ++i) { const int gc1 = nnc.nncdata()[i].cell1; const int gc2 = nnc.nncdata()[i].cell2; const int eq1 = eqlnumData[gc1]; const int eq2 = eqlnumData[gc2]; if (thresholdPressure->hasRegionBarrier(eq1,eq2)) { if (thresholdPressure->hasThresholdPressure(eq1,eq2)) { thpres_vals[i] = thresholdPressure->getThresholdPressure(eq1,eq2); } else { // set the threshold pressure for NNC of PVT regions where the third item // has been defaulted to the maximum pressure potential difference between // these regions const auto barrierId = std::make_pair(eq1, eq2); thpres_vals[i] = maxDp.at(barrierId); } } } } return thpres_vals; }
std::vector<double> thresholdPressures(const DeckConstPtr& /* deck */, EclipseStateConstPtr eclipseState, const Grid& grid, const std::map<std::pair<int, int>, double>& maxDp) { const SimulationConfig& simulationConfig = eclipseState->getSimulationConfig(); std::vector<double> thpres_vals; if (simulationConfig.hasThresholdPressure()) { std::shared_ptr<const ThresholdPressure> thresholdPressure = simulationConfig.getThresholdPressure(); const auto& eqlnum = eclipseState->get3DProperties().getIntGridProperty("EQLNUM"); const auto& eqlnumData = eqlnum.getData(); // Set threshold pressure values for each cell face. const int num_faces = UgGridHelpers::numFaces(grid); const auto& fc = UgGridHelpers::faceCells(grid); const int* gc = UgGridHelpers::globalCell(grid); thpres_vals.resize(num_faces, 0.0); for (int face = 0; face < num_faces; ++face) { const int c1 = fc(face, 0); const int c2 = fc(face, 1); if (c1 < 0 || c2 < 0) { // Boundary face, skip it. continue; } const int gc1 = (gc == 0) ? c1 : gc[c1]; const int gc2 = (gc == 0) ? c2 : gc[c2]; const int eq1 = eqlnumData[gc1]; const int eq2 = eqlnumData[gc2]; if (thresholdPressure->hasRegionBarrier(eq1,eq2)) { if (thresholdPressure->hasThresholdPressure(eq1,eq2)) { thpres_vals[face] = thresholdPressure->getThresholdPressure(eq1,eq2); } else { // set the threshold pressure for faces of PVT regions where the third item // has been defaulted to the maximum pressure potential difference between // these regions const auto barrierId = std::make_pair(std::min(eq1, eq2), std::max(eq1, eq2)); if (maxDp.count(barrierId) > 0) thpres_vals[face] = maxDp.at(barrierId); else thpres_vals[face] = 0.0; } } } } return thpres_vals; }
void init_from_restart_file(EclipseStateConstPtr eclipse_state, int numcells, const PhaseUsage& phase_usage, SimulatorState& simulator_state, WellState& wellstate) { InitConfigConstPtr initConfig = eclipse_state->getInitConfig(); IOConfigConstPtr ioConfig = eclipse_state->getIOConfig(); int restart_step = initConfig->getRestartStep(); const std::string& restart_file_root = initConfig->getRestartRootName(); bool output = false; const std::string& restart_file_name = ioConfig->getRestartFileName(restart_file_root, restart_step, output); Opm::restoreSOLUTION(restart_file_name, restart_step, ioConfig->getUNIFIN(), eclipse_state, numcells, phase_usage, simulator_state); Opm::restoreOPM_XWELKeyword(restart_file_name, restart_step, ioConfig->getUNIFIN(), wellstate); }
void restoreSOLUTION(const std::string& restart_filename, int reportstep, bool unified, EclipseStateConstPtr eclipseState, int numcells, const PhaseUsage& phaseUsage, SimulatorState& simulator_state) { const char* filename = restart_filename.c_str(); ecl_file_type* file_type = ecl_file_open(filename, 0); if (file_type) { bool block_selected = unified ? ecl_file_select_rstblock_report_step(file_type , reportstep) : true; if (block_selected) { restorePressureData(file_type, eclipseState, numcells, simulator_state); restoreTemperatureData(file_type, eclipseState, numcells, simulator_state); restoreSaturation(file_type, phaseUsage, numcells, simulator_state); BlackoilState* blackoilState = dynamic_cast<BlackoilState*>(&simulator_state); if (blackoilState) { SimulationConfigConstPtr sim_config = eclipseState->getSimulationConfig(); restoreRSandRV(file_type, sim_config, numcells, blackoilState); } } else { std::string error_str = "Restart file " + restart_filename + " does not contain data for report step " + std::to_string(reportstep) + "!\n"; throw std::runtime_error(error_str); } ecl_file_close(file_type); } else { std::string error_str = "Restart file " + restart_filename + " not found!\n"; throw std::runtime_error(error_str); } }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; if (argc <= 1) { usage(); exit(1); } const char* eclipseFilename = argv[1]; EclipseStateConstPtr eclState; ParserPtr parser(new Opm::Parser); Opm::ParseContext parseContext({{ ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE }, { ParseContext::PARSE_UNKNOWN_KEYWORD, InputError::IGNORE}, { ParseContext::PARSE_RANDOM_TEXT, InputError::IGNORE}, { ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER, InputError::IGNORE}, { ParseContext::UNSUPPORTED_COMPORD_TYPE, InputError::IGNORE}, { ParseContext::UNSUPPORTED_INITIAL_THPRES, InputError::IGNORE}, { ParseContext::INTERNAL_ERROR_UNINITIALIZED_THPRES, InputError::IGNORE} }); Opm::DeckConstPtr deck(parser->parseFile(eclipseFilename, parseContext)); eclState.reset(new EclipseState(deck, parseContext)); GridManager gm(deck); const UnstructuredGrid& grid = *gm.c_grid(); using boost::filesystem::path; path fpath(eclipseFilename); std::string baseName; if (boost::to_upper_copy(path(fpath.extension()).string())== ".DATA") { baseName = path(fpath.stem()).string(); } else { baseName = path(fpath.filename()).string(); } std::string logFile = baseName + ".SATFUNCLOG"; Opm::time::StopWatch timer; timer.start(); RelpermDiagnostics diagnostic(logFile); diagnostic.diagnosis(eclState, deck, grid); timer.stop(); double tt = timer.secsSinceStart(); std::cout << "relperm diagnostics: " << tt << " seconds." << std::endl; } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
/*! * \brief Implement the temperature part of the water PVT properties. */ void initFromDeck(DeckConstPtr deck, EclipseStateConstPtr eclState) { ////// // initialize the isothermal part ////// isothermalPvt_ = new IsothermalPvt; isothermalPvt_->initFromDeck(deck, eclState); ////// // initialize the thermal part ////// auto tables = eclState->getTableManager(); enableThermalDensity_ = deck->hasKeyword("WATDENT"); enableThermalViscosity_ = deck->hasKeyword("VISCREF"); unsigned numRegions = isothermalPvt_->numRegions(); setNumRegions(numRegions); if (enableThermalDensity_) { const auto& watdentKeyword = deck->getKeyword("WATDENT"); assert(watdentKeyword.size() == numRegions); for (unsigned regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const auto& watdentRecord = watdentKeyword.getRecord(regionIdx); watdentRefTemp_[regionIdx] = watdentRecord.getItem("REFERENCE_TEMPERATURE").getSIDouble(0); watdentCT1_[regionIdx] = watdentRecord.getItem("EXPANSION_COEFF_LINEAR").getSIDouble(0); watdentCT2_[regionIdx] = watdentRecord.getItem("EXPANSION_COEFF_QUADRATIC").getSIDouble(0); } } if (enableThermalViscosity_) { const auto& viscrefKeyword = deck->getKeyword("VISCREF"); const auto& watvisctTables = tables->getWatvisctTables(); assert(watvisctTables.size() == numRegions); assert(viscrefKeyword.size() == numRegions); for (unsigned regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { const auto& T = watvisctTables[regionIdx].getColumn("Temperature").vectorCopy(); const auto& mu = watvisctTables[regionIdx].getColumn("Viscosity").vectorCopy(); watvisctCurves_[regionIdx].setXYContainers(T, mu); const auto& viscrefRecord = viscrefKeyword.getRecord(regionIdx); viscrefPress_[regionIdx] = viscrefRecord.getItem("REFERENCE_PRESSURE").getSIDouble(0); } } }
/*! * \brief Initialize the parameters for dry gas using an ECL deck. * * This method assumes that the deck features valid DENSITY and PVDG keywords. */ void initFromDeck(DeckConstPtr deck, EclipseStateConstPtr eclState) { const auto& pvdgTables = eclState->getTableManager()->getPvdgTables(); const auto& densityKeyword = deck->getKeyword("DENSITY"); assert(pvdgTables.size() == densityKeyword.size()); size_t numRegions = pvdgTables.size(); setNumRegions(numRegions); for (unsigned regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { Scalar rhoRefO = densityKeyword.getRecord(regionIdx).getItem("OIL").getSIDouble(0); Scalar rhoRefG = densityKeyword.getRecord(regionIdx).getItem("GAS").getSIDouble(0); Scalar rhoRefW = densityKeyword.getRecord(regionIdx).getItem("WATER").getSIDouble(0); setReferenceDensities(regionIdx, rhoRefO, rhoRefG, rhoRefW); // determine the molar masses of the components Scalar p = 1.01325e5; // surface pressure, [Pa] Scalar T = 273.15 + 15.56; // surface temperature, [K] Scalar MO = 175e-3; // [kg/mol] Scalar MG = Opm::Constants<Scalar>::R*T*rhoRefG / p; // [kg/mol], consequence of the ideal gas law Scalar MW = 18.0e-3; // [kg/mol] // TODO (?): the molar mass of the components can possibly specified // explicitly in the deck. setMolarMasses(regionIdx, MO, MG, MW); const auto& pvdgTable = pvdgTables.getTable<PvdgTable>(regionIdx); // say 99.97% of all time: "premature optimization is the root of all // evil". Eclipse does this "optimization" for apparently no good reason! std::vector<Scalar> invB(pvdgTable.numRows()); const auto& Bg = pvdgTable.getFormationFactorColumn(); for (unsigned i = 0; i < Bg.size(); ++ i) { invB[i] = 1.0/Bg[i]; } size_t numSamples = invB.size(); inverseGasB_[regionIdx].setXYArrays(numSamples, pvdgTable.getPressureColumn(), invB); gasMu_[regionIdx].setXYArrays(numSamples, pvdgTable.getPressureColumn(), pvdgTable.getViscosityColumn()); } initEnd(); }
/*! * \brief Implement the temperature part of the gas PVT properties. */ void initFromDeck(DeckConstPtr deck, EclipseStateConstPtr eclState) { ////// // initialize the isothermal part ////// isothermalPvt_ = new IsothermalPvt; isothermalPvt_->initFromDeck(deck, eclState); ////// // initialize the thermal part ////// auto tables = eclState->getTableManager(); enableThermalDensity_ = deck->hasKeyword("TREF"); enableThermalViscosity_ = deck->hasKeyword("GASVISCT"); unsigned numRegions = isothermalPvt_->numRegions(); setNumRegions(numRegions); // viscosity if (enableThermalViscosity_) { const auto& gasvisctTables = tables->getGasvisctTables(); Opm::DeckKeywordConstPtr viscrefKeyword = deck->getKeyword("VISCREF"); int gasCompIdx = deck->getKeyword("GCOMPIDX")->getRecord(0)->getItem("GAS_COMPONENT_INDEX")->getInt(0) - 1; std::string gasvisctColumnName = "Viscosity"+std::to_string(static_cast<long long>(gasCompIdx)); for (unsigned regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const auto& T = gasvisctTables[regionIdx].getColumn("Temperature").vectorCopy(); const auto& mu = gasvisctTables[regionIdx].getColumn(gasvisctColumnName).vectorCopy(); gasvisctCurves_[regionIdx].setXYContainers(T, mu); } } // quantities required for density. note that we just always use the values // for the first EOS. (since EOS != PVT region.) refTemp_ = 0.0; if (enableThermalDensity_) { refTemp_ = deck->getKeyword("TREF")->getRecord(0)->getItem("TEMPERATURE")->getSIDouble(0); } }
void restorePressureData(const ecl_file_type* file, EclipseStateConstPtr eclipse_state, int numcells, SimulatorState& simulator_state) { const char* pressure = "PRESSURE"; if (ecl_file_has_kw(file , pressure)) { ecl_kw_type* pressure_kw = ecl_file_iget_named_kw(file, pressure, 0); if (ecl_kw_get_size(pressure_kw) != numcells) { throw std::runtime_error("Read of restart file: Could not restore pressure data, length of data from file not equal number of cells"); } float* pressure_data = ecl_kw_get_float_ptr(pressure_kw); const double deck_pressure_unit = (eclipse_state->getDeckUnitSystem().getType() == UnitSystem::UNIT_TYPE_METRIC) ? Opm::unit::barsa : Opm::unit::psia; for (size_t index = 0; index < simulator_state.pressure().size(); ++index) { simulator_state.pressure()[index] = unit::convert::from((double)pressure_data[index], deck_pressure_unit); } } else { throw std::runtime_error("Read of restart file: File does not contain PRESSURE data\n"); } }
void RelpermDiagnostics::scaledEndPointsCheck_(DeckConstPtr deck, EclipseStateConstPtr eclState, const GridT& grid) { const int nc = Opm::UgGridHelpers::numCells(grid); const auto& global_cell = Opm::UgGridHelpers::globalCell(grid); const auto dims = Opm::UgGridHelpers::cartDims(grid); const auto& compressedToCartesianIdx = Opm::compressedToCartesian(nc, global_cell); scaledEpsInfo_.resize(nc); EclEpsGridProperties epsGridProperties; epsGridProperties.initFromDeck(deck, eclState, /*imbibition=*/false); const auto& satnum = eclState->get3DProperties().getIntGridProperty("SATNUM"); const std::string tag = "Scaled endpoints"; for (int c = 0; c < nc; ++c) { const int cartIdx = compressedToCartesianIdx[c]; const std::string satnumIdx = std::to_string(satnum.iget(cartIdx)); std::array<int, 3> ijk; ijk[0] = cartIdx % dims[0]; ijk[1] = (cartIdx / dims[0]) % dims[1]; ijk[2] = cartIdx / dims[0] / dims[1]; const std::string cellIdx = "(" + std::to_string(ijk[0]) + ", " + std::to_string(ijk[1]) + ", " + std::to_string(ijk[2]) + ")"; scaledEpsInfo_[c].extractScaled(epsGridProperties, cartIdx); // SGU <= 1.0 - SWL if (scaledEpsInfo_[c].Sgu > (1.0 - scaledEpsInfo_[c].Swl)) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SGU exceed 1.0 - SWL"; OpmLog::warning(tag, msg); } // SGL <= 1.0 - SWU if (scaledEpsInfo_[c].Sgl > (1.0 - scaledEpsInfo_[c].Swu)) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SGL exceed 1.0 - SWU"; OpmLog::warning(tag, msg); } if (deck->hasKeyword("SCALECRS") && fluidSystem_ == FluidSystem::BlackOil) { // Mobilility check. if ((scaledEpsInfo_[c].Sowcr + scaledEpsInfo_[c].Swcr) >= 1.0) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SOWCR + SWCR exceed 1.0"; OpmLog::warning(tag, msg); } if ((scaledEpsInfo_[c].Sogcr + scaledEpsInfo_[c].Sgcr + scaledEpsInfo_[c].Swl) >= 1.0) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SOGCR + SGCR + SWL exceed 1.0"; OpmLog::warning(tag, msg); } } ///Following rules come from NEXUS. if (fluidSystem_ != FluidSystem::WaterGas) { if (scaledEpsInfo_[c].Swl > scaledEpsInfo_[c].Swcr) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SWL > SWCR"; OpmLog::warning(tag, msg); } if (scaledEpsInfo_[c].Swcr > scaledEpsInfo_[c].Sowcr) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SWCR > SOWCR"; OpmLog::warning(tag, msg); } if (scaledEpsInfo_[c].Sowcr > scaledEpsInfo_[c].Swu) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SOWCR > SWU"; OpmLog::warning(tag, msg); } } if (fluidSystem_ != FluidSystem::OilWater) { if (scaledEpsInfo_[c].Sgl > scaledEpsInfo_[c].Sgcr) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SGL > SGCR"; OpmLog::warning(tag, msg); } } if (fluidSystem_ != FluidSystem::BlackOil) { if (scaledEpsInfo_[c].Sgcr > scaledEpsInfo_[c].Sogcr) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SGCR > SOGCR"; OpmLog::warning(tag, msg); } if (scaledEpsInfo_[c].Sogcr > scaledEpsInfo_[c].Sgu) { const std::string msg = "For scaled endpoints input, cell" + cellIdx + " SATNUM = " + satnumIdx + ", SOGCR > SGU"; OpmLog::warning(tag, msg); } } } }
SolventPropsAdFromDeck::SolventPropsAdFromDeck(DeckConstPtr deck, EclipseStateConstPtr eclState, const int number_of_cells, const int* global_cell) { if (deck->hasKeyword("SOLVENT")) { // retrieve the cell specific PVT table index from the deck // and using the grid... extractPvtTableIndex(cellPvtRegionIdx_, eclState, number_of_cells, global_cell); extractTableIndex("SATNUM", eclState, number_of_cells, global_cell, cellSatNumRegionIdx_); // surface densities if (deck->hasKeyword("SDENSITY")) { const auto& densityKeyword = deck->getKeyword("SDENSITY"); int numRegions = densityKeyword.size(); solvent_surface_densities_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { solvent_surface_densities_[regionIdx] = densityKeyword.getRecord(regionIdx).getItem("SOLVENT_DENSITY").getSIDouble(0); } } else { OPM_THROW(std::runtime_error, "SDENSITY must be specified in SOLVENT runs\n"); } auto tables = eclState->getTableManager(); // pvt const TableContainer& pvdsTables = tables->getPvdsTables(); if (!pvdsTables.empty()) { int numRegions = pvdsTables.size(); // resize the attributes of the object b_.resize(numRegions); viscosity_.resize(numRegions); inverseBmu_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::PvdsTable& pvdsTable = pvdsTables.getTable<PvdsTable>(regionIdx); const auto& press = pvdsTable.getPressureColumn(); const auto& b = pvdsTable.getFormationFactorColumn(); const auto& visc = pvdsTable.getViscosityColumn(); const int sz = b.size(); std::vector<double> inverseBmu(sz); std::vector<double> inverseB(sz); for (int i = 0; i < sz; ++i) { inverseB[i] = 1.0 / b[i]; inverseBmu[i] = 1.0 / (b[i] * visc[i]); } b_[regionIdx] = NonuniformTableLinear<double>(press, inverseB); viscosity_[regionIdx] = NonuniformTableLinear<double>(press, visc); inverseBmu_[regionIdx] = NonuniformTableLinear<double>(press, inverseBmu); } } else { OPM_THROW(std::runtime_error, "PVDS must be specified in SOLVENT runs\n"); } const TableContainer& ssfnTables = tables->getSsfnTables(); // relative permeabilty multiplier if (!ssfnTables.empty()) { int numRegions = ssfnTables.size(); // resize the attributes of the object krg_.resize(numRegions); krs_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::SsfnTable& ssfnTable = ssfnTables.getTable<SsfnTable>(regionIdx); // Copy data const auto& solventFraction = ssfnTable.getSolventFractionColumn(); const auto& krg = ssfnTable.getGasRelPermMultiplierColumn(); const auto& krs = ssfnTable.getSolventRelPermMultiplierColumn(); krg_[regionIdx] = NonuniformTableLinear<double>(solventFraction, krg); krs_[regionIdx] = NonuniformTableLinear<double>(solventFraction, krs); } } else { OPM_THROW(std::runtime_error, "SSFN must be specified in SOLVENT runs\n"); } if (deck->hasKeyword("MISCIBLE") ) { // retrieve the cell specific Misc table index from the deck // and using the grid... extractTableIndex("MISCNUM", eclState, number_of_cells, global_cell, cellMiscRegionIdx_); // misicible hydrocabon relative permeability wrt water const TableContainer& sof2Tables = tables->getSof2Tables(); if (!sof2Tables.empty()) { int numRegions = sof2Tables.size(); // resize the attributes of the object krn_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::Sof2Table& sof2Table = sof2Tables.getTable<Sof2Table>(regionIdx); // Copy data // Sn = So + Sg + Ss; const auto& sn = sof2Table.getSoColumn(); const auto& krn = sof2Table.getKroColumn(); krn_[regionIdx] = NonuniformTableLinear<double>(sn, krn); } } else { OPM_THROW(std::runtime_error, "SOF2 must be specified in MISCIBLE (SOLVENT) runs\n"); } const TableContainer& miscTables = tables->getMiscTables(); if (!miscTables.empty()) { int numRegions = miscTables.size(); // resize the attributes of the object misc_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::MiscTable& miscTable = miscTables.getTable<MiscTable>(regionIdx); // Copy data // solventFraction = Ss / (Ss + Sg); const auto& solventFraction = miscTable.getSolventFractionColumn(); const auto& misc = miscTable.getMiscibilityColumn(); misc_[regionIdx] = NonuniformTableLinear<double>(solventFraction, misc); } } else { OPM_THROW(std::runtime_error, "MISC must be specified in MISCIBLE (SOLVENT) runs\n"); } const TableContainer& pmiscTables = tables->getPmiscTables(); if (!pmiscTables.empty()) { int numRegions = pmiscTables.size(); // resize the attributes of the object pmisc_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::PmiscTable& pmiscTable = pmiscTables.getTable<PmiscTable>(regionIdx); // Copy data const auto& po = pmiscTable.getOilPhasePressureColumn(); const auto& pmisc = pmiscTable.getMiscibilityColumn(); pmisc_[regionIdx] = NonuniformTableLinear<double>(po, pmisc); } } // miscible relative permeability multipleiers const TableContainer& msfnTables = tables->getMsfnTables(); if (!msfnTables.empty()) { int numRegions = msfnTables.size(); // resize the attributes of the object mkrsg_.resize(numRegions); mkro_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::MsfnTable& msfnTable = msfnTables.getTable<MsfnTable>(regionIdx); // Copy data // Ssg = Ss + Sg; const auto& Ssg = msfnTable.getGasPhaseFractionColumn(); const auto& krsg = msfnTable.getGasSolventRelpermMultiplierColumn(); const auto& kro = msfnTable.getOilRelpermMultiplierColumn(); mkrsg_[regionIdx] = NonuniformTableLinear<double>(Ssg, krsg); mkro_[regionIdx] = NonuniformTableLinear<double>(Ssg, kro); } } const TableContainer& sorwmisTables = tables->getSorwmisTables(); if (!sorwmisTables.empty()) { int numRegions = sorwmisTables.size(); // resize the attributes of the object sorwmis_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::SorwmisTable& sorwmisTable = sorwmisTables.getTable<SorwmisTable>(regionIdx); // Copy data const auto& sw = sorwmisTable.getWaterSaturationColumn(); const auto& sorwmis = sorwmisTable.getMiscibleResidualOilColumn(); sorwmis_[regionIdx] = NonuniformTableLinear<double>(sw, sorwmis); } } const TableContainer& sgcwmisTables = tables->getSgcwmisTables(); if (!sgcwmisTables.empty()) { int numRegions = sgcwmisTables.size(); // resize the attributes of the object sgcwmis_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const Opm::SgcwmisTable& sgcwmisTable = sgcwmisTables.getTable<SgcwmisTable>(regionIdx); // Copy data const auto& sw = sgcwmisTable.getWaterSaturationColumn(); const auto& sgcwmis = sgcwmisTable.getMiscibleResidualGasColumn(); sgcwmis_[regionIdx] = NonuniformTableLinear<double>(sw, sgcwmis); } } if (deck->hasKeyword("TLMIXPAR")) { const int numRegions = deck->getKeyword("TLMIXPAR").size(); // resize the attributes of the object mix_param_viscosity_.resize(numRegions); mix_param_density_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const auto& tlmixparRecord = deck->getKeyword("TLMIXPAR").getRecord(regionIdx); const auto& mix_params_viscosity = tlmixparRecord.getItem("TL_VISCOSITY_PARAMETER").getSIDoubleData(); mix_param_viscosity_[regionIdx] = mix_params_viscosity[0]; const auto& mix_params_density = tlmixparRecord.getItem("TL_DENSITY_PARAMETER").getSIDoubleData(); const int numDensityItems = mix_params_density.size(); if (numDensityItems == 0) { mix_param_density_[regionIdx] = mix_param_viscosity_[regionIdx]; } else if (numDensityItems == 1) { mix_param_density_[regionIdx] = mix_params_density[0]; } else { OPM_THROW(std::runtime_error, "Only one value can be entered for the TL parameter pr MISC region."); } } } } } }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; std::cout << "\n================ Test program for weakly compressible two-phase flow ===============\n\n"; parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); EclipseStateConstPtr eclipseState; std::unique_ptr<GridManager> grid; std::unique_ptr<BlackoilPropertiesInterface> props; std::unique_ptr<RockCompressibility> rock_comp; ParserPtr parser(new Opm::Parser()); Opm::DeckConstPtr deck; BlackoilState state; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { ParseMode parseMode; std::string deck_filename = param.get<std::string>("deck_filename"); deck = parser->parseFile(deck_filename , parseMode); eclipseState.reset(new EclipseState(deck, parseMode)); // Grid init grid.reset(new GridManager(deck)); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, *grid->c_grid(), param)); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. rock_comp.reset(new RockCompressibility(deck, eclipseState)); // Gravity. gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). if (param.has("init_saturation")) { initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); } else { initStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state); } initBlackoilSurfvol(*grid->c_grid(), *props, state); } else { // Grid init. const int nx = param.getDefault("nx", 100); const int ny = param.getDefault("ny", 100); const int nz = param.getDefault("nz", 1); const double dx = param.getDefault("dx", 1.0); const double dy = param.getDefault("dy", 1.0); const double dz = param.getDefault("dz", 1.0); grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); // Rock and fluid init. props.reset(new BlackoilPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells)); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); initBlackoilSurfvol(*grid->c_grid(), *props, state); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; // Initialising src int num_cells = grid->c_grid()->number_of_cells; std::vector<double> src(num_cells, 0.0); if (use_deck) { // Do nothing, wells will be the driving force, not source terms. } else { // Compute pore volumes, in order to enable specifying injection rate // terms of total pore volume. std::vector<double> porevol; if (rock_comp->isActive()) { computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); } else { computePorevolume(*grid->c_grid(), props->porosity(), porevol); } const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); const double default_injection = use_gravity ? 0.0 : 0.1; const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_day", default_injection) *tot_porevol_init/unit::day; src[0] = flow_per_sec; src[num_cells - 1] = -flow_per_sec; } // Boundary conditions. FlowBCManager bcs; if (param.getDefault("use_pside", false)) { int pside = param.get<int>("pside"); double pside_pressure = param.get<double>("pside_pressure"); bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure); } // Linear solver. LinearSolverFactory linsolver(param); // Write parameters used for later reference. bool output = param.getDefault("output", true); std::ofstream epoch_os; std::string output_dir; if (output) { output_dir = param.getDefault("output_dir", std::string("output")); boost::filesystem::path fpath(output_dir); try { create_directories(fpath); } catch (...) { OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); } std::string filename = output_dir + "/epoch_timing.param"; epoch_os.open(filename.c_str(), std::fstream::trunc | std::fstream::out); // open file to clean it. The file is appended to in SimulatorTwophase filename = output_dir + "/step_timing.param"; std::fstream step_os(filename.c_str(), std::fstream::trunc | std::fstream::out); step_os.close(); param.writeParam(output_dir + "/simulation.param"); } std::cout << "\n\n================ Starting main simulation loop ===============\n"; SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. WellsManager wells; // no wells. SimulatorCompressibleTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, src, bcs.c_bcs(), linsolver, grav); SimulatorTimer simtimer; simtimer.init(param); warnIfUnusedParams(param); WellState well_state; well_state.init(0, state); rep = simulator.run(simtimer, state, well_state); } else { // With a deck, we may have more epochs etc. WellState well_state; int step = 0; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck)); simtimer.init(timeMap); const double total_time = simtimer.totalTime(); for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of report step. std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every report step, // since number of wells may change etc. if (reportStepIdx == 0) { well_state.init(wells.c_wells(), state); } // Create and run simulator. SimulatorCompressibleTwophase simulator(param, *grid->c_grid(), *props, rock_comp->isActive() ? rock_comp.get() : 0, wells, src, bcs.c_bcs(), linsolver, grav); if (reportStepIdx == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); if (output) { epoch_rep.reportParam(epoch_os); } // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); } } std::cout << "\n\n================ End of simulation ===============\n\n"; rep.report(std::cout); if (output) { std::string filename = output_dir + "/walltime.param"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); rep.reportParam(tot_os); } } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; std::cout << "\n================ Test program for weakly compressible two-phase flow with polymer ===============\n\n"; parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); boost::scoped_ptr<EclipseGridParser> deck; boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<BlackoilPropertiesInterface> props; boost::scoped_ptr<RockCompressibility> rock_comp; EclipseStateConstPtr eclipseState; PolymerBlackoilState state; Opm::PolymerProperties poly_props; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { std::string deck_filename = param.get<std::string>("deck_filename"); ParserPtr parser(new Opm::Parser()); eclipseState.reset(new Opm::EclipseState(parser->parseFile(deck_filename))); deck.reset(new EclipseGridParser(deck_filename)); // Grid init grid.reset(new GridManager(*deck)); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(*deck, *grid->c_grid())); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); // Rock compressibility. rock_comp.reset(new RockCompressibility(*deck)); // Gravity. gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). if (param.has("init_saturation")) { initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); } else { initStateFromDeck(*grid->c_grid(), *props, *deck, gravity[2], state); } initBlackoilSurfvol(*grid->c_grid(), *props, state); // Init polymer properties. poly_props.readFromDeck(*deck); } else { // Grid init. const int nx = param.getDefault("nx", 100); const int ny = param.getDefault("ny", 100); const int nz = param.getDefault("nz", 1); const double dx = param.getDefault("dx", 1.0); const double dy = param.getDefault("dy", 1.0); const double dz = param.getDefault("dz", 1.0); grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); // Rock and fluid init. props.reset(new BlackoilPropertiesBasic(param, grid->c_grid()->dimensions, grid->c_grid()->number_of_cells)); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(*grid->c_grid(), *props, param, gravity[2], state); initBlackoilSurfvol(*grid->c_grid(), *props, state); // Init Polymer state if (param.has("poly_init")) { double poly_init = param.getDefault("poly_init", 0.0); for (int cell = 0; cell < grid->c_grid()->number_of_cells; ++cell) { double smin[2], smax[2]; props->satRange(1, &cell, smin, smax); if (state.saturation()[2*cell] > 0.5*(smin[0] + smax[0])) { state.concentration()[cell] = poly_init; state.maxconcentration()[cell] = poly_init; } else { state.saturation()[2*cell + 0] = 0.; state.saturation()[2*cell + 1] = 1.; state.concentration()[cell] = 0.; state.maxconcentration()[cell] = 0.; } } } // Init polymer properties. // Setting defaults to provide a simple example case. double c_max = param.getDefault("c_max_limit", 5.0); double mix_param = param.getDefault("mix_param", 1.0); double rock_density = param.getDefault("rock_density", 1000.0); double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15); double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability double c_max_ads = param.getDefault("c_max_ads", 1.); int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption); std::vector<double> c_vals_visc(2, -1e100); c_vals_visc[0] = 0.0; c_vals_visc[1] = 7.0; std::vector<double> visc_mult_vals(2, -1e100); visc_mult_vals[0] = 1.0; // poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0); visc_mult_vals[1] = 20.0; std::vector<double> c_vals_ads(3, -1e100); c_vals_ads[0] = 0.0; c_vals_ads[1] = 2.0; c_vals_ads[2] = 8.0; std::vector<double> ads_vals(3, -1e100); ads_vals[0] = 0.0; ads_vals[1] = 0.0015; ads_vals[2] = 0.0025; // ads_vals[1] = 0.0; // ads_vals[2] = 0.0; poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads, static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index), c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; // Initialising src int num_cells = grid->c_grid()->number_of_cells; std::vector<double> src(num_cells, 0.0); if (use_deck) { // Do nothing, wells will be the driving force, not source terms. } else { // Compute pore volumes, in order to enable specifying injection rate // terms of total pore volume. std::vector<double> porevol; if (rock_comp->isActive()) { computePorevolume(*grid->c_grid(), props->porosity(), *rock_comp, state.pressure(), porevol); } else { computePorevolume(*grid->c_grid(), props->porosity(), porevol); } const double tot_porevol_init = std::accumulate(porevol.begin(), porevol.end(), 0.0); const double default_injection = use_gravity ? 0.0 : 0.1; const double flow_per_sec = param.getDefault<double>("injected_porevolumes_per_day", default_injection) *tot_porevol_init/unit::day; src[0] = flow_per_sec; src[num_cells - 1] = -flow_per_sec; } // Boundary conditions. FlowBCManager bcs; if (param.getDefault("use_pside", false)) { int pside = param.get<int>("pside"); double pside_pressure = param.get<double>("pside_pressure"); bcs.pressureSide(*grid->c_grid(), FlowBCManager::Side(pside), pside_pressure); } // Linear solver. LinearSolverFactory linsolver(param); // Write parameters used for later reference. bool output = param.getDefault("output", true); if (output) { std::string output_dir = param.getDefault("output_dir", std::string("output")); boost::filesystem::path fpath(output_dir); try { create_directories(fpath); } catch (...) { OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); } param.writeParam(output_dir + "/simulation.param"); } std::cout << "\n\n================ Starting main simulation loop ===============\n" << " (number of epochs: " << (use_deck ? deck->numberOfEpochs() : 1) << ")\n\n" << std::flush; SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax())); WellsManager wells; SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, polymer_inflow, src, bcs.c_bcs(), linsolver, grav); SimulatorTimer simtimer; simtimer.init(param); warnIfUnusedParams(param); WellState well_state; well_state.init(0, state); rep = simulator.run(simtimer, state, well_state); } else { // With a deck, we may have more epochs etc. WellState well_state; int step = 0; SimulatorTimer simtimer; // Use timer for last epoch to obtain total time. deck->setCurrentEpoch(deck->numberOfEpochs() - 1); simtimer.init(*deck); const double total_time = simtimer.totalTime(); // Check for WPOLYMER presence in last epoch to decide // polymer injection control type. const bool use_wpolymer = deck->hasField("WPOLYMER"); if (use_wpolymer) { if (param.has("poly_start_days")) { OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } } for (int epoch = 0; epoch < deck->numberOfEpochs(); ++epoch) { // Set epoch index. deck->setCurrentEpoch(epoch); // Update the timer. if (deck->hasField("TSTEP")) { simtimer.init(*deck); } else { if (epoch != 0) { OPM_THROW(std::runtime_error, "No TSTEP in deck for epoch " << epoch); } simtimer.init(param); } simtimer.setCurrentStepNum(step); simtimer.setTotalTime(total_time); // Report on start of epoch. std::cout << "\n\n-------------- Starting epoch " << epoch << " --------------" << "\n (number of steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. WellsManager wells(eclipseState , epoch , *grid->c_grid(), props->permeability()); boost::scoped_ptr<PolymerInflowInterface> polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); } polymer_inflow.reset(new PolymerInflowFromDeck(*deck, *wells.c_wells(), props->numCells())); } else { polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax()))); } // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. if (epoch == 0) { well_state.init(wells.c_wells(), state); } // Create and run simulator. SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, *polymer_inflow, src, bcs.c_bcs(), linsolver, grav); if (epoch == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, state, well_state); // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); } } std::cout << "\n\n================ End of simulation ===============\n\n"; rep.report(std::cout); } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
void computeMaxDp(std::map<std::pair<int, int>, double>& maxDp, const DeckConstPtr& deck, EclipseStateConstPtr eclipseState, const Grid& grid, const BlackoilState& initialState, const BlackoilPropertiesFromDeck& props, const double gravity) { const PhaseUsage& pu = props.phaseUsage(); const auto& eqlnum = eclipseState->get3DProperties().getIntGridProperty("EQLNUM"); const auto& eqlnumData = eqlnum.getData(); const int numPhases = initialState.numPhases(); const int numCells = UgGridHelpers::numCells(grid); const int numPvtRegions = deck->getKeyword("TABDIMS").getRecord(0).getItem("NTPVT").get< int >(0); // retrieve the minimum (residual!?) and the maximum saturations for all cells std::vector<double> minSat(numPhases*numCells); std::vector<double> maxSat(numPhases*numCells); std::vector<int> allCells(numCells); for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) { allCells[cellIdx] = cellIdx; } props.satRange(numCells, allCells.data(), minSat.data(), maxSat.data()); // retrieve the surface densities std::vector<std::vector<double> > surfaceDensity(numPvtRegions); const auto& densityKw = deck->getKeyword("DENSITY"); for (int regionIdx = 0; regionIdx < numPvtRegions; ++regionIdx) { surfaceDensity[regionIdx].resize(numPhases); if (pu.phase_used[BlackoilPhases::Aqua]) { const int wpos = pu.phase_pos[BlackoilPhases::Aqua]; surfaceDensity[regionIdx][wpos] = densityKw.getRecord(regionIdx).getItem("WATER").getSIDouble(0); } if (pu.phase_used[BlackoilPhases::Liquid]) { const int opos = pu.phase_pos[BlackoilPhases::Liquid]; surfaceDensity[regionIdx][opos] = densityKw.getRecord(regionIdx).getItem("OIL").getSIDouble(0); } if (pu.phase_used[BlackoilPhases::Vapour]) { const int gpos = pu.phase_pos[BlackoilPhases::Vapour]; surfaceDensity[regionIdx][gpos] = densityKw.getRecord(regionIdx).getItem("GAS").getSIDouble(0); } } // retrieve the PVT region of each cell. note that we need c++ instead of // Fortran indices. const int* gc = UgGridHelpers::globalCell(grid); std::vector<int> pvtRegion(numCells); const auto& cartPvtRegion = eclipseState->get3DProperties().getIntGridProperty("PVTNUM").getData(); for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) { const int cartCellIdx = gc ? gc[cellIdx] : cellIdx; pvtRegion[cellIdx] = std::max(0, cartPvtRegion[cartCellIdx] - 1); } // compute the initial "phase presence" of each cell (required to calculate // the inverse formation volume factors std::vector<PhasePresence> cond(numCells); for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) { if (pu.phase_used[BlackoilPhases::Aqua]) { const double sw = initialState.saturation()[numPhases*cellIdx + pu.phase_pos[BlackoilPhases::Aqua]]; if (sw > 0.0) { cond[cellIdx].setFreeWater(); } } if (pu.phase_used[BlackoilPhases::Liquid]) { const double so = initialState.saturation()[numPhases*cellIdx + pu.phase_pos[BlackoilPhases::Liquid]]; if (so > 0.0) { cond[cellIdx].setFreeOil(); } } if (pu.phase_used[BlackoilPhases::Vapour]) { const double sg = initialState.saturation()[numPhases*cellIdx + pu.phase_pos[BlackoilPhases::Vapour]]; if (sg > 0.0) { cond[cellIdx].setFreeGas(); } } } // calculate the initial fluid densities for the gravity correction. std::vector<std::vector<double>> rho(numPhases); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { rho[phaseIdx].resize(numCells); } // compute the capillary pressures of the active phases std::vector<double> capPress(numCells*numPhases); std::vector<int> cellIdxArray(numCells); for (int cellIdx = 0; cellIdx < numCells; ++ cellIdx) { cellIdxArray[cellIdx] = cellIdx; } props.capPress(numCells, initialState.saturation().data(), cellIdxArray.data(), capPress.data(), NULL); // compute the absolute pressure of each active phase: for some reason, E100 // defines the capillary pressure for the water phase as p_o - p_w while it // uses p_g - p_o for the gas phase. (it would be more consistent to use the // oil pressure as reference for both the other phases.) probably this is // done to always have a positive number for the capillary pressure (as long // as the medium is hydrophilic) std::vector<std::vector<double> > phasePressure(numPhases); for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { phasePressure[phaseIdx].resize(numCells); } for (int cellIdx = 0; cellIdx < numCells; ++ cellIdx) { // we currently hard-code the oil phase as the reference phase! assert(pu.phase_used[BlackoilPhases::Liquid]); const int opos = pu.phase_pos[BlackoilPhases::Liquid]; phasePressure[opos][cellIdx] = initialState.pressure()[cellIdx]; if (pu.phase_used[BlackoilPhases::Aqua]) { const int wpos = pu.phase_pos[BlackoilPhases::Aqua]; phasePressure[wpos][cellIdx] = initialState.pressure()[cellIdx] + (capPress[cellIdx*numPhases + opos] - capPress[cellIdx*numPhases + wpos]); } if (pu.phase_used[BlackoilPhases::Vapour]) { const int gpos = pu.phase_pos[BlackoilPhases::Vapour]; phasePressure[gpos][cellIdx] = initialState.pressure()[cellIdx] + (capPress[cellIdx*numPhases + gpos] - capPress[cellIdx*numPhases + opos]); } } // calculate the densities of the active phases for each cell if (pu.phase_used[BlackoilPhases::Aqua]) { const int wpos = pu.phase_pos[BlackoilPhases::Aqua]; const auto& pvtw = props.waterPvt(); for (int cellIdx = 0; cellIdx < numCells; ++ cellIdx) { int pvtRegionIdx = pvtRegion[cellIdx]; double T = initialState.temperature()[cellIdx]; double p = phasePressure[wpos][cellIdx]; double b = pvtw.inverseFormationVolumeFactor(pvtRegionIdx, T, p); rho[wpos][cellIdx] = surfaceDensity[pvtRegionIdx][wpos]*b; } } if (pu.phase_used[BlackoilPhases::Liquid]) { const int opos = pu.phase_pos[BlackoilPhases::Liquid]; const auto& pvto = props.oilPvt(); for (int cellIdx = 0; cellIdx < numCells; ++ cellIdx) { int pvtRegionIdx = pvtRegion[cellIdx]; double T = initialState.temperature()[cellIdx]; double p = phasePressure[opos][cellIdx]; double Rs = initialState.gasoilratio()[cellIdx]; double RsSat = pvto.saturatedGasDissolutionFactor(pvtRegionIdx, T, p); double b; if (Rs >= RsSat) { b = pvto.saturatedInverseFormationVolumeFactor(pvtRegionIdx, T, p); } else { b = pvto.inverseFormationVolumeFactor(pvtRegionIdx, T, p, Rs); } rho[opos][cellIdx] = surfaceDensity[pvtRegionIdx][opos]*b; if (pu.phase_used[BlackoilPhases::Vapour]) { int gpos = pu.phase_pos[BlackoilPhases::Vapour]; rho[opos][cellIdx] += surfaceDensity[pvtRegionIdx][gpos]*Rs*b; } } } if (pu.phase_used[BlackoilPhases::Vapour]) { const int gpos = pu.phase_pos[BlackoilPhases::Vapour]; const auto& pvtg = props.gasPvt(); for (int cellIdx = 0; cellIdx < numCells; ++ cellIdx) { int pvtRegionIdx = pvtRegion[cellIdx]; double T = initialState.temperature()[cellIdx]; double p = phasePressure[gpos][cellIdx]; double Rv = initialState.rv()[cellIdx]; double RvSat = pvtg.saturatedOilVaporizationFactor(pvtRegionIdx, T, p); double b; if (Rv >= RvSat) { b = pvtg.saturatedInverseFormationVolumeFactor(pvtRegionIdx, T, p); } else { b = pvtg.inverseFormationVolumeFactor(pvtRegionIdx, T, p, Rv); } rho[gpos][cellIdx] = surfaceDensity[pvtRegionIdx][gpos]*b; if (pu.phase_used[BlackoilPhases::Liquid]) { int opos = pu.phase_pos[BlackoilPhases::Liquid]; rho[gpos][cellIdx] += surfaceDensity[pvtRegionIdx][opos]*Rv*b; } } } // Calculate the maximum pressure potential difference between all PVT region // transitions of the initial solution. const int num_faces = UgGridHelpers::numFaces(grid); const auto& fc = UgGridHelpers::faceCells(grid); for (int face = 0; face < num_faces; ++face) { const int c1 = fc(face, 0); const int c2 = fc(face, 1); if (c1 < 0 || c2 < 0) { // Boundary face, skip this. continue; } const int gc1 = (gc == 0) ? c1 : gc[c1]; const int gc2 = (gc == 0) ? c2 : gc[c2]; const int eq1 = eqlnumData[gc1]; const int eq2 = eqlnumData[gc2]; if (eq1 == eq2) { // not an equilibration region boundary. skip this. continue; } // update the maximum pressure potential difference between the two // regions const auto barrierId = std::make_pair(std::min(eq1, eq2), std::max(eq1, eq2)); if (maxDp.count(barrierId) == 0) { maxDp[barrierId] = 0.0; } for (int phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) { const double z1 = UgGridHelpers::cellCenterDepth(grid, c1); const double z2 = UgGridHelpers::cellCenterDepth(grid, c2); const double rhoAvg = (rho[phaseIdx][c1] + rho[phaseIdx][c2])/2; const double s1 = initialState.saturation()[numPhases*c1 + phaseIdx]; const double s2 = initialState.saturation()[numPhases*c2 + phaseIdx]; const double sResid1 = minSat[numPhases*c1 + phaseIdx]; const double sResid2 = minSat[numPhases*c2 + phaseIdx]; // compute gravity corrected pressure potentials at the average depth const double p1 = phasePressure[phaseIdx][c1]; const double p2 = phasePressure[phaseIdx][c2] + rhoAvg*gravity*(z1 - z2); if ((p1 > p2 && s1 > sResid1) || (p2 > p1 && s2 > sResid2)) maxDp[barrierId] = std::max(maxDp[barrierId], std::abs(p1 - p2)); } } }
/*! * \brief Implement the temperature part of the oil PVT properties. */ void initFromDeck(DeckConstPtr deck, EclipseStateConstPtr eclState) { ////// // initialize the isothermal part ////// isothermalPvt_ = new IsothermalPvt; isothermalPvt_->initFromDeck(deck, eclState); ////// // initialize the thermal part ////// auto tables = eclState->getTableManager(); enableThermalDensity_ = deck->hasKeyword("THERMEX1"); enableThermalViscosity_ = deck->hasKeyword("VISCREF"); unsigned numRegions = isothermalPvt_->numRegions(); setNumRegions(numRegions); // viscosity if (deck->hasKeyword("VISCREF")) { const auto& oilvisctTables = tables->getOilvisctTables(); Opm::DeckKeywordConstPtr viscrefKeyword = deck->getKeyword("VISCREF"); assert(oilvisctTables.size() == numRegions); assert(viscrefKeyword->size() == numRegions); for (unsigned regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const auto& TCol = oilvisctTables[regionIdx].getColumn("Temperature").vectorCopy(); const auto& muCol = oilvisctTables[regionIdx].getColumn("Viscosity").vectorCopy(); oilvisctCurves_[regionIdx].setXYContainers(TCol, muCol); DeckRecordConstPtr viscrefRecord = viscrefKeyword->getRecord(regionIdx); viscrefPress_[regionIdx] = viscrefRecord->getItem("REFERENCE_PRESSURE")->getSIDouble(0); viscrefRs_[regionIdx] = viscrefRecord->getItem("REFERENCE_RS")->getSIDouble(0); // temperature used to calculate the reference viscosity [K]. the // value does not really matter if the underlying PVT object really // is isothermal... Scalar Tref = 273.15 + 20; // compute the reference viscosity using the isothermal PVT object. viscRef_[regionIdx] = isothermalPvt_->viscosity(regionIdx, Tref, viscrefPress_[regionIdx], viscrefRs_[regionIdx]); } } // quantities required for density. note that we just always use the values // for the first EOS. (since EOS != PVT region.) refTemp_ = 0.0; if (deck->hasKeyword("THERMEX1")) { int oilCompIdx = deck->getKeyword("OCOMPIDX")->getRecord(0)->getItem("OIL_COMPONENT_INDEX")->getInt(0) - 1; // always use the values of the first EOS refTemp_ = deck->getKeyword("TREF")->getRecord(0)->getItem("TEMPERATURE")->getSIDouble(oilCompIdx); refPress_ = deck->getKeyword("PREF")->getRecord(0)->getItem("PRESSURE")->getSIDouble(oilCompIdx); refC_ = deck->getKeyword("CREF")->getRecord(0)->getItem("COMPRESSIBILITY")->getSIDouble(oilCompIdx); thermex1_ = deck->getKeyword("THERMEX1")->getRecord(0)->getItem("EXPANSION_COEFF")->getSIDouble(oilCompIdx); } }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; std::cout << "\n================ Test program for weakly compressible two-phase flow with polymer ===============\n\n"; parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<BlackoilPropertiesInterface> props; boost::scoped_ptr<RockCompressibility> rock_comp; Opm::DeckConstPtr deck; EclipseStateConstPtr eclipseState; std::unique_ptr<PolymerBlackoilState> state; Opm::PolymerProperties poly_props; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { std::string deck_filename = param.get<std::string>("deck_filename"); ParserPtr parser(new Opm::Parser()); Opm::ParseContext parseContext({{ ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE }}); deck = parser->parseFile(deck_filename , parseContext); eclipseState.reset(new Opm::EclipseState(deck , parseContext)); // Grid init grid.reset(new GridManager(deck)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, eclipseState, ug_grid)); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ), 2)); // Rock compressibility. rock_comp.reset(new RockCompressibility(deck, eclipseState)); // Gravity. gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). if (param.has("init_saturation")) { initStateBasic(ug_grid, *props, param, gravity[2], *state); } else { initStateFromDeck(ug_grid, *props, deck, gravity[2], *state); } initBlackoilSurfvol(ug_grid, *props, *state); // Init polymer properties. poly_props.readFromDeck(deck, eclipseState); } } else { // Grid init. const int nx = param.getDefault("nx", 100); const int ny = param.getDefault("ny", 100); const int nz = param.getDefault("nz", 1); const double dx = param.getDefault("dx", 1.0); const double dy = param.getDefault("dy", 1.0); const double dz = param.getDefault("dz", 1.0); grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init. props.reset(new BlackoilPropertiesBasic(param, ug_grid.dimensions, UgGridHelpers::numCells( ug_grid ))); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ) , 2)); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(ug_grid, *props, param, gravity[2], *state); initBlackoilSurfvol(ug_grid, *props, *state); // Init Polymer state if (param.has("poly_init")) { double poly_init = param.getDefault("poly_init", 0.0); for (int cell = 0; cell < UgGridHelpers::numCells( ug_grid ); ++cell) { double smin[2], smax[2]; auto& saturation = state->saturation(); auto& concentration = state->getCellData( state->CONCENTRATION ); auto& max_concentration = state->getCellData( state->CMAX ); props->satRange(1, &cell, smin, smax); if (saturation[2*cell] > 0.5*(smin[0] + smax[0])) { concentration[cell] = poly_init; max_concentration[cell] = poly_init; } else { saturation[2*cell + 0] = 0.; saturation[2*cell + 1] = 1.; concentration[cell] = 0.; max_concentration[cell] = 0.; } } } } // Init polymer properties. // Setting defaults to provide a simple example case. double c_max = param.getDefault("c_max_limit", 5.0); double mix_param = param.getDefault("mix_param", 1.0); double rock_density = param.getDefault("rock_density", 1000.0); double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15); double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability double c_max_ads = param.getDefault("c_max_ads", 1.); int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption); std::vector<double> c_vals_visc(2, -1e100); c_vals_visc[0] = 0.0; c_vals_visc[1] = 7.0; std::vector<double> visc_mult_vals(2, -1e100); visc_mult_vals[0] = 1.0; // poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0); visc_mult_vals[1] = 20.0; std::vector<double> c_vals_ads(3, -1e100); c_vals_ads[0] = 0.0; c_vals_ads[1] = 2.0; c_vals_ads[2] = 8.0; std::vector<double> ads_vals(3, -1e100); ads_vals[0] = 0.0; ads_vals[1] = 0.0015; ads_vals[2] = 0.0025; // ads_vals[1] = 0.0; // ads_vals[2] = 0.0; std::vector<double> water_vel_vals(2, -1e100); water_vel_vals[0] = 0.0; water_vel_vals[1] = 10.0; std::vector<double> shear_vrf_vals(2, -1e100); shear_vrf_vals[0] = 1.0; shear_vrf_vals[1] = 1.0; poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads, static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index), c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals, water_vel_vals, shear_vrf_vals); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; // Linear solver. LinearSolverFactory linsolver(param); // Write parameters used for later reference. bool output = param.getDefault("output", true); if (output) { std::string output_dir = param.getDefault("output_dir", std::string("output")); boost::filesystem::path fpath(output_dir); try { create_directories(fpath); } catch (...) { OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); } param.writeParam(output_dir + "/simulation.param"); } std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax())); WellsManager wells; SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, polymer_inflow, linsolver, grav); SimulatorTimer simtimer; simtimer.init(param); warnIfUnusedParams(param); WellState well_state; well_state.init(0, *state); rep = simulator.run(simtimer, *state, well_state); } else { // With a deck, we may have more epochs etc. WellState well_state; int step = 0; Opm::TimeMapPtr timeMap(new Opm::TimeMap(deck)); SimulatorTimer simtimer; simtimer.init(timeMap); // Check for WPOLYMER presence in last report step to decide // polymer injection control type. const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); if (use_wpolymer) { if (param.has("poly_start_days")) { OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } } for (size_t reportStepIdx = 0; reportStepIdx < timeMap->numTimesteps(); ++reportStepIdx) { simtimer.setCurrentStepNum(reportStepIdx); // Report on start of report step. std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" << "\n (number of remaining steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. WellsManager wells(eclipseState , reportStepIdx , *grid->c_grid(), props->permeability()); boost::scoped_ptr<PolymerInflowInterface> polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); } polymer_inflow.reset(new PolymerInflowFromDeck(eclipseState, *wells.c_wells(), props->numCells(), simtimer.currentStepNum())); } else { polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax()))); } // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every report step, // since number of wells may change etc. if (reportStepIdx == 0) { well_state.init(wells.c_wells(), *state); } // Create and run simulator. SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, *polymer_inflow, linsolver, grav); if (reportStepIdx == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, *state, well_state); // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); } } std::cout << "\n\n================ End of simulation ===============\n\n"; rep.report(std::cout); } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }