/* Copyright 2016 SINTEF ICT, Applied Mathematics. Copyright 2016 - 2017 Statoil ASA. Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services Copyright 2016 - 2018 IRIS AS This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OPM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . */ #ifndef OPM_BLACKOILWELLMODEL_HEADER_INCLUDED #define OPM_BLACKOILWELLMODEL_HEADER_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Opm::Parameters { struct EnableTerminalOutput { static constexpr bool value = true; }; } // namespace Opm::Parameters namespace Opm { #if COMPILE_GPU_BRIDGE template class WellContributions; #endif /// Class for handling the blackoil well model. template class BlackoilWellModel : public BaseAuxiliaryModule , public BlackoilWellModelGeneric> { public: // --------- Types --------- using Grid = GetPropType; using EquilGrid = GetPropType; using FluidSystem = GetPropType; using ElementContext = GetPropType; using Indices = GetPropType; using Simulator = GetPropType; using Scalar = GetPropType; using RateVector = GetPropType; using GlobalEqVector = GetPropType; using SparseMatrixAdapter = GetPropType; using GasLiftSingleWell = typename WellInterface::GasLiftSingleWell; using GLiftOptWells = typename BlackoilWellModelGeneric::GLiftOptWells; using GLiftProdWells = typename BlackoilWellModelGeneric::GLiftProdWells; using GLiftWellStateMap = typename BlackoilWellModelGeneric::GLiftWellStateMap; using GLiftEclWells = typename GasLiftGroupInfo::GLiftEclWells; using GLiftSyncGroups = typename GasLiftSingleWellGeneric::GLiftSyncGroups; using ModelParameters = BlackoilModelParameters; constexpr static std::size_t pressureVarIndex = GetPropType::pressureSwitchIdx; typedef typename BaseAuxiliaryModule::NeighborSet NeighborSet; static const int numEq = Indices::numEq; static const int solventSaturationIdx = Indices::solventSaturationIdx; static constexpr bool has_solvent_ = getPropValue(); static constexpr bool has_polymer_ = getPropValue(); static constexpr bool has_energy_ = getPropValue(); static constexpr bool has_micp_ = getPropValue(); // TODO: where we should put these types, WellInterface or Well Model? // or there is some other strategy, like TypeTag typedef Dune::FieldVector VectorBlockType; typedef Dune::BlockVector BVector; typedef BlackOilPolymerModule PolymerModule; typedef BlackOilMICPModule MICPModule; // For the conversion between the surface volume rate and resrevoir voidage rate using RateConverterType = RateConverter:: SurfaceToReservoirVoidage >; // For computing average pressured used by gpmaint using AverageRegionalPressureType = RegionAverageCalculator:: AverageRegionalPressure >; using Domain = SubDomain; BlackoilWellModel(Simulator& simulator); void init(); void initWellContainer(const int reportStepIdx) override; ///////////// // ///////////// unsigned numDofs() const override // No extra dofs are inserted for wells. (we use a Schur complement.) { return 0; } void addNeighbors(std::vector& neighbors) const override; void applyInitial() override {} void linearize(SparseMatrixAdapter& jacobian, GlobalEqVector& res) override; void linearizeDomain(const Domain& domain, SparseMatrixAdapter& jacobian, GlobalEqVector& res); void postSolve(GlobalEqVector& deltaX) override { recoverWellSolutionAndUpdateWellState(deltaX); } void postSolveDomain(GlobalEqVector& deltaX, const Domain& domain) { recoverWellSolutionAndUpdateWellStateDomain(deltaX, domain); } ///////////// // ///////////// template void deserialize(Restarter& /* res */) { // TODO (?) } /*! * \brief This method writes the complete state of the well * to the harddisk. */ template void serialize(Restarter& /* res*/) { // TODO (?) } void beginEpisode() { OPM_TIMEBLOCK(beginEpsiode); beginReportStep(simulator_.episodeIndex()); } void beginTimeStep(); void beginIteration() { OPM_TIMEBLOCK(beginIteration); assemble(simulator_.model().newtonMethod().numIterations(), simulator_.timeStepSize()); } void endIteration() { } void endTimeStep() { OPM_TIMEBLOCK(endTimeStep); timeStepSucceeded(simulator_.time(), simulator_.timeStepSize()); } void endEpisode() { endReportStep(); } void computeTotalRatesForDof(RateVector& rate, unsigned globalIdx) const; template void computeTotalRatesForDof(RateVector& rate, const Context& context, unsigned spaceIdx, unsigned timeIdx) const; using WellInterfacePtr = std::shared_ptr >; using BlackoilWellModelGeneric::initFromRestartFile; void initFromRestartFile(const RestartValue& restartValues) { initFromRestartFile(restartValues, this->simulator_.vanguard().transferWTestState(), grid().size(0), param_.use_multisegment_well_); } using BlackoilWellModelGeneric::prepareDeserialize; void prepareDeserialize(const int report_step) { prepareDeserialize(report_step, grid().size(0), param_.use_multisegment_well_); } data::Wells wellData() const { auto wsrpt = this->wellState() .report(simulator_.vanguard().globalCell().data(), [this](const int well_index) -> bool { return this->wasDynamicallyShutThisTimeStep(well_index); }); { const auto& tracerRates = this->simulator_.problem() .tracerModel().getWellTracerRates(); const auto& freeTracerRates = simulator_.problem() .tracerModel().getWellFreeTracerRates(); const auto& solTracerRates = simulator_.problem() .tracerModel().getWellSolTracerRates(); const auto& mswTracerRates = simulator_.problem() .tracerModel().getMswTracerRates(); this->assignWellTracerRates(wsrpt, tracerRates); this->assignWellTracerRates(wsrpt, freeTracerRates); this->assignWellTracerRates(wsrpt, solTracerRates); this->assignMswTracerRates(wsrpt, mswTracerRates); } BlackoilWellModelGuideRates(*this) .assignWellGuideRates(wsrpt, this->reportStepIndex()); this->assignWellTargets(wsrpt); this->assignShutConnections(wsrpt, this->reportStepIndex()); // only used to compute gas injection mass rates for CO2STORE runs // The gas reference density is thus the same for all pvt regions // We therefore for simplicity use 0 here if (eclState().runspec().co2Storage()) { this->assignMassGasRate(wsrpt, FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, 0)); } return wsrpt; } data::WellBlockAveragePressures wellBlockAveragePressures() const { return this->computeWellBlockAveragePressures(); } // subtract B*inv(D)*C * x from A*x void apply(const BVector& x, BVector& Ax) const; void applyDomain(const BVector& x, BVector& Ax, const int domainIndex) const; #if COMPILE_GPU_BRIDGE // accumulate the contributions of all Wells in the WellContributions object void getWellContributions(WellContributions& x) const; #endif // apply well model with scaling of alpha void applyScaleAdd(const Scalar alpha, const BVector& x, BVector& Ax) const; void applyScaleAddDomain(const Scalar alpha, const BVector& x, BVector& Ax, const int domainIndex) const; // Check if well equations is converged. ConvergenceReport getWellConvergence(const std::vector& B_avg, const bool checkWellGroupControls = false) const; // Check if well equations are converged locally. ConvergenceReport getDomainWellConvergence(const Domain& domain, const std::vector& B_avg, DeferredLogger& local_deferredLogger) const; const SimulatorReportSingle& lastReport() const; void addWellContributions(SparseMatrixAdapter& jacobian) const; // add source from wells to the reservoir matrix void addReservoirSourceTerms(GlobalEqVector& residual, std::vector& diagMatAddress) const; // called at the beginning of a report step void beginReportStep(const int time_step); // it should be able to go to prepareTimeStep(), however, the updateWellControls() and initPrimaryVariablesEvaluation() // makes it a little more difficult. unless we introduce if (iterationIdx != 0) to avoid doing the above functions // twice at the beginning of the time step /// Calculating the explict quantities used in the well calculation. By explicit, we mean they are cacluated /// at the beginning of the time step and no derivatives are included in these quantities void calculateExplicitQuantities(DeferredLogger& deferred_logger) const; // some preparation work, mostly related to group control and RESV, // at the beginning of each time step (Not report step) void prepareTimeStep(DeferredLogger& deferred_logger); void initPrimaryVariablesEvaluation() const; void initPrimaryVariablesEvaluationDomain(const Domain& domain) const; std::pair updateWellControls(const bool mandatory_network_balance, DeferredLogger& deferred_logger, const bool relax_network_tolerance = false); void updateAndCommunicate(const int reportStepIdx, const int iterationIdx, DeferredLogger& deferred_logger); bool updateGroupControls(const Group& group, DeferredLogger& deferred_logger, const int reportStepIdx, const int iterationIdx); WellInterfacePtr getWell(const std::string& well_name) const; bool hasWell(const std::string& well_name) const; using PressureMatrix = Dune::BCRSMatrix>; void addWellPressureEquations(PressureMatrix& jacobian, const BVector& weights,const bool use_well_weights) const; void addWellPressureEquationsDomain([[maybe_unused]] PressureMatrix& jacobian, [[maybe_unused]] const BVector& weights, [[maybe_unused]] const bool use_well_weights, [[maybe_unused]] const int domainIndex) const; void addWellPressureEquationsStruct(PressureMatrix& jacobian) const; void initGliftEclWellMap(GLiftEclWells &ecl_well_map); /// \brief Get list of local nonshut wells const std::vector& localNonshutWells() const { return well_container_; } // prototype for assemble function for ASPIN solveLocal() // will try to merge back to assemble() when done prototyping void assembleDomain(const int iterationIdx, const double dt, const Domain& domain); void updateWellControlsDomain(DeferredLogger& deferred_logger, const Domain& domain); void setupDomains(const std::vector& domains); protected: Simulator& simulator_; // a vector of all the wells. std::vector well_container_{}; std::vector is_cell_perforated_{}; void initializeWellState(const int timeStepIdx); // create the well container void createWellContainer(const int report_step) override; WellInterfacePtr createWellPointer(const int wellID, const int report_step) const; template std::unique_ptr createTypedWellPointer(const int wellID, const int time_step) const; WellInterfacePtr createWellForWellTest(const std::string& well_name, const int report_step, DeferredLogger& deferred_logger) const; const ModelParameters param_; std::size_t global_num_cells_{}; // the number of the cells in the local grid std::size_t local_num_cells_{}; Scalar gravity_{}; std::vector depth_{}; bool alternative_well_rate_init_{}; std::map well_group_thp_calc_; std::unique_ptr rateConverter_{}; std::map> regionalAveragePressureCalculator_{}; SimulatorReportSingle last_report_{}; // Pre-step network solve at static reservoir conditions (group and well states might be updated) void doPreStepNetworkRebalance(DeferredLogger& deferred_logger); // used to better efficiency of calcuation mutable BVector scaleAddRes_{}; std::vector B_avg_{}; // Store the local index of the wells perforated cells in the domain, if using subdomains SparseTable well_local_cells_; const Grid& grid() const { return simulator_.vanguard().grid(); } const EquilGrid& equilGrid() const { return simulator_.vanguard().equilGrid(); } const EclipseState& eclState() const { return simulator_.vanguard().eclState(); } // compute the well fluxes and assemble them in to the reservoir equations as source terms // and in the well equations. void assemble(const int iterationIdx, const double dt); // well controls and network pressures affect each other and are solved in an iterative manner. // the function handles one iteration of updating well controls and network pressures. // it is possible to decouple the update of well controls and network pressures further. // the returned two booleans are {continue_due_to_network, well_group_control_changed}, respectively std::pair updateWellControlsAndNetworkIteration(const bool mandatory_network_balance, const bool relax_network_tolerance, const double dt, DeferredLogger& local_deferredLogger); bool updateWellControlsAndNetwork(const bool mandatory_network_balance, const double dt, DeferredLogger& local_deferredLogger); void computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger); /// Update rank's notion of intersecting wells and their /// associate solution variables. /// /// \param[in] reportStepIdx Report step. /// /// \param[in] enableWellPIScaling Whether or not to enable WELPI /// scaling. Typically enabled (i.e., true) only at the start /// of a report step. void initializeLocalWellStructure(const int reportStepIdx, const bool enableWellPIScaling); /// Initialize group control modes/constraints and group solution state. /// /// \param[in] reportStepIdx Report step. void initializeGroupStructure(const int reportStepIdx); // called at the end of a time step void timeStepSucceeded(const double simulationTime, const double dt); // called at the end of a report step void endReportStep(); // using the solution x to recover the solution xw for wells and applying // xw to update Well State void recoverWellSolutionAndUpdateWellState(const BVector& x); // using the solution x to recover the solution xw for wells and applying // xw to update Well State void recoverWellSolutionAndUpdateWellStateDomain(const BVector& x, const Domain& domain); // setting the well_solutions_ based on well_state. void updatePrimaryVariables(DeferredLogger& deferred_logger); void initializeWBPCalculationService(); data::WellBlockAveragePressures computeWellBlockAveragePressures() const; typename ParallelWBPCalculation::EvaluatorFactory makeWellSourceEvaluatorFactory(const std::vector::size_type wellIdx) const; void updateAverageFormationFactor(); void computePotentials(const std::size_t widx, const WellState& well_state_copy, std::string& exc_msg, ExceptionType::ExcEnum& exc_type, DeferredLogger& deferred_logger) override; const std::vector& wellPerfEfficiencyFactors() const; void calculateProductivityIndexValuesShutWells(const int reportStepIdx, DeferredLogger& deferred_logger) override; void calculateProductivityIndexValues(DeferredLogger& deferred_logger) override; void calculateProductivityIndexValues(const WellInterface* wellPtr, DeferredLogger& deferred_logger); // The number of components in the model. int numComponents() const; int reportStepIndex() const; void assembleWellEq(const double dt, DeferredLogger& deferred_logger); void assembleWellEqDomain(const double dt, const Domain& domain, DeferredLogger& deferred_logger); void prepareWellsBeforeAssembling(const double dt, DeferredLogger& deferred_logger); // TODO: finding a better naming void assembleWellEqWithoutIteration(const double dt, DeferredLogger& deferred_logger); bool maybeDoGasLiftOptimize(DeferredLogger& deferred_logger); void gasLiftOptimizationStage1(DeferredLogger& deferred_logger, GLiftProdWells& prod_wells, GLiftOptWells& glift_wells, GasLiftGroupInfo& group_info, GLiftWellStateMap& state_map); // cannot be const since it accesses the non-const WellState void gasLiftOptimizationStage1SingleWell(WellInterface* well, DeferredLogger& deferred_logger, GLiftProdWells& prod_wells, GLiftOptWells& glift_wells, GasLiftGroupInfo& group_info, GLiftWellStateMap& state_map, GLiftSyncGroups& groups_to_sync); void extractLegacyCellPvtRegionIndex_(); void extractLegacyDepth_(); /// upate the wellTestState related to economic limits void updateWellTestState(const double& simulationTime, WellTestState& wellTestState) const; void wellTesting(const int timeStepIdx, const double simulationTime, DeferredLogger& deferred_logger); void calcResvCoeff(const int fipnum, const int pvtreg, const std::vector& production_rates, std::vector& resv_coeff) override; void calcInjResvCoeff(const int fipnum, const int pvtreg, std::vector& resv_coeff) override; void computeWellTemperature(); int compressedIndexForInterior(int cartesian_cell_idx) const override { return simulator_.vanguard().compressedIndexForInterior(cartesian_cell_idx); } private: BlackoilWellModel(Simulator& simulator, const PhaseUsage& pu); // These members are used to avoid reallocation in specific functions // (e.g., apply, applyDomain) instead of using local variables. // Their state is not relevant between function calls, so they can // (and must) be mutable, as the functions using them are const. mutable BVector x_local_; mutable BVector Ax_local_; mutable BVector res_local_; mutable GlobalEqVector linearize_res_local_; }; } // namespace Opm #ifndef OPM_BLACKOILWELLMODEL_IMPL_HEADER_INCLUDED #include "BlackoilWellModel_impl.hpp" #endif #endif