void WellCollection::addWell(WellConstPtr wellChild, size_t timeStep, const PhaseUsage& phaseUsage) { WellsGroupInterface* parent = findNode(wellChild->getGroupName(timeStep)); if (!parent) { OPM_THROW(std::runtime_error, "Trying to add well " << wellChild->name() << " Step: " << boost::lexical_cast<std::string>(timeStep) << " to group named " << wellChild->getGroupName(timeStep) << ", but this group does not exist in the WellCollection."); } std::shared_ptr<WellsGroupInterface> child = createWellWellsGroup(wellChild, timeStep, phaseUsage); WellsGroup* parent_as_group = static_cast<WellsGroup*> (parent); if (!parent_as_group) { OPM_THROW(std::runtime_error, "Trying to add well to group named " << wellChild->getGroupName(timeStep) << ", but it's not a group."); } parent_as_group->addChild(child); leaf_nodes_.push_back(static_cast<WellNode*>(child.get())); child->setParent(parent); }
inline bool is_resv(const WellMap& wmap, const std::string& name, const std::size_t step) { bool match = false; WellMap::const_iterator i = wmap.find(name); if (i != wmap.end()) { WellConstPtr wp = i->second; match = (wp->isProducer(step) && wp->getProductionProperties(step) .hasProductionControl(WellProducer::RESV)) || (wp->isInjector(step) && wp->getInjectionProperties(step) .hasInjectionControl(WellInjector::RESV)); } return match; }
void EclipseWriteRFTHandler::writeTimeStep(const std::string& filename, const ert_ecl_unit_enum ecl_unit, const SimulatorTimerInterface& simulatorTimer, std::vector<WellConstPtr>& wells, EclipseGridConstPtr eclipseGrid, std::vector<double>& pressure, std::vector<double>& swat, std::vector<double>& sgas) { std::vector<ecl_rft_node_type *> rft_nodes; for (std::vector<WellConstPtr>::const_iterator ci = wells.begin(); ci != wells.end(); ++ci) { WellConstPtr well = *ci; if ((well->getRFTActive(simulatorTimer.currentStepNum())) || (well->getPLTActive(simulatorTimer.currentStepNum()))) { ecl_rft_node_type * ecl_node = createEclRFTNode(well, simulatorTimer, eclipseGrid, pressure, swat, sgas); if (well->getPLTActive(simulatorTimer.currentStepNum())) { std::cerr << "PLT not supported, writing RFT data" << std::endl; } rft_nodes.push_back(ecl_node); } } if (rft_nodes.size() > 0) { ecl_rft_file_update(filename.c_str(), rft_nodes.data(), rft_nodes.size(), ecl_unit); } //Cleanup: The ecl_rft_file_update method takes care of freeing the ecl_rft_nodes that it receives. // Each ecl_rft_node is again responsible for freeing it's cells. }
void WellsManager::createWellsFromSpecs(std::vector<WellConstPtr>& wells, size_t timeStep, const C2F& c2f, const int* cart_dims, FC begin_face_centroids, int dimensions, std::vector<double>& dz, std::vector<std::string>& well_names, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index, const PhaseUsage& phaseUsage, const std::map<int,int>& cartesian_to_compressed, const double* permeability, const NTG& ntg, std::vector<int>& wells_on_proc) { if (dimensions != 3) { OPM_THROW(std::domain_error, "WellsManager::createWellsFromSpecs() only " "supported in three space dimensions"); } std::vector<std::vector<PerfData> > wellperf_data; wellperf_data.resize(wells.size()); wells_on_proc.resize(wells.size(), 1); 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); // shut completions and open ones stored in this process will have 1 others 0. std::vector<std::size_t> completion_on_proc(completionSet->size(), 1); std::size_t shut_completions_number = 0; 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()) { if ( is_parallel_run_ ) { completion_on_proc[c]=0; continue; } else { OPM_MESSAGE("****Warning: Cell with i,j,k indices " << i << ' ' << j << ' ' << k << " not found in grid. The completion will be igored (well = " << well->name() << ')'); } } else { int cell = cgit->second; PerfData pd; pd.cell = cell; { const Value<double>& transmissibilityFactor = completion->getConnectionTransmissibilityFactorAsValueObject(); const double wellPi = completion ->getWellPi(); if (transmissibilityFactor.hasValue()) { pd.well_index = transmissibilityFactor.getValue(); } else { 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); } std::array<double, 3> cubical = WellsManagerDetail::getCubeDim<3>(c2f, begin_face_centroids, cell); // overwrite dz values calculated in getCubeDim. if (dz.size() > 0) { cubical[2] = dz[cell]; } const double* cell_perm = &permeability[dimensions*dimensions*cell]; pd.well_index = WellsManagerDetail::computeWellIndex(radius, cubical, cell_perm, completion->getSkinFactor(), completion->getDirection(), ntg[cell]); } pd.well_index *= wellPi; } wellperf_data[well_index].push_back(pd); } } else { ++shut_completions_number; if (completion->getState() != WellCompletion::SHUT) { OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion->getState() ) << " not handled"); } } } if ( is_parallel_run_ ) { // sum_completions_on_proc includes completions // that are shut std::size_t sum_completions_on_proc = std::accumulate(completion_on_proc.begin(), completion_on_proc.end(),0); // Set wells that are not on this processor to SHUT. // A well is not here if only shut completions are found. if ( sum_completions_on_proc == shut_completions_number ) { // Mark well as not existent on this process wells_on_proc[wellIter-wells.begin()] = 0; continue; } else { // Check that the complete well is on this process if ( sum_completions_on_proc < completionSet->size() ) { std::cout<< "Well "<< well->name() << " semms not be in " << "completely in the disjoint partition of " << "process deactivating here." << std::endl; // Mark well as not existent on this process wells_on_proc[wellIter-wells.begin()] = 0; continue; } } } } { // WELSPECS handling well_names_to_index[well->name()] = well_index; well_names.push_back(well->name()); { WellData wd; wd.reference_bhp_depth = well->getRefDepth(); wd.welspecsline = -1; if (well->isInjector( timeStep )) wd.type = INJECTOR; else wd.type = PRODUCER; well_data.push_back(wd); } } well_index++; } // Set up reference depths that were defaulted. Count perfs. const int num_wells = well_data.size(); int num_perfs = 0; assert (dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); } // Create the well data structures. w_ = create_wells(phaseUsage.num_phases, num_wells, num_perfs); if (!w_) { OPM_THROW(std::runtime_error, "Failed creating Wells struct."); } // Add wells. for (int w = 0; w < num_wells; ++w) { const int w_num_perf = wellperf_data[w].size(); std::vector<int> perf_cells (w_num_perf); std::vector<double> perf_prodind(w_num_perf); for (int perf = 0; perf < w_num_perf; ++perf) { perf_cells [perf] = wellperf_data[w][perf].cell; perf_prodind[perf] = wellperf_data[w][perf].well_index; } const double* comp_frac = NULL; // We initialize all wells with a null component fraction, // and must (for injection wells) overwrite it later. const int ok = add_well(well_data[w].type, well_data[w].reference_bhp_depth, w_num_perf, comp_frac, perf_cells.data(), perf_prodind.data(), well_names[w].c_str(), w_); if (!ok) { OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); } } }
void WellsManager::createWellsFromSpecs(std::vector<WellConstPtr>& wells, size_t timeStep, const C2F& c2f, const int* cart_dims, FC begin_face_centroids, CC begin_cell_centroids, int dimensions, std::vector<std::string>& well_names, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index, const PhaseUsage& phaseUsage, const std::map<int,int>& cartesian_to_compressed, const double* permeability, const NTG& ntg) { if (dimensions != 3) { OPM_THROW(std::domain_error, "WellsManager::createWellsFromSpecs() only " "supported in three space dimensions"); } std::vector<std::vector<PerfData> > wellperf_data; wellperf_data.resize(wells.size()); int well_index = 0; for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); { // WELSPECS handling well_names_to_index[well->name()] = well_index; well_names.push_back(well->name()); { WellData wd; // If defaulted, set refdepth to a marker // value, will be changed after getting perforation // data to the centroid of the cell of the top well // perforation. wd.reference_bhp_depth = (well->getRefDepthDefaulted()) ? -1e100 : well->getRefDepth(); wd.welspecsline = -1; if (well->isInjector( timeStep )) wd.type = INJECTOR; else wd.type = PRODUCER; well_data.push_back(wd); } } { // COMPDAT handling CompletionSetConstPtr completionSet = well->getCompletions(timeStep); for (size_t c=0; c<completionSet->size(); c++) { CompletionConstPtr completion = completionSet->get(c); 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; PerfData pd; pd.cell = cell; if (completion->getConnectionTransmissibilityFactor() > 0.0) { pd.well_index = completion->getConnectionTransmissibilityFactor(); } else { 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>(c2f, begin_face_centroids, cell); const double* cell_perm = &permeability[dimensions*dimensions*cell]; pd.well_index = WellsManagerDetail::computeWellIndex(radius, cubical, cell_perm, completion->getSkinFactor(), completion->getDirection(), ntg[cell]); } wellperf_data[well_index].push_back(pd); } } well_index++; } // Set up reference depths that were defaulted. Count perfs. const int num_wells = well_data.size(); int num_perfs = 0; assert (dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); if (well_data[w].reference_bhp_depth == -1e100) { // It was defaulted. Set reference depth to minimum perforation depth. double min_depth = 1e100; int num_wperfs = wellperf_data[w].size(); for (int perf = 0; perf < num_wperfs; ++perf) { using UgGridHelpers::increment; using UgGridHelpers::getCoordinate; const CC& cc = increment(begin_cell_centroids, wellperf_data[w][perf].cell, dimensions); const double depth = getCoordinate(cc, 2); min_depth = std::min(min_depth, depth); } well_data[w].reference_bhp_depth = min_depth; } } // Create the well data structures. w_ = create_wells(phaseUsage.num_phases, num_wells, num_perfs); if (!w_) { OPM_THROW(std::runtime_error, "Failed creating Wells struct."); } // Add wells. for (int w = 0; w < num_wells; ++w) { const int w_num_perf = wellperf_data[w].size(); std::vector<int> perf_cells (w_num_perf); std::vector<double> perf_prodind(w_num_perf); for (int perf = 0; perf < w_num_perf; ++perf) { perf_cells [perf] = wellperf_data[w][perf].cell; perf_prodind[perf] = wellperf_data[w][perf].well_index; } const double* comp_frac = NULL; // We initialize all wells with a null component fraction, // and must (for injection wells) overwrite it later. const int ok = add_well(well_data[w].type, well_data[w].reference_bhp_depth, w_num_perf, comp_frac, & perf_cells [0], & perf_prodind[0], well_names[w].c_str(), w_); if (!ok) { OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); } } }
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++; } }
void SimulatorBase<Implementation>::computeRESV(const std::size_t step, const Wells* wells, const BlackoilState& x, WellState& xw) { typedef SimFIBODetails::WellMap WellMap; const std::vector<WellConstPtr>& w_ecl = eclipse_state_->getSchedule()->getWells(step); const WellMap& wmap = SimFIBODetails::mapWells(w_ecl); const std::vector<int>& resv_wells = SimFIBODetails::resvWells(wells, step, wmap); if (! resv_wells.empty()) { const PhaseUsage& pu = props_.phaseUsage(); const std::vector<double>::size_type np = props_.numPhases(); rateConverter_.defineState(x); std::vector<double> distr (np); std::vector<double> hrates(np); std::vector<double> prates(np); for (std::vector<int>::const_iterator rp = resv_wells.begin(), e = resv_wells.end(); rp != e; ++rp) { WellControls* ctrl = wells->ctrls[*rp]; const bool is_producer = wells->type[*rp] == PRODUCER; // RESV control mode, all wells { const int rctrl = SimFIBODetails::resv_control(ctrl); if (0 <= rctrl) { const std::vector<double>::size_type off = (*rp) * np; if (is_producer) { // Convert to positive rates to avoid issues // in coefficient calculations. std::transform(xw.wellRates().begin() + (off + 0*np), xw.wellRates().begin() + (off + 1*np), prates.begin(), std::negate<double>()); } else { std::copy(xw.wellRates().begin() + (off + 0*np), xw.wellRates().begin() + (off + 1*np), prates.begin()); } const int fipreg = 0; // Hack. Ignore FIP regions. rateConverter_.calcCoeff(prates, fipreg, distr); well_controls_iset_distr(ctrl, rctrl, & distr[0]); } } // RESV control, WCONHIST wells. A bit of duplicate // work, regrettably. if (is_producer && wells->name[*rp] != 0) { WellMap::const_iterator i = wmap.find(wells->name[*rp]); if (i != wmap.end()) { WellConstPtr wp = i->second; const WellProductionProperties& p = wp->getProductionProperties(step); if (! p.predictionMode) { // History matching (WCONHIST/RESV) SimFIBODetails::historyRates(pu, p, hrates); const int fipreg = 0; // Hack. Ignore FIP regions. rateConverter_.calcCoeff(hrates, fipreg, distr); // WCONHIST/RESV target is sum of all // observed phase rates translated to // reservoir conditions. Recall sign // convention: Negative for producers. const double target = - std::inner_product(distr.begin(), distr.end(), hrates.begin(), 0.0); well_controls_clear(ctrl); well_controls_assert_number_of_phases(ctrl, int(np)); const int ok_resv = well_controls_add_new(RESERVOIR_RATE, target, & distr[0], ctrl); // For WCONHIST/RESV the BHP limit is set to 1 atm. // TODO: Make it possible to modify the BHP limit using // the WELTARG keyword const int ok_bhp = well_controls_add_new(BHP, unit::convert::from(1.0, unit::atm), NULL, ctrl); if (ok_resv != 0 && ok_bhp != 0) { xw.currentControls()[*rp] = 0; well_controls_set_current(ctrl, 0); } } } } } } }
void SimulatorBase<Implementation>::computeRESV(const std::size_t step, const Wells* wells, const BlackoilState& x, WellState& xw) { typedef SimFIBODetails::WellMap WellMap; const std::vector<WellConstPtr>& w_ecl = eclipse_state_->getSchedule()->getWells(step); const WellMap& wmap = SimFIBODetails::mapWells(w_ecl); const std::vector<int>& resv_wells = SimFIBODetails::resvWells(wells, step, wmap); const std::size_t number_resv_wells = resv_wells.size(); std::size_t global_number_resv_wells = number_resv_wells; #if HAVE_MPI if ( solver_.parallelInformation().type() == typeid(ParallelISTLInformation) ) { const auto& info = boost::any_cast<const ParallelISTLInformation&>(solver_.parallelInformation()); global_number_resv_wells = info.communicator().sum(global_number_resv_wells); if ( global_number_resv_wells ) { // At least one process has resv wells. Therefore rate converter needs // to calculate averages over regions that might cross process // borders. This needs to be done by all processes and therefore // outside of the next if statement. rateConverter_.defineState(x, boost::any_cast<const ParallelISTLInformation&>(solver_.parallelInformation())); } } else #endif { if ( global_number_resv_wells ) { rateConverter_.defineState(x); } } if (! resv_wells.empty()) { const PhaseUsage& pu = props_.phaseUsage(); const std::vector<double>::size_type np = props_.numPhases(); std::vector<double> distr (np); std::vector<double> hrates(np); std::vector<double> prates(np); for (std::vector<int>::const_iterator rp = resv_wells.begin(), e = resv_wells.end(); rp != e; ++rp) { WellControls* ctrl = wells->ctrls[*rp]; const bool is_producer = wells->type[*rp] == PRODUCER; // RESV control mode, all wells { const int rctrl = SimFIBODetails::resv_control(ctrl); if (0 <= rctrl) { const std::vector<double>::size_type off = (*rp) * np; if (is_producer) { // Convert to positive rates to avoid issues // in coefficient calculations. std::transform(xw.wellRates().begin() + (off + 0*np), xw.wellRates().begin() + (off + 1*np), prates.begin(), std::negate<double>()); } else { std::copy(xw.wellRates().begin() + (off + 0*np), xw.wellRates().begin() + (off + 1*np), prates.begin()); } const int fipreg = 0; // Hack. Ignore FIP regions. rateConverter_.calcCoeff(prates, fipreg, distr); well_controls_iset_distr(ctrl, rctrl, & distr[0]); } } // RESV control, WCONHIST wells. A bit of duplicate // work, regrettably. if (is_producer && wells->name[*rp] != 0) { WellMap::const_iterator i = wmap.find(wells->name[*rp]); if (i != wmap.end()) { WellConstPtr wp = i->second; const WellProductionProperties& p = wp->getProductionProperties(step); if (! p.predictionMode) { // History matching (WCONHIST/RESV) SimFIBODetails::historyRates(pu, p, hrates); const int fipreg = 0; // Hack. Ignore FIP regions. rateConverter_.calcCoeff(hrates, fipreg, distr); // WCONHIST/RESV target is sum of all // observed phase rates translated to // reservoir conditions. Recall sign // convention: Negative for producers. const double target = - std::inner_product(distr.begin(), distr.end(), hrates.begin(), 0.0); well_controls_clear(ctrl); well_controls_assert_number_of_phases(ctrl, int(np)); static const double invalid_alq = -std::numeric_limits<double>::max(); static const int invalid_vfp = -std::numeric_limits<int>::max(); const int ok_resv = well_controls_add_new(RESERVOIR_RATE, target, invalid_alq, invalid_vfp, & distr[0], ctrl); // For WCONHIST the BHP limit is set to 1 atm. // or a value specified using WELTARG double bhp_limit = (p.BHPLimit > 0) ? p.BHPLimit : unit::convert::from(1.0, unit::atm); const int ok_bhp = well_controls_add_new(BHP, bhp_limit, invalid_alq, invalid_vfp, NULL, ctrl); if (ok_resv != 0 && ok_bhp != 0) { xw.currentControls()[*rp] = 0; well_controls_set_current(ctrl, 0); } } } } } } if( wells ) { for (int w = 0, nw = wells->number_of_wells; w < nw; ++w) { WellControls* ctrl = wells->ctrls[w]; const bool is_producer = wells->type[w] == PRODUCER; if (!is_producer && wells->name[w] != 0) { WellMap::const_iterator i = wmap.find(wells->name[w]); if (i != wmap.end()) { WellConstPtr wp = i->second; const WellInjectionProperties& injector = wp->getInjectionProperties(step); if (!injector.predictionMode) { //History matching WCONINJEH static const double invalid_alq = -std::numeric_limits<double>::max(); static const int invalid_vfp = -std::numeric_limits<int>::max(); // For WCONINJEH the BHP limit is set to a large number // or a value specified using WELTARG double bhp_limit = (injector.BHPLimit > 0) ? injector.BHPLimit : std::numeric_limits<double>::max(); const int ok_bhp = well_controls_add_new(BHP, bhp_limit, invalid_alq, invalid_vfp, NULL, ctrl); if (!ok_bhp) { OPM_THROW(std::runtime_error, "Failed to add well control."); } } } } } } }