/// Looks at presence of WATER, OIL and GAS keywords in state object /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(Opm::EclipseStateConstPtr eclipseState) { PhaseUsage pu; std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); // Discover phase usage. if (eclipseState->hasPhase(Phase::PhaseEnum::WATER)) { pu.phase_used[BlackoilPhases::Aqua] = 1; } if (eclipseState->hasPhase(Phase::PhaseEnum::OIL)) { pu.phase_used[BlackoilPhases::Liquid] = 1; } if (eclipseState->hasPhase(Phase::PhaseEnum::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; }
void WellConnections::init(const Opm::EclipseStateConstPtr eclipseState, const std::array<int, 3>& cartesianSize, const std::vector<int>& cartesian_to_compressed) { std::vector<const Opm::Well*> wells = eclipseState->getSchedule()->getWells(); int last_time_step = eclipseState->getSchedule()->getTimeMap()->size()-1; well_indices_.resize(wells.size()); // We assume that we know all the wells. int index=0; for (const auto well : wells) { std::set<int>& well_indices = well_indices_[index]; Opm::CompletionSetConstPtr completionSet = well->getCompletions(last_time_step); for (size_t c=0; c<completionSet->size(); c++) { Opm::CompletionConstPtr completion = completionSet->get(c); int i = completion->getI(); int j = completion->getJ(); int k = completion->getK(); int cart_grid_idx = i + cartesianSize[0]*(j + cartesianSize[1]*k); int compressed_idx = cartesian_to_compressed[cart_grid_idx]; if ( compressed_idx >= 0 ) // Ignore completions in inactive cells. { well_indices.insert(compressed_idx); } } ++index; } }
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"); } } } }
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; } }
/// 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); } } }
std::unordered_set<std::string> computeDefunctWellNames(const std::vector<std::vector<int> >& wells_on_proc, const Opm::EclipseStateConstPtr eclipseState, const CollectiveCommunication<MPI_Comm>& cc, int root) { std::vector<const Opm::Well*> wells = eclipseState->getSchedule()->getWells(); std::vector<int> my_well_indices; const int well_information_tag = 267553; if( root == cc.rank() ) { std::vector<MPI_Request> reqs(cc.size(), MPI_REQUEST_NULL); my_well_indices = wells_on_proc[root]; for ( int i=0; i < cc.size(); ++i ) { if(i==root) { continue; } MPI_Isend(const_cast<int*>(wells_on_proc[i].data()), wells_on_proc[i].size(), MPI_INT, i, well_information_tag, cc, &reqs[i]); } std::vector<MPI_Status> stats(reqs.size()); MPI_Waitall(reqs.size(), reqs.data(), stats.data()); } else { MPI_Status stat; MPI_Probe(root, well_information_tag, cc, &stat); int msg_size; MPI_Get_count(&stat, MPI_INT, &msg_size); my_well_indices.resize(msg_size); MPI_Recv(my_well_indices.data(), msg_size, MPI_INT, root, well_information_tag, cc, &stat); } // Compute defunct wells in parallel run. std::vector<int> defunct_wells(wells.size(), true); for(auto well_index : my_well_indices) { defunct_wells[well_index] = false; } // We need to use well names as only they are consistent. std::unordered_set<std::string> defunct_well_names; for(auto defunct = defunct_wells.begin(); defunct != defunct_wells.end(); ++defunct) { if ( *defunct ) { defunct_well_names.insert(wells[defunct-defunct_wells.begin()]->name()); } } return defunct_well_names; }
CombinedGridWellGraph::CombinedGridWellGraph(const CpGrid& grid, const Opm::EclipseStateConstPtr eclipseState, const double* transmissibilities, bool pretendEmptyGrid) : grid_(grid), eclipseState_(eclipseState), transmissibilities_(transmissibilities) { if ( pretendEmptyGrid ) { // wellsGraph not needed return; } wellsGraph_.resize(grid.numCells()); const auto& cpgdim = grid.logicalCartesianSize(); // create compressed lookup from cartesian. std::vector<Opm::WellConstPtr> wells = eclipseState->getSchedule()->getWells(); std::vector<int> cartesian_to_compressed(cpgdim[0]*cpgdim[1]*cpgdim[2], -1); int last_time_step = eclipseState->getSchedule()->getTimeMap()->size()-1; for( int i=0; i < grid.numCells(); ++i ) { cartesian_to_compressed[grid.globalCell()[i]] = i; } // We assume that we know all the wells. for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { Opm::WellConstPtr well = (*wellIter); std::set<int> well_indices; Opm::CompletionSetConstPtr completionSet = well->getCompletions(last_time_step); for (size_t c=0; c<completionSet->size(); c++) { Opm::CompletionConstPtr completion = completionSet->get(c); int i = completion->getI(); int j = completion->getJ(); int k = completion->getK(); int cart_grid_idx = i + cpgdim[0]*(j + cpgdim[1]*k); int compressed_idx = cartesian_to_compressed[cart_grid_idx]; if ( compressed_idx >= 0 ) // Ignore completions in inactive cells. { well_indices.insert(compressed_idx); } } addCompletionSetToGraph(well_indices); } }
void SolventPropsAdFromDeck::extractTableIndex(const std::string& keyword, Opm::EclipseStateConstPtr eclState, size_t numCompressed, const int* compressedToCartesianCellIdx, std::vector<int>& tableIdx) const { //Get the Region data const std::vector<int>& regionData = eclState->getIntGridProperty(keyword)->getData(); // Convert this into an array of compressed cells // Eclipse uses Fortran-style indices which start at 1 // instead of 0, we subtract 1. tableIdx.resize(numCompressed); for (size_t cellIdx = 0; cellIdx < numCompressed; ++ cellIdx) { size_t cartesianCellIdx = compressedToCartesianCellIdx ? compressedToCartesianCellIdx[cellIdx]:cellIdx; assert(cartesianCellIdx < regionData.size()); tableIdx[cellIdx] = regionData[cartesianCellIdx] - 1; } }
void extractPvtTableIndex(std::vector<int> &pvtTableIdx, Opm::EclipseStateConstPtr eclState, size_t numCompressed, const int *compressedToCartesianCellIdx) { //Get the PVTNUM data const std::vector<int>& pvtnumData = eclState->getIntGridProperty("PVTNUM")->getData(); // Convert this into an array of compressed cells // Eclipse uses Fortran-style indices which start at 1 // instead of 0, we subtract 1. pvtTableIdx.resize(numCompressed); for (size_t cellIdx = 0; cellIdx < numCompressed; ++ cellIdx) { size_t cartesianCellIdx = compressedToCartesianCellIdx ? compressedToCartesianCellIdx[cellIdx]:cellIdx; assert(cartesianCellIdx < pvtnumData.size()); pvtTableIdx[cellIdx] = pvtnumData[cartesianCellIdx] - 1; } }
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"); } } } }
void WellsManager::init(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, int number_of_cells, const int* global_cell, const int* cart_dims, int dimensions, const C2F& cell_to_faces, FC begin_face_centroids, const double* permeability) { if (dimensions != 3) { OPM_THROW(std::runtime_error, "We cannot initialize wells from a deck unless " "the corresponding grid is 3-dimensional."); } if (eclipseState->getSchedule()->numWells() == 0) { OPM_MESSAGE("No wells specified in Schedule section, " "initializing no wells"); return; } std::map<int,int> cartesian_to_compressed; setupCompressedToCartesian(global_cell, number_of_cells, cartesian_to_compressed); // Obtain phase usage data. PhaseUsage pu = phaseUsageFromDeck(eclipseState); // These data structures will be filled in this constructor, // then used to initialize the Wells struct. std::vector<std::string> well_names; std::vector<WellData> well_data; // For easy lookup: std::map<std::string, int> well_names_to_index; ScheduleConstPtr schedule = eclipseState->getSchedule(); std::vector<WellConstPtr> wells = schedule->getWells(timeStep); std::vector<int> wells_on_proc; well_names.reserve(wells.size()); well_data.reserve(wells.size()); typedef GridPropertyAccess::ArrayPolicy::ExtractFromDeck<double> DoubleArray; typedef GridPropertyAccess::Compressed<DoubleArray, GridPropertyAccess::Tag::NTG> NTGArray; DoubleArray ntg_glob(eclipseState, "NTG", 1.0); NTGArray ntg(ntg_glob, global_cell); EclipseGridConstPtr eclGrid = eclipseState->getEclipseGrid(); // use cell thickness (dz) from eclGrid // dz overwrites values calculated by WellDetails::getCubeDim std::vector<double> dz(number_of_cells); { std::vector<int> gc = compressedToCartesian(number_of_cells, global_cell); for (int cell = 0; cell < number_of_cells; ++cell) { dz[cell] = eclGrid->getCellThicknes(gc[cell]); } } createWellsFromSpecs(wells, timeStep, cell_to_faces, cart_dims, begin_face_centroids, dimensions, dz, well_names, well_data, well_names_to_index, pu, cartesian_to_compressed, permeability, ntg, wells_on_proc); setupWellControls(wells, timeStep, well_names, pu, wells_on_proc); { GroupTreeNodeConstPtr fieldNode = schedule->getGroupTree(timeStep)->getNode("FIELD"); GroupConstPtr fieldGroup = schedule->getGroup(fieldNode->name()); well_collection_.addField(fieldGroup, timeStep, pu); addChildGroups(fieldNode, schedule, timeStep, pu); } for (auto w = wells.begin(), e = wells.end(); w != e; ++w) { well_collection_.addWell(*w, timeStep, pu); } well_collection_.setWellsPointer(w_); well_collection_.applyGroupControls(); setupGuideRates(wells, timeStep, well_data, well_names_to_index); // Debug output. #define EXTRA_OUTPUT #ifdef EXTRA_OUTPUT /* std::cout << "\t WELL DATA" << std::endl; for(int i = 0; i< num_wells; ++i) { std::cout << i << ": " << well_data[i].type << " " << well_data[i].control << " " << well_data[i].target << std::endl; } std::cout << "\n\t PERF DATA" << std::endl; for(int i=0; i< int(wellperf_data.size()); ++i) { for(int j=0; j< int(wellperf_data[i].size()); ++j) { std::cout << i << ": " << wellperf_data[i][j].cell << " " << wellperf_data[i][j].well_index << std::endl; } } */ #endif }
/*! * \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"); } }
void SimulatorFullyImplicitBlackoilPolymer<GridT>:: computeRepRadiusPerfLength(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, const GridT& grid, std::vector<double>& wells_rep_radius, std::vector<double>& wells_perf_length, std::vector<double>& wells_bore_diameter) { // TODO, the function does not work for parallel running // to be fixed later. int number_of_cells = Opm::UgGridHelpers::numCells(grid); const int* global_cell = Opm::UgGridHelpers::globalCell(grid); const int* cart_dims = Opm::UgGridHelpers::cartDims(grid); auto cell_to_faces = Opm::UgGridHelpers::cell2Faces(grid); auto begin_face_centroids = Opm::UgGridHelpers::beginFaceCentroids(grid); if (eclipseState->getSchedule()->numWells() == 0) { OPM_MESSAGE("No wells specified in Schedule section, " "initializing no wells"); return; } const size_t n_perf = wells_rep_radius.size(); wells_rep_radius.clear(); wells_perf_length.clear(); wells_bore_diameter.clear(); wells_rep_radius.reserve(n_perf); wells_perf_length.reserve(n_perf); wells_bore_diameter.reserve(n_perf); std::map<int,int> cartesian_to_compressed; setupCompressedToCartesian(global_cell, number_of_cells, cartesian_to_compressed); ScheduleConstPtr schedule = eclipseState->getSchedule(); std::vector<WellConstPtr> wells = schedule->getWells(timeStep); int well_index = 0; for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); if (well->getStatus(timeStep) == WellCommon::SHUT) { continue; } { // COMPDAT handling CompletionSetConstPtr completionSet = well->getCompletions(timeStep); for (size_t c=0; c<completionSet->size(); c++) { CompletionConstPtr completion = completionSet->get(c); if (completion->getState() == WellCompletion::OPEN) { int i = completion->getI(); int j = completion->getJ(); int k = completion->getK(); const int* cpgdim = cart_dims; int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k); std::map<int, int>::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); if (cgit == cartesian_to_compressed.end()) { OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' << k << " not found in grid (well = " << well->name() << ')'); } int cell = cgit->second; { double radius = 0.5*completion->getDiameter(); if (radius <= 0.0) { radius = 0.5*unit::feet; OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); } const std::array<double, 3> cubical = WellsManagerDetail::getCubeDim<3>(cell_to_faces, begin_face_centroids, cell); WellCompletion::DirectionEnum direction = completion->getDirection(); double re; // area equivalent radius of the grid block double perf_length; // the length of the well perforation switch (direction) { case Opm::WellCompletion::DirectionEnum::X: re = std::sqrt(cubical[1] * cubical[2] / M_PI); perf_length = cubical[0]; break; case Opm::WellCompletion::DirectionEnum::Y: re = std::sqrt(cubical[0] * cubical[2] / M_PI); perf_length = cubical[1]; break; case Opm::WellCompletion::DirectionEnum::Z: re = std::sqrt(cubical[0] * cubical[1] / M_PI); perf_length = cubical[2]; break; default: OPM_THROW(std::runtime_error, " Dirtecion of well is not supported "); } double repR = std::sqrt(re * radius); wells_rep_radius.push_back(repR); wells_perf_length.push_back(perf_length); wells_bore_diameter.push_back(2. * radius); } } else { if (completion->getState() != WellCompletion::SHUT) { OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion->getState() ) << " not handled"); } } } } well_index++; } }
std::vector<std::vector<int> > postProcessPartitioningForWells(std::vector<int>& parts, const Opm::EclipseStateConstPtr eclipseState, const WellConnections& well_connections, std::size_t no_procs) { std::vector<const Opm::Well*> wells = eclipseState->getSchedule()->getWells(); // Contains for each process the indices of the wells assigned to it. std::vector<std::vector<int> > well_indices_on_proc(no_procs); if( ! well_connections.size() ) { // No wells to be processed return well_indices_on_proc; } // prevent memory allocation for(auto& well_indices : well_indices_on_proc) { well_indices.reserve(wells.size()); } // Check that all completions of a well have ended up on one process. // If that is not the case for well then move them manually to the // process that already has the most completions on it. int well_index = 0; for (const auto* well: wells) { const auto& well_completions = well_connections[well_index]; std::map<int,std::size_t> no_completions_on_proc; for ( auto completion_index: well_completions ) { ++no_completions_on_proc[parts[completion_index]]; } int owner = no_completions_on_proc.begin()->first; if ( no_completions_on_proc.size() > 1 ) { // partition with the most completions on it becomes new owner int new_owner = std::max_element(no_completions_on_proc.begin(), no_completions_on_proc.end(), [](const std::pair<int,std::size_t>& p1, const std::pair<int,std::size_t>& p2){ return ( p1.second > p2.second ); })->first; std::cout << "Manually moving well " << well->name() << " to partition " << new_owner << std::endl; for ( auto completion_cell : well_completions ) { parts[completion_cell] = new_owner; } owner = new_owner; } well_indices_on_proc[owner].push_back(well_index); ++well_index; } return well_indices_on_proc; }