void filterIntegerField(const std::string& keyword, std::vector<int>& output_field) { if (deck_->hasKeyword(keyword)) { const std::vector<int>& field = deck_->getKeyword(keyword)->getIntData(); filterField(field, output_field); } }
IncompPropertiesSinglePhase::IncompPropertiesSinglePhase(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclState, const UnstructuredGrid& grid) { rock_.init(eclState, grid.number_of_cells, grid.global_cell, grid.cartdims); if (deck->hasKeyword("DENSITY")) { Opm::DeckRecordConstPtr densityRecord = deck->getKeyword("DENSITY")->getRecord(0); surface_density_ = densityRecord->getItem("OIL")->getSIDouble(0); } else { surface_density_ = 1000.0; OPM_MESSAGE("Input is missing DENSITY -- using a standard density of " << surface_density_ << ".\n"); } // This will be modified if we have a PVCDO specification. reservoir_density_ = surface_density_; if (deck->hasKeyword("PVCDO")) { Opm::DeckRecordConstPtr pvcdoRecord = deck->getKeyword("PVCDO")->getRecord(0); if (pvcdoRecord->getItem("OIL_COMPRESSIBILITY")->getSIDouble(0) != 0.0 || pvcdoRecord->getItem("OIL_VISCOSIBILITY")->getSIDouble(0) != 0.0) { OPM_MESSAGE("Compressibility effects in PVCDO are ignored."); } reservoir_density_ /= pvcdoRecord->getItem("OIL_VOL_FACTOR")->getSIDouble(0); viscosity_ = pvcdoRecord->getItem("OIL_VISCOSITY")->getSIDouble(0); } else { viscosity_ = 1.0 * prefix::centi*unit::Poise; OPM_MESSAGE("Input is missing PVCDO -- using a standard viscosity of " << viscosity_ << " and reservoir density equal to surface density.\n"); } }
RockCompressibility::RockCompressibility(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState) : pref_(0.0), rock_comp_(0.0) { const auto tables = eclipseState->getTableManager(); const auto& rocktabTables = tables->getRocktabTables(); if (rocktabTables.size() > 0) { const auto& rocktabTable = rocktabTables.getTable<RocktabTable>(0); if (rocktabTables.size() != 1) OPM_THROW(std::runtime_error, "Can only handle a single region in ROCKTAB."); p_ = rocktabTable.getColumn("PO").vectorCopy( ); poromult_ = rocktabTable.getColumn("PV_MULT").vectorCopy(); if (rocktabTable.hasColumn("PV_MULT_TRAN")) { transmult_ = rocktabTable.getColumn("PV_MULT_TRAN").vectorCopy(); } else { transmult_ = rocktabTable.getColumn("PV_MULT_TRANX").vectorCopy(); } } else if (deck->hasKeyword("ROCK")) { Opm::DeckKeywordConstPtr rockKeyword = deck->getKeyword("ROCK"); if (rockKeyword->size() != 1) { // here it would be better not to use std::cout directly but to add the // warning to some "warning list"... std::cout << "Can only handle a single region in ROCK ("<<rockKeyword->size()<<" regions specified)." << " Ignoring all except for the first.\n"; } pref_ = rockKeyword->getRecord(0)->getItem("PREF")->getSIDouble(0); rock_comp_ = rockKeyword->getRecord(0)->getItem("COMPRESSIBILITY")->getSIDouble(0); } else { std::cout << "**** warning: no rock compressibility data found in deck (ROCK or ROCKTAB)." << std::endl; } }
void filterDoubleField(const std::string& keyword, std::vector<double>& output_field) { if (deck_->hasKeyword(keyword)) { const std::vector<double>& field = deck_->getKeyword(keyword)->getRawDoubleData(); filterField(field, output_field); } }
/// Looks at presence of WATER, OIL and GAS keywords in deck /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(Opm::DeckConstPtr deck) { PhaseUsage pu; std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); // Discover phase usage. if (deck->hasKeyword("WATER")) { pu.phase_used[BlackoilPhases::Aqua] = 1; } if (deck->hasKeyword("OIL")) { pu.phase_used[BlackoilPhases::Liquid] = 1; } if (deck->hasKeyword("GAS")) { pu.phase_used[BlackoilPhases::Vapour] = 1; } pu.num_phases = 0; for (int i = 0; i < BlackoilPhases::MaxNumPhases; ++i) { pu.phase_pos[i] = pu.num_phases; pu.num_phases += pu.phase_used[i]; } // Only 2 or 3 phase systems handled. if (pu.num_phases < 2 || pu.num_phases > 3) { OPM_THROW(std::runtime_error, "Cannot handle cases with " << pu.num_phases << " phases."); } // We need oil systems, since we do not support the keywords needed for // water-gas systems. if (!pu.phase_used[BlackoilPhases::Liquid]) { OPM_THROW(std::runtime_error, "Cannot handle cases with no OIL, i.e. water-gas systems."); } return pu; }
/// set the tables which specify the temperature dependence of the water viscosity void initFromDeck(std::shared_ptr<const PvtInterface> isothermalPvt, Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState) { isothermalPvt_ = isothermalPvt; watvisctTables_ = 0; // stuff which we need to get from the PVTW keyword const auto& pvtwKeyword = deck->getKeyword("PVTW"); int numRegions = pvtwKeyword.size(); pvtwRefPress_.resize(numRegions); pvtwRefB_.resize(numRegions); pvtwCompressibility_.resize(numRegions); pvtwViscosity_.resize(numRegions); pvtwViscosibility_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { const auto& pvtwRecord = pvtwKeyword.getRecord(regionIdx); pvtwRefPress_[regionIdx] = pvtwRecord.getItem("P_REF").getSIDouble(0); pvtwRefB_[regionIdx] = pvtwRecord.getItem("WATER_VOL_FACTOR").getSIDouble(0); pvtwViscosity_[regionIdx] = pvtwRecord.getItem("WATER_VISCOSITY").getSIDouble(0); pvtwViscosibility_[regionIdx] = pvtwRecord.getItem("WATER_VISCOSIBILITY").getSIDouble(0); } // quantities required for the temperature dependence of the viscosity // (basically we expect well-behaved VISCREF and WATVISCT keywords.) if (deck->hasKeyword("VISCREF")) { auto tables = eclipseState->getTableManager(); watvisctTables_ = &tables->getWatvisctTables(); const auto& viscrefKeyword = deck->getKeyword("VISCREF"); assert(int(watvisctTables_->size()) == numRegions); assert(int(viscrefKeyword.size()) == numRegions); viscrefPress_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { const auto& viscrefRecord = viscrefKeyword.getRecord(regionIdx); viscrefPress_[regionIdx] = viscrefRecord.getItem("REFERENCE_PRESSURE").getSIDouble(0); } } // quantities required for the temperature dependence of the density if (deck->hasKeyword("WATDENT")) { const auto& watdentKeyword = deck->getKeyword("WATDENT"); assert(int(watdentKeyword.size()) == numRegions); watdentRefTemp_.resize(numRegions); watdentCT1_.resize(numRegions); watdentCT2_.resize(numRegions); for (int 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); } } }
void Rock<dim>::assignPorosity(Opm::DeckConstPtr deck, const std::vector<int>& global_cell) { porosity_.assign(global_cell.size(), 1.0); if (deck->hasKeyword("PORO")) { const std::vector<double>& poro = deck->getKeyword("PORO").getSIDoubleData(); for (int c = 0; c < int(porosity_.size()); ++c) { porosity_[c] = poro[global_cell[c]]; } } }
void PolymerInflowFromDeck::setInflowValues(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState, size_t currentStep) { Opm::DeckKeywordConstPtr keyword = deck->getKeyword("WPOLYMER"); // Schedule schedule(deck); ScheduleConstPtr schedule = eclipseState->getSchedule(); for (size_t recordNr = 0; recordNr < keyword->size(); recordNr++) { DeckRecordConstPtr record = keyword->getRecord(recordNr); const std::string& wellNamesPattern = record->getItem("WELL")->getTrimmedString(0); std::string wellName = record->getItem("WELL")->getTrimmedString(0); std::vector<WellPtr> wells = schedule->getWells(wellNamesPattern); for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter) { WellPtr well = *wellIter; WellInjectionProperties injection = well->getInjectionProperties(currentStep); if (injection.injectorType == WellInjector::WATER) { WellPolymerProperties polymer = well->getPolymerProperties(currentStep); wellPolymerRate_.insert(std::make_pair(wellName, polymer.m_polymerConcentration)); } else { OPM_THROW(std::logic_error, "For polymer injector you must have a water injector"); } } } }
void GridManager::createGrdecl(Opm::DeckConstPtr deck, struct grdecl &grdecl) { // Extract data from deck. const std::vector<double>& zcorn = deck->getKeyword("ZCORN").getSIDoubleData(); const std::vector<double>& coord = deck->getKeyword("COORD").getSIDoubleData(); const int* actnum = NULL; if (deck->hasKeyword("ACTNUM")) { actnum = &(deck->getKeyword("ACTNUM").getIntData()[0]); } std::array<int, 3> dims; if (deck->hasKeyword("DIMENS")) { const auto& dimensKeyword = deck->getKeyword("DIMENS"); dims[0] = dimensKeyword.getRecord(0).getItem(0).get< int >(0); dims[1] = dimensKeyword.getRecord(0).getItem(1).get< int >(0); dims[2] = dimensKeyword.getRecord(0).getItem(2).get< int >(0); } else if (deck->hasKeyword("SPECGRID")) { const auto& specgridKeyword = deck->getKeyword("SPECGRID"); dims[0] = specgridKeyword.getRecord(0).getItem(0).get< int >(0); dims[1] = specgridKeyword.getRecord(0).getItem(1).get< int >(0); dims[2] = specgridKeyword.getRecord(0).getItem(2).get< int >(0); } else { OPM_THROW(std::runtime_error, "Deck must have either DIMENS or SPECGRID."); } // Collect in input struct for preprocessing. grdecl.zcorn = &zcorn[0]; grdecl.coord = &coord[0]; grdecl.actnum = actnum; grdecl.dims[0] = dims[0]; grdecl.dims[1] = dims[1]; grdecl.dims[2] = dims[2]; if (deck->hasKeyword("MAPAXES")) { const auto& mapaxesKeyword = deck->getKeyword("MAPAXES"); const auto& mapaxesRecord = mapaxesKeyword.getRecord(0); // memleak alert: here we need to make sure that C code // can properly take ownership of the grdecl.mapaxes // object. if it is not freed, it will result in a // memleak... double *cWtfMapaxes = static_cast<double*>(malloc(sizeof(double)*mapaxesRecord.size())); for (unsigned i = 0; i < mapaxesRecord.size(); ++i) cWtfMapaxes[i] = mapaxesRecord.getItem(i).getSIDouble(0); grdecl.mapaxes = cWtfMapaxes; } else grdecl.mapaxes = NULL; }
/// Constructor. /// @param[in] deck Input deck expected to contain WPOLYMER. PolymerInflowFromDeck::PolymerInflowFromDeck(Opm::DeckConstPtr deck, const Wells& wells, const int num_cells) : sparse_inflow_(num_cells) { if (!deck->hasKeyword("WPOLYMER")) { OPM_MESSAGE("PolymerInflowFromDeck initialized without WPOLYMER in current epoch."); return; } // Extract concentrations and put into cell->concentration map. Opm::DeckKeywordConstPtr wpolymerKeyword = deck->getKeyword("WPOLYMER"); const int num_wpl = wpolymerKeyword->size(); std::map<int, double> perfcell_conc; for (int i = 0; i < num_wpl; ++i) { // Only use well name and polymer concentration. // That is, we ignore salt concentration and group // names. int wix = 0; for (; wix < wells.number_of_wells; ++wix) { if (wpolymerKeyword->getRecord(i)->getItem("WELL")->getString(0) == wells.name[wix]) { break; } } if (wix == wells.number_of_wells) { OPM_THROW(std::runtime_error, "Could not find a match for well " << wpolymerKeyword->getRecord(i)->getItem("WELL")->getString(0) << " from WPOLYMER."); } for (int j = wells.well_connpos[wix]; j < wells.well_connpos[wix+1]; ++j) { const int perf_cell = wells.well_cells[j]; perfcell_conc[perf_cell] = wpolymerKeyword->getRecord(i)->getItem("POLYMER_CONCENTRATION")->getSIDouble(0); } } // Build sparse vector from map. std::map<int, double>::const_iterator it = perfcell_conc.begin(); for (; it != perfcell_conc.end(); ++it) { sparse_inflow_.addElement(it->second, it->first); } }
std::vector<double> getMapaxesValues(Opm::DeckConstPtr deck) { const auto& mapaxesRecord = deck->getKeyword("MAPAXES").getRecord(0); std::vector<double> result; for (size_t itemIdx = 0; itemIdx < mapaxesRecord.size(); ++itemIdx) { const auto& curItem = mapaxesRecord.getItem(itemIdx); for (size_t dataItemIdx = 0; dataItemIdx < curItem.size(); ++dataItemIdx) { result.push_back(curItem.get< double >(dataItemIdx)); } } return result; }
std::vector<double> getMapaxesValues(Opm::DeckConstPtr deck) { Opm::DeckRecordConstPtr mapaxesRecord = deck->getKeyword("MAPAXES")->getRecord(0); std::vector<double> result; for (size_t itemIdx = 0; itemIdx < mapaxesRecord->size(); ++itemIdx) { Opm::DeckItemConstPtr curItem = mapaxesRecord->getItem(itemIdx); for (size_t dataItemIdx = 0; dataItemIdx < curItem->size(); ++dataItemIdx) { result.push_back(curItem->getRawDouble(dataItemIdx)); } } return result; }
TimeMap::TimeMap(Opm::DeckConstPtr deck) { // The default start date is not specified in the Eclipse // reference manual. We hence just assume it is same as for // the START keyword for Eclipse R100, i.e., January 1st, // 1983... boost::posix_time::ptime startTime(boost::gregorian::date(1983, 1, 1)); // use the 'START' keyword to find out the start date (if the // keyword was specified) if (deck->hasKeyword("START")) { Opm::DeckKeywordConstPtr keyword = deck->getKeyword("START"); startTime = timeFromEclipse(keyword->getRecord(/*index=*/0)); } m_timeList.push_back( startTime ); // find all "TSTEP" and "DATES" keywords in the deck and deal // with them one after another size_t numKeywords = deck->size(); for (size_t keywordIdx = 0; keywordIdx < numKeywords; ++keywordIdx) { Opm::DeckKeywordConstPtr keyword = deck->getKeyword(keywordIdx); // We're only interested in "TSTEP" and "DATES" keywords, // so we ignore everything else here... if (keyword->name() != "TSTEP" && keyword->name() != "DATES") { continue; } if (keyword->name() == "TSTEP") addFromTSTEPKeyword(keyword); else if (keyword->name() == "DATES") addFromDATESKeyword(keyword); } }
/// Mirror keyword SPECGRID in deck void mirror_specgrid(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) { // We only need to multiply the dimension by 2 in the correct direction. Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0); std::vector<int> dimensions(3); dimensions[0] = specgridRecord->getItem("NX")->getInt(0); dimensions[1] = specgridRecord->getItem("NY")->getInt(0); dimensions[2] = specgridRecord->getItem("NZ")->getInt(0); if (direction == "x") {dimensions[0] *= 2;} else if (direction == "y") {dimensions[1] *= 2;} else {std::cerr << "Direction should be either x or y" << std::endl; exit(1);} out << "SPECGRID" << std::endl << dimensions[0] << " " << dimensions[1] << " " << dimensions[2] << " " << specgridRecord->getItem("NUMRES")->getInt(0) << " " << specgridRecord->getItem("COORD_TYPE")->getString(0) << " " << std::endl << "/" << std::endl << std::endl; }
/// Mirror keyword MAPAXES in deck void mirror_mapaxes(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) { // Assumes axis aligned with x/y-direction std::cout << "Warning: Keyword MAPAXES not fully understood. Result should be verified manually." << std::endl; if (deck->hasKeyword("MAPAXES")) { std::vector<double> mapaxes = getMapaxesValues(deck); std::vector<double> mapaxes_mirrored = mapaxes; // Double the length of the coordinate axis if (direction == "x") { mapaxes_mirrored[4] = (mapaxes[4]-mapaxes[2])*2 + mapaxes[2]; } else if (direction == "y") { mapaxes_mirrored[1] = (mapaxes[1]-mapaxes[3])*2 + mapaxes[3]; } printKeywordValues(out, "MAPAXES", mapaxes_mirrored, 2); } }
/*! * \brief Reads all relevant material parameters form a cell of a parsed ECL deck. * * This requires that the opm-parser module is available. */ void initFromDeck(Opm::DeckConstPtr deck) { enableHysteresis_ = false; if (!deck->hasKeyword("SATOPTS")) return; Opm::DeckItemConstPtr satoptsItem = deck->getKeyword("SATOPTS")->getRecord(0)->getItem(0); for (unsigned i = 0; i < satoptsItem->size(); ++i) { std::string satoptsValue = satoptsItem->getString(0); std::transform(satoptsValue.begin(), satoptsValue.end(), satoptsValue.begin(), ::toupper); if (satoptsValue == "HYSTER") enableHysteresis_ = true; } // check for the (deprecated) HYST keyword if (deck->hasKeyword("HYST")) enableHysteresis_ = true; if (!enableHysteresis_) return; if (!deck->hasKeyword("EHYSTR")) OPM_THROW(std::runtime_error, "Enabling hysteresis via the HYST parameter for SATOPTS requires the " "presence of the EHYSTR keyword"); Opm::DeckKeywordConstPtr ehystrKeyword = deck->getKeyword("EHYSTR"); if (deck->hasKeyword("NOHYKR")) krHysteresisModel_ = -1; else { krHysteresisModel_ = ehystrKeyword->getRecord(0)->getItem("relative_perm_hyst")->getInt(0); if (krHysteresisModel_ != 0) OPM_THROW(std::runtime_error, "Only the Carlson kr hystersis model (indicated by a 0 on the second item" " of the 'EHYSTR' keyword) is supported"); } if (deck->hasKeyword("NOHYPC")) pcHysteresisModel_ = -1; else { // if capillary pressure hysteresis is enabled, Eclipse always uses the // Killough model pcHysteresisModel_ = 0; } }
void BlackoilPVT::init(Opm::DeckConstPtr deck) { Opm::ParseMode parseMode; const auto eclipseState = std::make_shared<EclipseState>(deck , parseMode); region_number_ = 0; // Surface densities. Accounting for different orders in eclipse and our code. if (deck->hasKeyword("DENSITY")) { Opm::DeckRecordConstPtr densityRecord = deck->getKeyword("DENSITY")->getRecord(/*regionIdx=*/0); densities_[Aqua] = densityRecord->getItem("WATER")->getSIDouble(0); densities_[Vapour] = densityRecord->getItem("GAS")->getSIDouble(0); densities_[Liquid] = densityRecord->getItem("OIL")->getSIDouble(0); } else { OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); } // Water PVT if (deck->hasKeyword("PVTW")) { water_props_.reset(new MiscibilityWater(deck->getKeyword("PVTW"))); } else { water_props_.reset(new MiscibilityWater(0.5*Opm::prefix::centi*Opm::unit::Poise)); // Eclipse 100 default } // Oil PVT const auto& tables = eclipseState->getTableManager(); const auto& pvdoTables = tables->getPvdoTables(); const auto& pvtoTables = tables->getPvtoTables(); if (!pvdoTables.empty()) { const auto& pvdoTable = pvdoTables.getTable<PvdoTable>(0); oil_props_.reset(new MiscibilityDead(pvdoTable)); } else if (pvtoTables.empty()) { // PVTOTables is a std::vector<> const auto& pvtoTable = pvtoTables[0]; oil_props_.reset(new MiscibilityLiveOil(pvtoTable)); } else if (deck->hasKeyword("PVCDO")) { auto *misc_water = new MiscibilityWater(0); misc_water->initFromPvcdo(deck->getKeyword("PVCDO")); oil_props_.reset(misc_water); } else { OPM_THROW(std::runtime_error, "Input is missing PVDO and PVTO\n"); } // Gas PVT const auto& pvdgTables = tables->getPvdgTables(); const auto& pvtgTables = tables->getPvtgTables(); if (!pvdgTables.empty()) { const auto& pvdgTable = pvdgTables.getTable<PvdgTable>(0); gas_props_.reset(new MiscibilityDead(pvdgTable)); } else if (pvtgTables.empty()) { gas_props_.reset(new MiscibilityLiveGas(pvtgTables[0])); } else { OPM_THROW(std::runtime_error, "Input is missing PVDG and PVTG\n"); } }
void PvtPropertiesIncompFromDeck::init(Opm::DeckConstPtr deck) { // So far, this class only supports a single PVT region. TODO? int region_number = 0; PhaseUsage phase_usage = phaseUsageFromDeck(deck); if (phase_usage.phase_used[PhaseUsage::Vapour] || !phase_usage.phase_used[PhaseUsage::Aqua] || !phase_usage.phase_used[PhaseUsage::Liquid]) { OPM_THROW(std::runtime_error, "PvtPropertiesIncompFromDeck::init() -- must have gas and oil phases (only) in deck input.\n"); } // Surface densities. Accounting for different orders in eclipse and our code. if (deck->hasKeyword("DENSITY")) { const auto& densityRecord = deck->getKeyword("DENSITY").getRecord(region_number); surface_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] = densityRecord.getItem("OIL").getSIDouble(0); surface_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] = densityRecord.getItem("WATER").getSIDouble(0); } else { OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); } // Make reservoir densities the same as surface densities initially. // We will modify them with formation volume factors if found. reservoir_density_ = surface_density_; // Water viscosity. if (deck->hasKeyword("PVTW")) { const auto& pvtwRecord = deck->getKeyword("PVTW").getRecord(region_number); if (pvtwRecord.getItem("WATER_COMPRESSIBILITY").getSIDouble(0) != 0.0 || pvtwRecord.getItem("WATER_VISCOSIBILITY").getSIDouble(0) != 0.0) { OPM_MESSAGE("Compressibility effects in PVTW are ignored."); } reservoir_density_[phase_usage.phase_pos[PhaseUsage::Aqua]] /= pvtwRecord.getItem("WATER_VOL_FACTOR").getSIDouble(0); viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = pvtwRecord.getItem("WATER_VISCOSITY").getSIDouble(0); } else { // Eclipse 100 default. // viscosity_[phase_usage.phase_pos[PhaseUsage::Aqua]] = 0.5*Opm::prefix::centi*Opm::unit::Poise; OPM_THROW(std::runtime_error, "Input is missing PVTW\n"); } // Oil viscosity. if (deck->hasKeyword("PVCDO")) { const auto& pvcdoRecord = deck->getKeyword("PVCDO").getRecord(region_number); if (pvcdoRecord.getItem("OIL_COMPRESSIBILITY").getSIDouble(0) != 0.0 || pvcdoRecord.getItem("OIL_VISCOSIBILITY").getSIDouble(0) != 0.0) { OPM_MESSAGE("Compressibility effects in PVCDO are ignored."); } reservoir_density_[phase_usage.phase_pos[PhaseUsage::Liquid]] /= pvcdoRecord.getItem("OIL_VOL_FACTOR").getSIDouble(0); viscosity_[phase_usage.phase_pos[PhaseUsage::Liquid]] = pvcdoRecord.getItem("OIL_VISCOSITY").getSIDouble(0); } else { OPM_THROW(std::runtime_error, "Input is missing PVCDO\n"); } }
/// Constructor. /// @param[in] deck Input deck expected to contain WPOLYMER. PolymerInflowFromDeck::PolymerInflowFromDeck(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState, const Wells& wells, const int num_cells, size_t currentStep) : sparse_inflow_(num_cells) { if (!deck->hasKeyword("WPOLYMER")) { OPM_MESSAGE("PolymerInflowFromDeck initialized without WPOLYMER in current epoch."); return; } setInflowValues(deck, eclipseState, currentStep); std::unordered_map<std::string, double>::const_iterator map_it; // Extract concentrations and put into cell->concentration map. std::map<int, double> perfcell_conc; for (size_t i = 0; i < wellPolymerRate_.size(); ++i) { // Only use well name and polymer concentration. // That is, we ignore salt concentration and group // names. int wix = 0; for (; wix < wells.number_of_wells; ++wix) { map_it = wellPolymerRate_.find(wells.name[wix]); if (map_it == wellPolymerRate_.end()) { OPM_THROW(std::runtime_error, "Could not find a match for well from WPOLYMER."); } else { break; } } for (int j = wells.well_connpos[wix]; j < wells.well_connpos[wix+1]; ++j) { const int perf_cell = wells.well_cells[j]; perfcell_conc[perf_cell] = map_it->second; } } // Build sparse vector from map. std::map<int, double>::const_iterator it = perfcell_conc.begin(); for (; it != perfcell_conc.end(); ++it) { sparse_inflow_.addElement(it->second, it->first); } }
inline void BlackoilPropertiesFromDeck::init(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclState, std::shared_ptr<MaterialLawManager> materialLawManager, int number_of_cells, const int* global_cell, const int* cart_dims, const parameter::ParameterGroup& param, bool init_rock) { // retrieve the cell specific PVT table index from the deck // and using the grid... extractPvtTableIndex(cellPvtRegionIdx_, eclState, number_of_cells, global_cell); if(init_rock){ rock_.init(eclState, number_of_cells, global_cell, cart_dims); } const int pvt_samples = param.getDefault("pvt_tab_size", -1); pvt_.init(deck, eclState, pvt_samples); // Unfortunate lack of pointer smartness here... std::string threephase_model = param.getDefault<std::string>("threephase_model", "gwseg"); if (deck->hasKeyword("ENDSCALE") && threephase_model != "gwseg") { OPM_THROW(std::runtime_error, "Sorry, end point scaling currently available for the 'gwseg' model only."); } SaturationPropsFromDeck* ptr = new SaturationPropsFromDeck(); ptr->init(phaseUsageFromDeck(deck), materialLawManager); satprops_.reset(ptr); if (pvt_.numPhases() != satprops_->numPhases()) { OPM_THROW(std::runtime_error, "BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); } }
void mirror_celldata(std::string keyword, Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) { if ( ! deck->hasKeyword(keyword)) { std::cout << "Ignoring keyword " << keyword << " as it was not found." << std::endl; return; } // Get data from eclipse deck Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0); std::vector<int> dimensions(3); dimensions[0] = specgridRecord->getItem("NX")->getInt(0); dimensions[1] = specgridRecord->getItem("NY")->getInt(0); dimensions[2] = specgridRecord->getItem("NZ")->getInt(0); std::vector<T> values = getKeywordValues(keyword, deck, T(0.0)); std::vector<T> values_mirrored(2*dimensions[0]*dimensions[1]*dimensions[2], 0.0); // Handle the two directions differently due to ordering of the pillars. if (direction == "x") { typename std::vector<T>::iterator it_orig = values.begin(); typename std::vector<T>::iterator it_new = values_mirrored.begin(); // Loop through each line and copy old cell data and add new (which are the old reversed) for ( ; it_orig != values.end(); it_orig += dimensions[0]) { // Copy old cell data copy(it_orig, it_orig + dimensions[0], it_new); it_new += dimensions[0]; // Add new cell data std::vector<double> next_vec(it_orig, it_orig + dimensions[0]); std::vector<double> next_reversed = next_vec; reverse(next_reversed.begin(), next_reversed.end()); copy(next_reversed.begin(), next_reversed.end(), it_new); it_new += dimensions[0]; } } else if (direction =="y") { typename std::vector<T>::iterator it_orig = values.begin(); typename std::vector<T>::iterator it_new = values_mirrored.begin(); // Entries per layer const int entries_per_layer = dimensions[0]*dimensions[1]; // Loop through each layer and copy old cell data and add new (which are the old reordered) for ( ; it_orig != values.end(); it_orig += entries_per_layer) { // Copy old cell data copy(it_orig, it_orig + entries_per_layer, it_new); it_new += entries_per_layer; // Add new cell data std::vector<T> next_vec(it_orig, it_orig + entries_per_layer); std::vector<T> next_reordered(entries_per_layer, 0.0); typename std::vector<T>::iterator it_next = next_vec.end(); typename std::vector<T>::iterator it_reordered = next_reordered.begin(); // Reorder next entries for ( ; it_reordered != next_reordered.end(); it_reordered += dimensions[0]) { copy(it_next - dimensions[0], it_next, it_reordered); it_next -= dimensions[0]; } copy(next_reordered.begin(), next_reordered.end(), it_new); it_new += entries_per_layer; } } else { std::cerr << "Direction should be either x or y" << std::endl; exit(1); } // Write new keyword values to output file printKeywordValues(out, keyword, values_mirrored, 8); }
std::vector<int> getKeywordValues(std::string keyword, Opm::DeckConstPtr deck, int dummy) { return deck->getKeyword(keyword)->getIntData(); }
std::vector<double> getKeywordValues(std::string keyword, Opm::DeckConstPtr deck, double dummy) { return deck->getKeyword(keyword)->getRawDoubleData(); }
/// Mirror keyword COORD in deck void mirror_coord(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) { // We assume uniform spacing in x and y directions and parallel top and bottom faces Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0); std::vector<int> dimensions(3); dimensions[0] = specgridRecord->getItem("NX")->getInt(0); dimensions[1] = specgridRecord->getItem("NY")->getInt(0); dimensions[2] = specgridRecord->getItem("NZ")->getInt(0); std::vector<double> coord = deck->getKeyword("COORD")->getRawDoubleData(); const int entries_per_pillar = 6; std::vector<double> coord_mirrored; // Handle the two directions differently due to ordering of the pillars. if (direction == "x") { // Total entries in mirrored ZCORN. Number of pillars times 6 const int entries = (2*dimensions[0] + 1) * (dimensions[1] + 1) * entries_per_pillar; // Entries per line in x-direction. Number of pillars in x-direction times 6 const int entries_per_line = entries_per_pillar*(dimensions[0] + 1); coord_mirrored.assign(entries, 0.0); // Distance between pillars in x-directiion const double spacing = coord[entries_per_pillar]-coord[0]; std::vector<double>::iterator it_new = coord_mirrored.begin(); std::vector<double>::iterator it_orig; // Loop through each pillar line in the x-direction for (it_orig = coord.begin(); it_orig != coord.end(); it_orig += entries_per_line) { // Copy old pillars copy(it_orig, it_orig + entries_per_line, it_new); // Add new pillars in between it_new += entries_per_line; std::vector<double> next_vec(it_orig + entries_per_line - entries_per_pillar, it_orig + entries_per_line); for (int r=0; r < dimensions[0]; ++r) { next_vec[0] += spacing; next_vec[3] += spacing; copy(next_vec.begin(), next_vec.end(), it_new); it_new += entries_per_pillar; } } } else if (direction == "y") { // Total entries in mirrored ZCORN. Number of pillars times 6 const int entries = (dimensions[0] + 1) * (2*dimensions[1] + 1) * entries_per_pillar; // Entries per line in y-direction. Number of pillars in y-direction times 6 const int entries_per_line = entries_per_pillar*(dimensions[0] + 1); coord_mirrored.assign(entries, 0.0); // Distance between pillars in y-directiion const double spacing = coord[entries_per_line + 1]-coord[1]; std::vector<double>::iterator it_new = coord_mirrored.begin(); // Copy old pillars copy(coord.begin(), coord.end(), it_new); // Add new pillars at the end it_new += coord.size(); std::vector<double> next_vec(coord.end() - entries_per_line, coord.end()); for ( ; it_new != coord_mirrored.end(); it_new += entries_per_line) { for (int i = 1; i < entries_per_line; i += 3) { next_vec[i] += spacing; } copy(next_vec.begin(), next_vec.end(), it_new); } } else { std::cerr << "Direction should be either x or y" << std::endl; exit(1); } // Write new COORD values to output file printKeywordValues(out, "COORD", coord_mirrored, 6); }
/// Mirror keyword ZCORN in deck void mirror_zcorn(Opm::DeckConstPtr deck, std::string direction, std::ofstream& out) { Opm::DeckRecordConstPtr specgridRecord = deck->getKeyword("SPECGRID")->getRecord(0); std::vector<int> dimensions(3); dimensions[0] = specgridRecord->getItem("NX")->getInt(0); dimensions[1] = specgridRecord->getItem("NY")->getInt(0); dimensions[2] = specgridRecord->getItem("NZ")->getInt(0); std::vector<double> zcorn = deck->getKeyword("ZCORN")->getRawDoubleData(); std::vector<double> zcorn_mirrored; // Handle the two directions differently due to ordering of the pillars. if (direction == "x") { // Total entries in mirrored ZCORN. Eight corners per cell. const int entries = dimensions[0]*2*dimensions[1]*dimensions[2]*8; zcorn_mirrored.assign(entries, 0.0); // Entries per line in x-direction. Two for each cell. const int entries_per_line = dimensions[0]*2; std::vector<double>::iterator it_new = zcorn_mirrored.begin(); std::vector<double>::iterator it_orig = zcorn.begin(); // Loop through each line and copy old corner-points and add new (which are the old reversed) for ( ; it_orig != zcorn.end(); it_orig += entries_per_line) { std::vector<double> next_vec(it_orig, it_orig + entries_per_line); std::vector<double> next_reversed = next_vec; reverse(next_reversed.begin(), next_reversed.end()); // Copy old corner-points copy(it_orig, it_orig + entries_per_line, it_new); it_new += entries_per_line; // Add new corner-points copy(next_reversed.begin(), next_reversed.end(), it_new); it_new += entries_per_line; } } else if (direction == "y") { // Total entries in mirrored ZCORN. Eight corners per cell. const int entries = dimensions[0]*dimensions[1]*2*dimensions[2]*8; zcorn_mirrored.assign(entries, 0.0); // Entries per line in x-direction. Two for each cell. const int entries_per_line_x = dimensions[0]*2; // Entries per layer of corner-points. Four for each cell const int entries_per_layer = dimensions[0]*dimensions[1]*4; std::vector<double>::iterator it_new = zcorn_mirrored.begin(); std::vector<double>::iterator it_orig = zcorn.begin(); // Loop through each layer and copy old corner-points and add new (which are the old reordered) for ( ; it_orig != zcorn.end(); it_orig += entries_per_layer) { // Copy old corner-points copy(it_orig, it_orig + entries_per_layer, it_new); it_new += entries_per_layer; // Add new corner-points std::vector<double> next_vec(it_orig, it_orig + entries_per_layer); std::vector<double> next_reordered(entries_per_layer, 0.0); std::vector<double>::iterator it_next = next_vec.end(); std::vector<double>::iterator it_reordered = next_reordered.begin(); // Reorder next entries for ( ; it_reordered != next_reordered.end(); it_reordered += entries_per_line_x) { copy(it_next - entries_per_line_x, it_next, it_reordered); it_next -= entries_per_line_x; } copy(next_reordered.begin(), next_reordered.end(), it_new); it_new += entries_per_layer; } } else { std::cerr << "Direction should be either x or y" << std::endl; exit(1); } // Write new ZCORN values to output file printKeywordValues(out, "ZCORN", zcorn_mirrored, 8); }
/*! * \brief Reads all relevant material parameters form a cell of a parsed ECL deck. * * This requires that the opm-parser module is available. */ void initFromDeck(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclState, Opm::EclTwoPhaseSystemType twoPhaseSystemType) { // find out if endpoint scaling is used in the first place if (!deck->hasKeyword("ENDSCALE")) { // it is not used, i.e., just set all enable$Foo attributes to 0 and be done // with it. enableSatScaling_ = false; enableThreePointKrSatScaling_ = false; enablePcScaling_ = false; enableKrwScaling_ = false; enableKrnScaling_ = false; return; } // endpoint scaling is used, i.e., at least saturation scaling needs to be enabled enableSatScaling_ = true; // check if three-point scaling is to be used for the saturations of the relative // permeabilities if (deck->hasKeyword("SCALECRS")) { // if the deck features the SCALECRS keyword, it must be set to 'YES' Opm::DeckKeywordConstPtr scalecrsKeyword = deck->getKeyword("SCALECRS"); std::string scalecrsValue = scalecrsKeyword->getRecord(0)->getItem("VALUE")->getString(0); // convert the value of the SCALECRS keyword to upper case, just to be sure std::transform(scalecrsValue.begin(), scalecrsValue.end(), scalecrsValue.begin(), ::toupper); if (scalecrsValue == "YES" || scalecrsValue == "Y") enableThreePointKrSatScaling_ = true; else enableThreePointKrSatScaling_ = false; } else enableThreePointKrSatScaling_ = false; // check if we are supposed to scale the Y axis of the capillary pressure if (twoPhaseSystemType == EclOilWaterSystem) enablePcScaling_ = eclState->hasDoubleGridProperty("PCW") || eclState->hasDoubleGridProperty("SWATINIT"); else { assert(twoPhaseSystemType == EclGasOilSystem); enablePcScaling_ = eclState->hasDoubleGridProperty("PCG"); } // check if we are supposed to scale the Y axis of the wetting phase relperm if (twoPhaseSystemType == EclOilWaterSystem) enableKrwScaling_ = eclState->hasDoubleGridProperty("KRW"); else { assert(twoPhaseSystemType == EclGasOilSystem); enableKrwScaling_ = eclState->hasDoubleGridProperty("KRO"); } // check if we are supposed to scale the Y axis of the non-wetting phase relperm if (twoPhaseSystemType == EclOilWaterSystem) enableKrnScaling_ = eclState->hasDoubleGridProperty("KRO"); else { assert(twoPhaseSystemType == EclGasOilSystem); enableKrnScaling_ = eclState->hasDoubleGridProperty("KRG"); } }
// ----------------- 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; }
void SatFuncBase<TableType>::init(Opm::DeckConstPtr newParserDeck, const int table_num, const PhaseUsage phase_usg, const int samples) { phase_usage = phase_usg; double swco = 0.0; double swmax = 1.0; if (phase_usage.phase_used[Aqua]) { Opm::SwofTable swof(newParserDeck->getKeyword("SWOF"), table_num); const std::vector<double>& sw = swof.getSwColumn(); const std::vector<double>& krw = swof.getKrwColumn(); const std::vector<double>& krow = swof.getKrowColumn(); const std::vector<double>& pcow = swof.getPcowColumn(); if (krw.front() != 0.0 || krow.back() != 0.0) { OPM_THROW(std::runtime_error, "Error SWOF data - non-zero krw(swco) and/or krow(1-sor)"); } // Extend the tables with constant values such that the // derivatives at the endpoints are zero int n = sw.size(); std::vector<double> sw_ex(n+2); std::vector<double> krw_ex(n+2); std::vector<double> krow_ex(n+2); std::vector<double> pcow_ex(n+2); extendTable(sw,sw_ex,1); extendTable(krw,krw_ex,0); extendTable(krow,krow_ex,0); extendTable(pcow,pcow_ex,0); initializeTableType(krw_,sw_ex, krw_ex, samples); initializeTableType(krow_,sw_ex, krow_ex, samples); initializeTableType(pcow_,sw_ex, pcow_ex, samples); krocw_ = krow[0]; // At connate water -> ecl. SWOF swco = sw[0]; smin_[phase_usage.phase_pos[Aqua]] = sw[0]; swmax = sw.back(); smax_[phase_usage.phase_pos[Aqua]] = sw.back(); krwmax_ = krw.back(); kromax_ = krow.front(); swcr_ = swmax; sowcr_ = 1.0 - swco; krwr_ = krw.back(); krorw_ = krow.front(); for (std::vector<double>::size_type i=1; i<sw.size(); ++i) { if (krw[i]> 0.0) { swcr_ = sw[i-1]; krorw_ = krow[i-1]; break; } } for (std::vector<double>::size_type i=sw.size()-1; i>=1; --i) { if (krow[i-1]> 0.0) { sowcr_ = 1.0 - sw[i]; krwr_ = krw[i]; break; } } } if (phase_usage.phase_used[Vapour]) { Opm::SgofTable sgof(newParserDeck->getKeyword("SGOF"), table_num); const std::vector<double>& sg = sgof.getSgColumn(); const std::vector<double>& krg = sgof.getKrgColumn(); const std::vector<double>& krog = sgof.getKrogColumn(); const std::vector<double>& pcog = sgof.getPcogColumn(); // Extend the tables with constant values such that the // derivatives at the endpoints are zero int n = sg.size(); std::vector<double> sg_ex(n+2); std::vector<double> krg_ex(n+2); std::vector<double> krog_ex(n+2); std::vector<double> pcog_ex(n+2); extendTable(sg,sg_ex,1); extendTable(krg,krg_ex,0); extendTable(krog,krog_ex,0); extendTable(pcog,pcog_ex,0); initializeTableType(krg_,sg_ex, krg_ex, samples); initializeTableType(krog_,sg_ex, krog_ex, samples); initializeTableType(pcog_,sg_ex, pcog_ex, samples); smin_[phase_usage.phase_pos[Vapour]] = sg[0]; if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { OPM_THROW(std::runtime_error, "Gas maximum saturation in SGOF table = " << sg.back() << ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); } smax_[phase_usage.phase_pos[Vapour]] = sg.back(); smin_[phase_usage.phase_pos[Vapour]] = sg.front(); krgmax_ = krg.back(); sgcr_ = sg.front(); sogcr_ = 1.0 - sg.back(); krgr_ = krg.back(); krorg_ = krg.front(); for (std::vector<double>::size_type i=1; i<sg.size(); ++i) { if (krg[i]> 0.0) { sgcr_ = sg[i-1]; krorg_ = krog[i-1]; break; } } for (std::vector<double>::size_type i=sg.size()-1; i>=1; --i) { if (krog[i-1]> 0.0) { sogcr_ = 1.0 - sg[i]; krgr_ = krg[i]; break; } } } if (phase_usage.phase_used[Vapour] && phase_usage.phase_used[Aqua]) { sowcr_ -= smin_[phase_usage.phase_pos[Vapour]]; sogcr_ -= smin_[phase_usage.phase_pos[Aqua]]; smin_[phase_usage.phase_pos[Liquid]] = 0.0; smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Aqua]] - smin_[phase_usage.phase_pos[Vapour]]; // First entry in SGOF-table supposed to be zero anyway ... } else if (phase_usage.phase_used[Aqua]) { smin_[phase_usage.phase_pos[Liquid]] = 1.0 - smax_[phase_usage.phase_pos[Aqua]]; smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Aqua]]; } else if (phase_usage.phase_used[Vapour]) { smin_[phase_usage.phase_pos[Liquid]] = 1.0 - smax_[phase_usage.phase_pos[Vapour]]; smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Vapour]]; } }
void BlackoilPvtProperties::init(Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState, int numSamples) { phase_usage_ = phaseUsageFromDeck(deck); // Surface densities. Accounting for different orders in eclipse and our code. Opm::DeckKeywordConstPtr densityKeyword = deck->getKeyword("DENSITY"); int numRegions = densityKeyword->size(); densities_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { if (phase_usage_.phase_used[Liquid]) { densities_[regionIdx][phase_usage_.phase_pos[Liquid]] = densityKeyword->getRecord(regionIdx)->getItem("OIL")->getSIDouble(0); } if (phase_usage_.phase_used[Aqua]) { densities_[regionIdx][phase_usage_.phase_pos[Aqua]] = densityKeyword->getRecord(regionIdx)->getItem("WATER")->getSIDouble(0); } if (phase_usage_.phase_used[Vapour]) { densities_[regionIdx][phase_usage_.phase_pos[Vapour]] = densityKeyword->getRecord(regionIdx)->getItem("GAS")->getSIDouble(0); } } // Resize the property objects container props_.resize(phase_usage_.num_phases); // Water PVT if (phase_usage_.phase_used[Aqua]) { // if water is used, we require the presence of the "PVTW" // keyword for now... std::shared_ptr<PvtConstCompr> pvtw(new PvtConstCompr); pvtw->initFromWater(deck->getKeyword("PVTW")); props_[phase_usage_.phase_pos[Aqua]] = pvtw; } { auto tables = eclipseState->getTableManager(); // Oil PVT if (phase_usage_.phase_used[Liquid]) { // for oil, we support the "PVDO", "PVTO" and "PVCDO" // keywords... const auto &pvdoTables = tables->getPvdoTables(); const auto &pvtoTables = tables->getPvtoTables(); if (pvdoTables.size() > 0) { if (numSamples > 0) { auto splinePvt = std::shared_ptr<PvtDeadSpline>(new PvtDeadSpline); splinePvt->initFromOil(pvdoTables, numSamples); props_[phase_usage_.phase_pos[Liquid]] = splinePvt; } else { auto deadPvt = std::shared_ptr<PvtDead>(new PvtDead); deadPvt->initFromOil(pvdoTables); props_[phase_usage_.phase_pos[Liquid]] = deadPvt; } } else if (pvtoTables.size() > 0) { props_[phase_usage_.phase_pos[Liquid]].reset(new PvtLiveOil(pvtoTables)); } else if (deck->hasKeyword("PVCDO")) { std::shared_ptr<PvtConstCompr> pvcdo(new PvtConstCompr); pvcdo->initFromOil(deck->getKeyword("PVCDO")); props_[phase_usage_.phase_pos[Liquid]] = pvcdo; } else { OPM_THROW(std::runtime_error, "Input is missing PVDO, PVCDO or PVTO\n"); } } // Gas PVT if (phase_usage_.phase_used[Vapour]) { // gas can be specified using the "PVDG" or "PVTG" keywords... const auto &pvdgTables = tables->getPvdgTables(); const auto &pvtgTables = tables->getPvtgTables(); if (pvdgTables.size() > 0) { if (numSamples > 0) { std::shared_ptr<PvtDeadSpline> splinePvt(new PvtDeadSpline); splinePvt->initFromGas(pvdgTables, numSamples); props_[phase_usage_.phase_pos[Vapour]] = splinePvt; } else { std::shared_ptr<PvtDead> deadPvt(new PvtDead); deadPvt->initFromGas(pvdgTables); props_[phase_usage_.phase_pos[Vapour]] = deadPvt; } } else if (pvtgTables.size() > 0) { props_[phase_usage_.phase_pos[Vapour]].reset(new PvtLiveGas(pvtgTables)); } else { OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n"); } } } }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; { std::string version = moduleVersionName(); std::cout << "**********************************************************************\n"; std::cout << "* *\n"; std::cout << "* This is Flow-Polymer (version " << version << ")" << std::string(18 - version.size(), ' ') << "*\n"; std::cout << "* *\n"; std::cout << "* Flow-Polymer is a simulator for fully implicit three-phase, *\n"; std::cout << "* four-component (black-oil + polymer) flow, and is part of OPM. *\n"; std::cout << "* For more information see http://opm-project.org *\n"; std::cout << "* *\n"; std::cout << "**********************************************************************\n\n"; } // Read parameters, see if a deck was specified on the command line. std::cout << "--------------- Reading parameters ---------------" << std::endl; parameter::ParameterGroup param(argc, argv, false); if (!param.unhandledArguments().empty()) { if (param.unhandledArguments().size() != 1) { OPM_THROW(std::runtime_error, "You can only specify a single input deck on the command line."); } else { param.insertParameter("deck_filename", param.unhandledArguments()[0]); } } // We must have an input deck. Grid and props will be read from that. if (!param.has("deck_filename")) { std::cerr << "This program must be run with an input deck.\n" "Specify the deck filename either\n" " a) as a command line argument by itself\n" " b) as a command line parameter with the syntax deck_filename=<path to your deck>, or\n" " c) as a parameter in a parameter file (.param or .xml) passed to the program.\n"; OPM_THROW(std::runtime_error, "Input deck required."); } std::shared_ptr<GridManager> grid; std::shared_ptr<BlackoilPropertiesFromDeck> props; std::shared_ptr<BlackoilPropsAdFromDeck> new_props; std::shared_ptr<RockCompressibility> rock_comp; PolymerBlackoilState state; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; std::string deck_filename = param.get<std::string>("deck_filename"); // Write parameters used for later reference. bool output = param.getDefault("output", true); std::string output_dir; if (output) { // Create output directory if needed. 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); } // Write simulation parameters. param.writeParam(output_dir + "/simulation.param"); } std::string logFile = output_dir + "/LOGFILE.txt"; Opm::ParserPtr parser(new Opm::Parser()); { std::shared_ptr<Opm::StreamLog> streamLog = std::make_shared<Opm::StreamLog>(logFile , Opm::Log::DefaultMessageTypes); std::shared_ptr<Opm::CounterLog> counterLog = std::make_shared<Opm::CounterLog>(Opm::Log::DefaultMessageTypes); Opm::OpmLog::addBackend( "STREAM" , streamLog ); Opm::OpmLog::addBackend( "COUNTER" , counterLog ); } Opm::DeckConstPtr deck; std::shared_ptr<EclipseState> eclipseState; Opm::ParseMode parseMode; try { deck = parser->parseFile(deck_filename , parseMode); Opm::checkDeck(deck); eclipseState.reset(new Opm::EclipseState(deck , parseMode)); } catch (const std::invalid_argument& e) { std::cerr << "Failed to create valid ECLIPSESTATE object. See logfile: " << logFile << std::endl; std::cerr << "Exception caught: " << e.what() << std::endl; return EXIT_FAILURE; } // Grid init std::vector<double> porv = eclipseState->getDoubleGridProperty("PORV")->getData(); grid.reset(new GridManager(eclipseState->getEclipseGrid(), porv)); auto &cGrid = *grid->c_grid(); const PhaseUsage pu = Opm::phaseUsageFromDeck(deck); // Rock and fluid init std::vector<int> compressedToCartesianIdx; Opm::createGlobalCellArray(*grid->c_grid(), compressedToCartesianIdx); typedef BlackoilPropsAdFromDeck::MaterialLawManager MaterialLawManager; auto materialLawManager = std::make_shared<MaterialLawManager>(); materialLawManager->initFromDeck(deck, eclipseState, compressedToCartesianIdx); props.reset(new BlackoilPropertiesFromDeck( deck, eclipseState, materialLawManager, Opm::UgGridHelpers::numCells(cGrid), Opm::UgGridHelpers::globalCell(cGrid), Opm::UgGridHelpers::cartDims(cGrid), param)); new_props.reset(new BlackoilPropsAdFromDeck(deck, eclipseState, materialLawManager, cGrid)); const bool polymer = deck->hasKeyword("POLYMER"); const bool use_wpolymer = deck->hasKeyword("WPOLYMER"); PolymerProperties polymer_props(deck, eclipseState); PolymerPropsAd polymer_props_ad(polymer_props); // 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); initBlackoilSurfvol(*grid->c_grid(), *props, state); enum { Oil = BlackoilPhases::Liquid, Gas = BlackoilPhases::Vapour }; if (pu.phase_used[Oil] && pu.phase_used[Gas]) { const int np = props->numPhases(); const int nc = grid->c_grid()->number_of_cells; for (int c = 0; c < nc; ++c) { state.gasoilratio()[c] = state.surfacevol()[c*np + pu.phase_pos[Gas]] / state.surfacevol()[c*np + pu.phase_pos[Oil]]; } } } else if (deck->hasKeyword("EQUIL") && props->numPhases() == 3) { state.init(*grid->c_grid(), props->numPhases()); const double grav = param.getDefault("gravity", unit::gravity); initStateEquil(*grid->c_grid(), *props, deck, eclipseState, grav, state); state.faceflux().resize(grid->c_grid()->number_of_faces, 0.0); } else { initBlackoilStateFromDeck(*grid->c_grid(), *props, deck, gravity[2], state); } // The capillary pressure is scaled in new_props to match the scaled capillary pressure in props. if (deck->hasKeyword("SWATINIT")) { const int nc = grid->c_grid()->number_of_cells; std::vector<int> cells(nc); for (int c = 0; c < nc; ++c) { cells[c] = c; } std::vector<double> pc = state.saturation(); props->capPress(nc, state.saturation().data(), cells.data(), pc.data(),NULL); new_props->setSwatInitScaling(state.saturation(),pc); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; // Solver for Newton iterations. std::unique_ptr<NewtonIterationBlackoilInterface> fis_solver; if (param.getDefault("use_cpr", true)) { fis_solver.reset(new NewtonIterationBlackoilCPR(param)); } else { fis_solver.reset(new NewtonIterationBlackoilSimple(param)); } Opm::ScheduleConstPtr schedule = eclipseState->getSchedule(); Opm::TimeMapConstPtr timeMap(schedule->getTimeMap()); SimulatorTimer simtimer; // initialize variables simtimer.init(timeMap); if (polymer){ if (!use_wpolymer) { OPM_MESSAGE("Warning: simulate polymer injection without WPOLYMER."); } else { if (param.has("polymer_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."); } } } else { if (use_wpolymer) { OPM_MESSAGE("Warning: use WPOLYMER in a non-polymer scenario."); } } bool use_local_perm = param.getDefault("use_local_perm", true); Opm::DerivedGeology geology(*grid->c_grid(), *new_props, eclipseState, use_local_perm, grav); std::map<std::pair<int, int>, double> maxDp; computeMaxDp(maxDp, deck, eclipseState, *grid->c_grid(), state, *props, gravity[2]); std::vector<double> threshold_pressures = thresholdPressures(deck, eclipseState, *grid->c_grid(), maxDp); Opm::BlackoilOutputWriter outputWriter(cGrid, param, eclipseState, pu, new_props->permeability()); SimulatorFullyImplicitBlackoilPolymer<UnstructuredGrid> simulator(param, *grid->c_grid(), geology, *new_props, polymer_props_ad, rock_comp->isActive() ? rock_comp.get() : 0, *fis_solver, grav, deck->hasKeyword("DISGAS"), deck->hasKeyword("VAPOIL"), polymer, deck->hasKeyword("PLYSHLOG"), deck->hasKeyword("SHRATE"), eclipseState, outputWriter, deck, threshold_pressures); if (!schedule->initOnly()){ std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; SimulatorReport fullReport = simulator.run(simtimer, state); std::cout << "\n\n================ End of simulation ===============\n\n"; fullReport.report(std::cout); if (output) { std::string filename = output_dir + "/walltime.txt"; std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out); fullReport.reportParam(tot_os); warnIfUnusedParams(param); } } else { outputWriter.writeInit( simtimer ); std::cout << "\n\n================ Simulation turned off ===============\n" << std::flush; } } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }