diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp deleted file mode 100644 index a35aaeac0..000000000 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver.hpp +++ /dev/null @@ -1,449 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL ASA. - - 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_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED -#define OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -struct UnstructuredGrid; -struct Wells; - -namespace Opm { - - namespace parameter { class ParameterGroup; } - class DerivedGeology; - class RockCompressibility; - class NewtonIterationBlackoilInterface; - class PolymerBlackoilState; - class WellStateFullyImplicitBlackoil; - - - /// A fully implicit solver for the black-oil-polymer problem. - /// - /// The simulator is capable of handling three-phase problems - /// where gas can be dissolved in oil (but not vice versa). It - /// uses an industry-standard TPFA discretization with per-phase - /// upwind weighting of mobilities. - /// - /// It uses automatic differentiation via the class AutoDiffBlock - /// to simplify assembly of the jacobian matrix. - template - class FullyImplicitBlackoilPolymerSolver - { - public: - // the Newton relaxation type - enum RelaxType { DAMPEN, SOR }; - - // class holding the solver parameters - struct SolverParameter - { - double dp_max_rel_; - double ds_max_; - double dr_max_rel_; - enum RelaxType relax_type_; - double relax_max_; - double relax_increment_; - double relax_rel_tol_; - double max_residual_allowed_; - double tolerance_mb_; - double tolerance_cnv_; - double tolerance_wells_; - int max_iter_; // max newton iterations - int min_iter_; // min newton iterations - - SolverParameter( const parameter::ParameterGroup& param ); - SolverParameter(); - - void reset(); - }; - - /// \brief The type of the grid that we use. - typedef T Grid; - /// Construct a solver. It will retain references to the - /// arguments of this functions, and they are expected to - /// remain in scope for the lifetime of the solver. - /// \param[in] param parameters - /// \param[in] grid grid data structure - /// \param[in] fluid fluid properties - /// \param[in] geo rock properties - /// \param[in] rock_comp_props if non-null, rock compressibility properties - /// \param[in] wells well structure - /// \param[in] linsolver linear solver - FullyImplicitBlackoilPolymerSolver(const SolverParameter& param, - const Grid& grid , - const BlackoilPropsAdInterface& fluid, - const DerivedGeology& geo , - const RockCompressibility* rock_comp_props, - const PolymerPropsAd& polymer_props_ad, - const Wells* wells, - const NewtonIterationBlackoilInterface& linsolver, - const bool has_disgas, - const bool has_vapoil, - const bool has_polymer, - const bool has_plyshlog, - const bool terminal_output); - - /// \brief Set threshold pressures that prevent or reduce flow. - /// This prevents flow across faces if the potential - /// difference is less than the threshold. If the potential - /// difference is greater, the threshold value is subtracted - /// before calculating flow. This is treated symmetrically, so - /// flow is prevented or reduced in both directions equally. - /// \param[in] threshold_pressures_by_face array of size equal to the number of faces - /// of the grid passed in the constructor. - void setThresholdPressures(const std::vector& threshold_pressures_by_face); - - /// Take a single forward step, modifiying - /// state.pressure() - /// state.faceflux() - /// state.saturation() - /// state.gasoilratio() - /// wstate.bhp() - /// \param[in] dt time step size - /// \param[in] state reservoir state - /// \param[in] wstate well state - /// \return number of linear iterations used - int - step(const double dt , - PolymerBlackoilState& state , - WellStateFullyImplicitBlackoil& wstate, - const std::vector& polymer_inflow); - - unsigned int newtonIterations () const { return newtonIterations_; } - unsigned int linearIterations () const { return linearIterations_; } - - private: - // Types and enums - typedef AutoDiffBlock ADB; - typedef ADB::V V; - typedef ADB::M M; - typedef Eigen::Array DataBlock; - - struct ReservoirResidualQuant { - ReservoirResidualQuant(); - std::vector accum; // Accumulations - ADB mflux; // Mass flux (surface conditions) - ADB b; // Reciprocal FVF - ADB head; // Pressure drop across int. interfaces - ADB mob; // Phase mobility (per cell) - }; - - struct SolutionState { - SolutionState(const int np); - ADB pressure; - ADB temperature; - std::vector saturation; - ADB rs; - ADB rv; - ADB concentration; - ADB qs; - ADB bhp; - // Below are quantities stored in the state for optimization purposes. - std::vector canonical_phase_pressures; // Always has 3 elements, even if only 2 phases active. - }; - - struct WellOps { - WellOps(const Wells* wells); - M w2p; // well -> perf (scatter) - M p2w; // perf -> well (gather) - }; - - enum { Water = BlackoilPropsAdInterface::Water, - Oil = BlackoilPropsAdInterface::Oil , - Gas = BlackoilPropsAdInterface::Gas , - MaxNumPhases = BlackoilPropsAdInterface::MaxNumPhases - }; - - enum PrimalVariables { Sg = 0, RS = 1, RV = 2 }; - - // Member data - const Grid& grid_; - const BlackoilPropsAdInterface& fluid_; - const DerivedGeology& geo_; - const RockCompressibility* rock_comp_props_; - const PolymerPropsAd& polymer_props_ad_; - const Wells* wells_; - const NewtonIterationBlackoilInterface& linsolver_; - // For each canonical phase -> true if active - const std::vector active_; - // Size = # active phases. Maps active -> canonical phase indices. - const std::vector canph_; - const std::vector cells_; // All grid cells - HelperOps ops_; - const WellOps wops_; - V cmax_; - const bool has_disgas_; - const bool has_vapoil_; - const bool has_polymer_; - const bool has_plyshlog_; - const int poly_pos_; - - SolverParameter param_; - bool use_threshold_pressure_; - V threshold_pressures_by_interior_face_; - - std::vector rq_; - std::vector phaseCondition_; - V well_perforation_pressure_diffs_; // Diff to bhp for each well perforation. - - LinearisedBlackoilResidual residual_; - - /// \brief Whether we print something to std::cout - bool terminal_output_; - unsigned int newtonIterations_; - unsigned int linearIterations_; - - std::vector primalVariable_; - - // Private methods. - - // return true if wells are available - bool wellsActive() const { return wells_ ? wells_->number_of_wells > 0 : false ; } - // return wells object - const Wells& wells () const { assert( bool(wells_ != 0) ); return *wells_; } - - SolutionState - constantState(const PolymerBlackoilState& x, - const WellStateFullyImplicitBlackoil& xw) const; - - void - makeConstantState(SolutionState& state) const; - - SolutionState - variableState(const PolymerBlackoilState& x, - const WellStateFullyImplicitBlackoil& xw) const; - - void - computeAccum(const SolutionState& state, - const int aix ); - - void computeWellConnectionPressures(const SolutionState& state, - const WellStateFullyImplicitBlackoil& xw); - - void - addWellControlEq(const SolutionState& state, - const WellStateFullyImplicitBlackoil& xw, - const V& aliveWells); - - void - addWellEq(const SolutionState& state, - WellStateFullyImplicitBlackoil& xw, - V& aliveWells, - const std::vector& polymer_inflow); - - void updateWellControls(WellStateFullyImplicitBlackoil& xw) const; - - void - assemble(const V& dtpv, - const PolymerBlackoilState& x, - const bool initial_assembly, - WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow); - - V solveJacobianSystem() const; - - void updateState(const V& dx, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state); - - std::vector - computePressures(const SolutionState& state) const; - - std::vector - computePressures(const ADB& po, - const ADB& sw, - const ADB& so, - const ADB& sg) const; - - V - computeGasPressure(const V& po, - const V& sw, - const V& so, - const V& sg) const; - - std::vector - computeRelPerm(const SolutionState& state) const; - - void - computeMassFlux(const int actph , - const V& transi, - const ADB& kr , - const ADB& p , - const SolutionState& state ); - - void - computeCmax(PolymerBlackoilState& state); - - ADB - computeMc(const SolutionState& state) const; - - void applyThresholdPressures(ADB& dp); - - double - residualNorm() const; - - /// \brief Compute the residual norms of the mass balance for each phase, - /// the well flux, and the well equation. - /// \return a vector that contains for each phase the norm of the mass balance - /// and afterwards the norm of the residual of the well flux and the well equation. - std::vector computeResidualNorms() const; - - ADB - fluidViscosity(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const; - - ADB - fluidReciprocFVF(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const; - - ADB - fluidDensity(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const; - - V - fluidRsSat(const V& p, - const V& so, - const std::vector& cells) const; - - ADB - fluidRsSat(const ADB& p, - const ADB& so, - const std::vector& cells) const; - - V - fluidRvSat(const V& p, - const V& so, - const std::vector& cells) const; - - ADB - fluidRvSat(const ADB& p, - const ADB& so, - const std::vector& cells) const; - - ADB - poroMult(const ADB& p) const; - - ADB - transMult(const ADB& p) const; - - void - classifyCondition(const SolutionState& state, - std::vector& cond ) const; - - const std::vector - phaseCondition() const {return phaseCondition_;} - - void - classifyCondition(const PolymerBlackoilState& state); - - - /// update the primal variable for Sg, Rv or Rs. The Gas phase must - /// be active to call this method. - void - updatePrimalVariableFromState(const PolymerBlackoilState& state); - - /// Update the phaseCondition_ member based on the primalVariable_ member. - void - updatePhaseCondFromPrimalVariable(); - - /// Compute convergence based on total mass balance (tol_mb) and maximum - /// residual mass balance (tol_cnv). - bool getConvergence(const double dt, const int iteration); - - /// \brief Compute the reduction within the convergence check. - /// \param[in] B A matrix with MaxNumPhases columns and the same number rows - /// as the number of cells of the grid. B.col(i) contains the values - /// for phase i. - /// \param[in] tempV A matrix with MaxNumPhases columns and the same number rows - /// as the number of cells of the grid. tempV.col(i) contains the - /// values - /// for phase i. - /// \param[in] R A matrix with MaxNumPhases columns and the same number rows - /// as the number of cells of the grid. B.col(i) contains the values - /// for phase i. - /// \param[out] R_sum An array of size MaxNumPhases where entry i contains the sum - /// of R for the phase i. - /// \param[out] maxCoeff An array of size MaxNumPhases where entry i contains the - /// maximum of tempV for the phase i. - /// \param[out] B_avg An array of size MaxNumPhases where entry i contains the average - /// of B for the phase i. - /// \param[in] nc The number of cells of the local grid. - /// \return The total pore volume over all cells. - double - convergenceReduction(const Eigen::Array& B, - const Eigen::Array& tempV, - const Eigen::Array& R, - std::array& R_sum, - std::array& maxCoeff, - std::array& B_avg, - int nc) const; - - void detectNewtonOscillations(const std::vector>& residual_history, - const int it, const double relaxRelTol, - bool& oscillate, bool& stagnate) const; - - void stablizeNewton(V& dx, V& dxOld, const double omega, const RelaxType relax_type) const; - - double dpMaxRel() const { return param_.dp_max_rel_; } - double dsMax() const { return param_.ds_max_; } - double drMaxRel() const { return param_.dr_max_rel_; } - enum RelaxType relaxType() const { return param_.relax_type_; } - double relaxMax() const { return param_.relax_max_; }; - double relaxIncrement() const { return param_.relax_increment_; }; - double relaxRelTol() const { return param_.relax_rel_tol_; }; - double maxIter() const { return param_.max_iter_; } - double minIter() const { return param_.min_iter_; } - double maxResidualAllowed() const { return param_.max_residual_allowed_; } - - }; -} // namespace Opm - -#include "FullyImplicitBlackoilPolymerSolver_impl.hpp" - -#endif // OPM_FULLYIMPLICITBLACKOILPOLYMERSOLVER_HEADER_INCLUDED diff --git a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp b/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp deleted file mode 100644 index 056f1279e..000000000 --- a/opm/polymer/fullyimplicit/FullyImplicitBlackoilPolymerSolver_impl.hpp +++ /dev/null @@ -1,2491 +0,0 @@ -/* - Copyright 2013 SINTEF ICT, Applied Mathematics. - Copyright 2014 STATOIL ASA. - Copyright 2015 Dr. Blatt - HPC-Simulation-Software & Services - Copyright 2015 NTNU - - 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 . -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -//#include - -// A debugging utility. -#define DUMP(foo) \ - do { \ - std::cout << "==========================================\n" \ - << #foo ":\n" \ - << collapseJacs(foo) << std::endl; \ - } while (0) - -#define DUMPVAL(foo) \ - do { \ - std::cout << "==========================================\n" \ - << #foo ":\n" \ - << foo.value() << std::endl; \ - } while (0) - -#define DISKVAL(foo) \ - do { \ - std::ofstream os(#foo); \ - os.precision(16); \ - os << foo.value() << std::endl; \ - } while (0) - - -namespace Opm { - -typedef AutoDiffBlock ADB; -typedef ADB::V V; -typedef ADB::M M; -typedef Eigen::Array DataBlock; - - -namespace detail { - - - std::vector - buildAllCells(const int nc) - { - std::vector all_cells(nc); - - for (int c = 0; c < nc; ++c) { all_cells[c] = c; } - - return all_cells; - } - - - - template - std::vector - activePhases(const PU& pu) - { - const int maxnp = Opm::BlackoilPhases::MaxNumPhases; - std::vector active(maxnp, false); - - for (int p = 0; p < pu.MaxNumPhases; ++p) { - active[ p ] = pu.phase_used[ p ] != 0; - } - - return active; - } - - - - template - std::vector - active2Canonical(const PU& pu) - { - const int maxnp = Opm::BlackoilPhases::MaxNumPhases; - std::vector act2can(maxnp, -1); - - for (int phase = 0; phase < maxnp; ++phase) { - if (pu.phase_used[ phase ]) { - act2can[ pu.phase_pos[ phase ] ] = phase; - } - } - - return act2can; - } - - - - template - int polymerPos(const PU& pu) - { - const int maxnp = Opm::BlackoilPhases::MaxNumPhases; - int pos = 0; - for (int phase = 0; phase < maxnp; ++phase) { - if (pu.phase_used[phase]) { - pos++; - } - } - - return pos; - } - -} // namespace detail - - template - void FullyImplicitBlackoilPolymerSolver::SolverParameter:: - reset() - { - // default values for the solver parameters - dp_max_rel_ = 1.0e9; - ds_max_ = 0.2; - dr_max_rel_ = 1.0e9; - relax_type_ = DAMPEN; - relax_max_ = 0.5; - relax_increment_ = 0.1; - relax_rel_tol_ = 0.2; - max_iter_ = 15; // not more then 15 its by default - min_iter_ = 1; // Default to always do at least one nonlinear iteration. - max_residual_allowed_ = 1e7; - tolerance_mb_ = 1.0e-5; - tolerance_cnv_ = 1.0e-2; - tolerance_wells_ = 5.0e-1; - - } - - template - FullyImplicitBlackoilPolymerSolver::SolverParameter:: - SolverParameter() - { - // set default values - reset(); - } - - template - FullyImplicitBlackoilPolymerSolver::SolverParameter:: - SolverParameter( const parameter::ParameterGroup& param ) - { - // set default values - reset(); - - // overload with given parameters - dp_max_rel_ = param.getDefault("dp_max_rel", dp_max_rel_); - ds_max_ = param.getDefault("ds_max", ds_max_); - dr_max_rel_ = param.getDefault("dr_max_rel", dr_max_rel_); - relax_max_ = param.getDefault("relax_max", relax_max_); - max_iter_ = param.getDefault("max_iter", max_iter_); - min_iter_ = param.getDefault("min_iter", min_iter_); - max_residual_allowed_ = param.getDefault("max_residual_allowed", max_residual_allowed_); - - tolerance_mb_ = param.getDefault("tolerance_mb", tolerance_mb_); - tolerance_cnv_ = param.getDefault("tolerance_cnv", tolerance_cnv_); - tolerance_wells_ = param.getDefault("tolerance_wells", tolerance_wells_ ); - - std::string relaxation_type = param.getDefault("relax_type", std::string("dampen")); - if (relaxation_type == "dampen") { - relax_type_ = DAMPEN; - } else if (relaxation_type == "sor") { - relax_type_ = SOR; - } else { - OPM_THROW(std::runtime_error, "Unknown Relaxtion Type " << relaxation_type); - } - } - - - template - FullyImplicitBlackoilPolymerSolver:: - FullyImplicitBlackoilPolymerSolver(const SolverParameter& param, - const Grid& grid , - const BlackoilPropsAdInterface& fluid, - const DerivedGeology& geo , - const RockCompressibility* rock_comp_props, - const PolymerPropsAd& polymer_props_ad, - const Wells* wells, - const NewtonIterationBlackoilInterface& linsolver, - const bool has_disgas, - const bool has_vapoil, - const bool has_polymer, - const bool has_plyshlog, - const bool terminal_output) - : grid_ (grid) - , fluid_ (fluid) - , geo_ (geo) - , rock_comp_props_(rock_comp_props) - , polymer_props_ad_(polymer_props_ad) - , wells_ (wells) - , linsolver_ (linsolver) - , active_(detail::activePhases(fluid.phaseUsage())) - , canph_ (detail::active2Canonical(fluid.phaseUsage())) - , cells_ (detail::buildAllCells(Opm::AutoDiffGrid::numCells(grid))) - , ops_ (grid) - , wops_ (wells_) - , cmax_(V::Zero(Opm::AutoDiffGrid::numCells(grid))) - , has_disgas_(has_disgas) - , has_vapoil_(has_vapoil) - , has_polymer_(has_polymer) - , has_plyshlog_(has_plyshlog) - , poly_pos_(detail::polymerPos(fluid.phaseUsage())) - , param_( param ) - , use_threshold_pressure_(false) - , rq_ (fluid.numPhases()) - , phaseCondition_(AutoDiffGrid::numCells(grid)) - , residual_ ( { std::vector(fluid.numPhases(), ADB::null()), - ADB::null(), - ADB::null() } ) - , terminal_output_ (terminal_output) - , newtonIterations_( 0 ) - , linearIterations_( 0 ) - { -#if HAVE_MPI - if ( terminal_output_ ) { - if ( linsolver_.parallelInformation().type() == typeid(ParallelISTLInformation) ) - { - const ParallelISTLInformation& info = - boost::any_cast(linsolver_.parallelInformation()); - // Only rank 0 does print to std::cout if terminal_output is enabled - terminal_output_ = (info.communicator().rank()==0); - } - } -#endif - if (has_polymer_) { - if (!active_[Water]) { - OPM_THROW(std::logic_error, "Polymer must solved in water!\n"); - } - // If deck has polymer, residual_ should contain polymer equation. - rq_.resize(fluid_.numPhases()+1); - residual_.material_balance_eq.resize(fluid_.numPhases()+1, ADB::null()); - assert(poly_pos_ == fluid_.numPhases()); - } - } - - - - template - void - FullyImplicitBlackoilPolymerSolver:: - setThresholdPressures(const std::vector& threshold_pressures_by_face) - { - const int num_faces = AutoDiffGrid::numFaces(grid_); - if (int(threshold_pressures_by_face.size()) != num_faces) { - OPM_THROW(std::runtime_error, "Illegal size of threshold_pressures_by_face input, must be equal to number of faces."); - } - use_threshold_pressure_ = true; - // Map to interior faces. - const int num_ifaces = ops_.internal_faces.size(); - threshold_pressures_by_interior_face_.resize(num_ifaces); - for (int ii = 0; ii < num_ifaces; ++ii) { - threshold_pressures_by_interior_face_[ii] = threshold_pressures_by_face[ops_.internal_faces[ii]]; - } - } - - - - - template - int - FullyImplicitBlackoilPolymerSolver:: - step(const double dt, - PolymerBlackoilState& x , - WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow) - { - const V pvdt = geo_.poreVolume() / dt; - - // Initial max concentration of this time step from PolymerBlackoilState. - cmax_ = Eigen::Map(&x.maxconcentration()[0], Opm::AutoDiffGrid::numCells(grid_)); - if (active_[Gas]) { updatePrimalVariableFromState(x); } - - // For each iteration we store in a vector the norms of the residual of - // the mass balance for each active phase, the well flux and the well equations - std::vector> residual_norms_history; - - assemble(pvdt, x, true, xw, polymer_inflow); - - - bool converged = false; - double omega = 1.; - - residual_norms_history.push_back(computeResidualNorms()); - - int it = 0; - converged = getConvergence(dt,it); - const int sizeNonLinear = residual_.sizeNonLinear(); - - V dxOld = V::Zero(sizeNonLinear); - - bool isOscillate = false; - bool isStagnate = false; - const enum RelaxType relaxtype = relaxType(); - int linearIterations = 0; - - while ((!converged && (it < maxIter())) || (minIter() > it)) { - V dx = solveJacobianSystem(); - - // store number of linear iterations used - linearIterations += linsolver_.iterations(); - - detectNewtonOscillations(residual_norms_history, it, relaxRelTol(), isOscillate, isStagnate); - - if (isOscillate) { - omega -= relaxIncrement(); - omega = std::max(omega, relaxMax()); - if (terminal_output_) - { - std::cout << " Oscillating behavior detected: Relaxation set to " << omega << std::endl; - } - } - - stablizeNewton(dx, dxOld, omega, relaxtype); - - updateState(dx, x, xw); - - assemble(pvdt, x, false, xw, polymer_inflow); - - residual_norms_history.push_back(computeResidualNorms()); - - // increase iteration counter - ++it; - - converged = getConvergence(dt,it); - } - - if (!converged) { - std::cerr << "WARNING: Failed to compute converged solution in " << it << " iterations." << std::endl; - return -1; // -1 indicates that the solver has to be restarted - } - - linearIterations_ += linearIterations; - newtonIterations_ += it; - // Update max concentration. - computeCmax(x); - return linearIterations; - } - - - - - - template - FullyImplicitBlackoilPolymerSolver::ReservoirResidualQuant::ReservoirResidualQuant() - : accum(2, ADB::null()) - , mflux( ADB::null()) - , b ( ADB::null()) - , head ( ADB::null()) - , mob ( ADB::null()) - { - } - - - - - - template - FullyImplicitBlackoilPolymerSolver::SolutionState::SolutionState(const int np) - : pressure ( ADB::null()) - , temperature( ADB::null()) - , saturation(np, ADB::null()) - , rs ( ADB::null()) - , rv ( ADB::null()) - , concentration( ADB::null()) - , qs ( ADB::null()) - , bhp ( ADB::null()) - , canonical_phase_pressures(3, ADB::null()) - { - } - - - - - - template - FullyImplicitBlackoilPolymerSolver:: - WellOps::WellOps(const Wells* wells) - : w2p(), - p2w() - { - if( wells ) - { - w2p = M(wells->well_connpos[ wells->number_of_wells ], wells->number_of_wells); - p2w = M(wells->number_of_wells, wells->well_connpos[ wells->number_of_wells ]); - - const int nw = wells->number_of_wells; - const int* const wpos = wells->well_connpos; - - typedef Eigen::Triplet Tri; - - std::vector scatter, gather; - scatter.reserve(wpos[nw]); - gather .reserve(wpos[nw]); - - for (int w = 0, i = 0; w < nw; ++w) { - for (; i < wpos[ w + 1 ]; ++i) { - scatter.push_back(Tri(i, w, 1.0)); - gather .push_back(Tri(w, i, 1.0)); - } - } - - w2p.setFromTriplets(scatter.begin(), scatter.end()); - p2w.setFromTriplets(gather .begin(), gather .end()); - } - } - - - - - - template - typename FullyImplicitBlackoilPolymerSolver::SolutionState - FullyImplicitBlackoilPolymerSolver::constantState(const PolymerBlackoilState& x, - const WellStateFullyImplicitBlackoil& xw) const - { - auto state = variableState(x, xw); - makeConstantState(state); - return state; - } - - - - - - template - void - FullyImplicitBlackoilPolymerSolver::makeConstantState(SolutionState& state) const - { - // HACK: throw away the derivatives. this may not be the most - // performant way to do things, but it will make the state - // automatically consistent with variableState() (and doing - // things automatically is all the rage in this module ;) - state.pressure = ADB::constant(state.pressure.value()); - state.temperature = ADB::constant(state.temperature.value()); - state.rs = ADB::constant(state.rs.value()); - state.rv = ADB::constant(state.rv.value()); - state.concentration = ADB::constant(state.concentration.value()); - const int num_phases = state.saturation.size(); - for (int phaseIdx = 0; phaseIdx < num_phases; ++ phaseIdx) { - state.saturation[phaseIdx] = ADB::constant(state.saturation[phaseIdx].value()); - } - state.qs = ADB::constant(state.qs.value()); - state.bhp = ADB::constant(state.bhp.value()); - assert(state.canonical_phase_pressures.size() == Opm::BlackoilPhases::MaxNumPhases); - for (int canphase = 0; canphase < Opm::BlackoilPhases::MaxNumPhases; ++canphase) { - ADB& pp = state.canonical_phase_pressures[canphase]; - pp = ADB::constant(pp.value()); - } - } - - - - - - template - typename FullyImplicitBlackoilPolymerSolver::SolutionState - FullyImplicitBlackoilPolymerSolver::variableState(const PolymerBlackoilState& x, - const WellStateFullyImplicitBlackoil& xw) const - { - using namespace Opm::AutoDiffGrid; - const int nc = numCells(grid_); - const int np = x.numPhases(); - - std::vector vars0; - // p, Sw and Rs, Rv or Sg, concentration is used as primary depending on solution conditions - vars0.reserve(np + 2); - // Initial pressure. - assert (not x.pressure().empty()); - const V p = Eigen::Map(& x.pressure()[0], nc, 1); - vars0.push_back(p); - - // Initial saturation. - assert (not x.saturation().empty()); - const DataBlock s = Eigen::Map(& x.saturation()[0], nc, np); - const Opm::PhaseUsage pu = fluid_.phaseUsage(); - // We do not handle a Water/Gas situation correctly, guard against it. - assert (active_[ Oil]); - if (active_[ Water ]) { - const V sw = s.col(pu.phase_pos[ Water ]); - vars0.push_back(sw); - } - - // store cell status in vectors - V isRs = V::Zero(nc,1); - V isRv = V::Zero(nc,1); - V isSg = V::Zero(nc,1); - - if (active_[ Gas ]){ - for (int c = 0; c < nc ; c++ ) { - switch (primalVariable_[c]) { - case PrimalVariables::RS: - isRs[c] = 1; - break; - - case PrimalVariables::RV: - isRv[c] = 1; - break; - - default: - isSg[c] = 1; - break; - } - } - - - // define new primary variable xvar depending on solution condition - V xvar(nc); - const V sg = s.col(pu.phase_pos[ Gas ]); - const V rs = Eigen::Map(& x.gasoilratio()[0], x.gasoilratio().size()); - const V rv = Eigen::Map(& x.rv()[0], x.rv().size()); - xvar = isRs*rs + isRv*rv + isSg*sg; - vars0.push_back(xvar); - } - - // Initial polymer concentration. - if (has_polymer_) { - assert (not x.concentration().empty()); - const V c = Eigen::Map(& x.concentration()[0], nc, 1); - vars0.push_back(c); - } - - // Initial well rates. - if( wellsActive() ) { - // Need to reshuffle well rates, from phase running fastest - // to wells running fastest. - const int nw = wells().number_of_wells; - - // The transpose() below switches the ordering. - const DataBlock wrates = Eigen::Map(& xw.wellRates()[0], nw, np).transpose(); - const V qs = Eigen::Map(wrates.data(), nw*np); - vars0.push_back(qs); - - // Initial well bottom-hole pressure. - assert (not xw.bhp().empty()); - const V bhp = Eigen::Map(& xw.bhp()[0], xw.bhp().size()); - vars0.push_back(bhp); - } else { - // push null states for qs and bhp - vars0.push_back(V()); - vars0.push_back(V()); - } - - std::vector vars = ADB::variables(vars0); - - SolutionState state(np); - - // Pressure. - int nextvar = 0; - state.pressure = std::move(vars[ nextvar++ ]); - - // temperature - const V temp = Eigen::Map(& x.temperature()[0], x.temperature().size()); - state.temperature = ADB::constant(temp); - - // Saturations - { - - ADB so = ADB::constant(V::Ones(nc, 1)); - - if (active_[ Water ]) { - state.saturation[pu.phase_pos[ Water ]] = std::move(vars[ nextvar++ ]); - const ADB& sw = state.saturation[pu.phase_pos[ Water ]]; - so -= sw; - } - - if (active_[ Gas ]) { - // Define Sg Rs and Rv in terms of xvar. - // Xvar is only defined if gas phase is active - const ADB& xvar = vars[ nextvar++ ]; - ADB& sg = state.saturation[ pu.phase_pos[ Gas ] ]; - sg = isSg*xvar + isRv* so; - so -= sg; - - if (active_[ Oil ]) { - // RS and RV is only defined if both oil and gas phase are active. - const ADB& sw = (active_[ Water ] - ? state.saturation[ pu.phase_pos[ Water ] ] - : ADB::constant(V::Zero(nc, 1))); - state.canonical_phase_pressures = computePressures(state.pressure, sw, so, sg); - const ADB rsSat = fluidRsSat(state.canonical_phase_pressures[ Oil ], so , cells_); - if (has_disgas_) { - state.rs = (1-isRs) * rsSat + isRs*xvar; - } else { - state.rs = rsSat; - } - const ADB rvSat = fluidRvSat(state.canonical_phase_pressures[ Gas ], so , cells_); - if (has_vapoil_) { - state.rv = (1-isRv) * rvSat + isRv*xvar; - } else { - state.rv = rvSat; - } - } - } - - if (active_[ Oil ]) { - // Note that so is never a primary variable. - state.saturation[pu.phase_pos[ Oil ]] = std::move(so); - } - } - - // Concentration. - if (has_polymer_) { - state.concentration = std::move(vars[ nextvar++ ]); - } - - // Qs. - state.qs = std::move(vars[ nextvar++ ]); - - // Bhp. - state.bhp = std::move(vars[ nextvar++ ]); - - assert(nextvar == int(vars.size())); - - return state; - } - - - - - - template - void - FullyImplicitBlackoilPolymerSolver::computeAccum(const SolutionState& state, - const int aix ) - { - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - - const ADB& press = state.pressure; - const ADB& temp = state.temperature; - const std::vector& sat = state.saturation; - const ADB& rs = state.rs; - const ADB& rv = state.rv; - const ADB& c = state.concentration; - - const std::vector cond = phaseCondition(); - - const ADB pv_mult = poroMult(press); - - const int maxnp = Opm::BlackoilPhases::MaxNumPhases; - for (int phase = 0; phase < maxnp; ++phase) { - if (active_[ phase ]) { - const int pos = pu.phase_pos[ phase ]; - rq_[pos].b = fluidReciprocFVF(phase, state.canonical_phase_pressures[phase], temp, rs, rv, cond, cells_); - rq_[pos].accum[aix] = pv_mult * rq_[pos].b * sat[pos]; - // DUMP(rq_[pos].b); - // DUMP(rq_[pos].accum[aix]); - } - } - - if (active_[ Oil ] && active_[ Gas ]) { - // Account for gas dissolved in oil and vaporized oil - const int po = pu.phase_pos[ Oil ]; - const int pg = pu.phase_pos[ Gas ]; - - // Temporary copy to avoid contribution of dissolved gas in the vaporized oil - // when both dissolved gas and vaporized oil are present. - const ADB accum_gas_copy =rq_[pg].accum[aix]; - - rq_[pg].accum[aix] += state.rs * rq_[po].accum[aix]; - rq_[po].accum[aix] += state.rv * accum_gas_copy; - //DUMP(rq_[pg].accum[aix]); - } - - if (has_polymer_) { - // compute polymer properties. - const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); - const ADB ads = polymer_props_ad_.adsorption(state.concentration, cmax); - const double rho_rock = polymer_props_ad_.rockDensity(); - const V phi = Eigen::Map(& fluid_.porosity()[0], AutoDiffGrid::numCells(grid_), 1); - const double dead_pore_vol = polymer_props_ad_.deadPoreVol(); - // compute total phases and determin polymer position. - rq_[poly_pos_].accum[aix] = pv_mult * rq_[pu.phase_pos[Water]].b * sat[pu.phase_pos[Water]] * c * (1. - dead_pore_vol) - + pv_mult * rho_rock * (1. - phi) / phi * ads; - } - - } - - - - - - - template - void FullyImplicitBlackoilPolymerSolver::computeCmax(PolymerBlackoilState& state) - { - const int nc = AutoDiffGrid::numCells(grid_); - V tmp = V::Zero(nc); - for (int i = 0; i < nc; ++i) { - tmp[i] = std::max(state.maxconcentration()[i], state.concentration()[i]); - } - std::copy(&tmp[0], &tmp[0] + nc, state.maxconcentration().begin()); - } - - - - - - template - void FullyImplicitBlackoilPolymerSolver::computeWellConnectionPressures(const SolutionState& state, - const WellStateFullyImplicitBlackoil& xw) - { - if( ! wellsActive() ) return ; - - using namespace Opm::AutoDiffGrid; - // 1. Compute properties required by computeConnectionPressureDelta(). - // Note that some of the complexity of this part is due to the function - // taking std::vector arguments, and not Eigen objects. - const int nperf = wells().well_connpos[wells().number_of_wells]; - const int nw = wells().number_of_wells; - const std::vector well_cells(wells().well_cells, wells().well_cells + nperf); - - // Compute the average pressure in each well block - const V perf_press = Eigen::Map(xw.perfPress().data(), nperf); - V avg_press = perf_press*0; - for (int w = 0; w < nw; ++w) { - for (int perf = wells().well_connpos[w]; perf < wells().well_connpos[w+1]; ++perf) { - const double p_above = perf == wells().well_connpos[w] ? state.bhp.value()[w] : perf_press[perf - 1]; - const double p_avg = (perf_press[perf] + p_above)/2; - avg_press[perf] = p_avg; - } - } - - // Use cell values for the temperature as the wells don't knows its temperature yet. - const ADB perf_temp = subset(state.temperature, well_cells); - - // Compute b, rsmax, rvmax values for perforations. - // Evaluate the properties using average well block pressures - // and cell values for rs, rv, phase condition and temperature. - const ADB avg_press_ad = ADB::constant(avg_press); - std::vector perf_cond(nperf); - const std::vector& pc = phaseCondition(); - for (int perf = 0; perf < nperf; ++perf) { - perf_cond[perf] = pc[well_cells[perf]]; - } - const PhaseUsage& pu = fluid_.phaseUsage(); - DataBlock b(nperf, pu.num_phases); - std::vector rsmax_perf(nperf, 0.0); - std::vector rvmax_perf(nperf, 0.0); - if (pu.phase_used[BlackoilPhases::Aqua]) { - const V bw = fluid_.bWat(avg_press_ad, perf_temp, well_cells).value(); - b.col(pu.phase_pos[BlackoilPhases::Aqua]) = bw; - } - assert(active_[Oil]); - const V perf_so = subset(state.saturation[pu.phase_pos[Oil]].value(), well_cells); - if (pu.phase_used[BlackoilPhases::Liquid]) { - const ADB perf_rs = subset(state.rs, well_cells); - const V bo = fluid_.bOil(avg_press_ad, perf_temp, perf_rs, perf_cond, well_cells).value(); - b.col(pu.phase_pos[BlackoilPhases::Liquid]) = bo; - const V rssat = fluidRsSat(avg_press, perf_so, well_cells); - rsmax_perf.assign(rssat.data(), rssat.data() + nperf); - } - if (pu.phase_used[BlackoilPhases::Vapour]) { - const ADB perf_rv = subset(state.rv, well_cells); - const V bg = fluid_.bGas(avg_press_ad, perf_temp, perf_rv, perf_cond, well_cells).value(); - b.col(pu.phase_pos[BlackoilPhases::Vapour]) = bg; - const V rvsat = fluidRvSat(avg_press, perf_so, well_cells); - rvmax_perf.assign(rvsat.data(), rvsat.data() + nperf); - } - // b is row major, so can just copy data. - std::vector b_perf(b.data(), b.data() + nperf * pu.num_phases); - // Extract well connection depths. - const V depth = cellCentroidsZToEigen(grid_); - const V pdepth = subset(depth, well_cells); - std::vector perf_depth(pdepth.data(), pdepth.data() + nperf); - // Surface density. - std::vector surf_dens(fluid_.surfaceDensity(), fluid_.surfaceDensity() + pu.num_phases); - // Gravity - double grav = 0.0; - const double* g = geo_.gravity(); - const int dim = dimensions(grid_); - if (g) { - // Guard against gravity in anything but last dimension. - for (int dd = 0; dd < dim - 1; ++dd) { - assert(g[dd] == 0.0); - } - grav = g[dim - 1]; - } - - // 2. Compute pressure deltas, and store the results. - std::vector cdp = WellDensitySegmented - ::computeConnectionPressureDelta(wells(), xw, fluid_.phaseUsage(), - b_perf, rsmax_perf, rvmax_perf, perf_depth, - surf_dens, grav); - well_perforation_pressure_diffs_ = Eigen::Map(cdp.data(), nperf); - } - - - - - - template - void - FullyImplicitBlackoilPolymerSolver:: - assemble(const V& pvdt, - const PolymerBlackoilState& x , - const bool initial_assembly, - WellStateFullyImplicitBlackoil& xw, - const std::vector& polymer_inflow) - { - using namespace Opm::AutoDiffGrid; - - // Possibly switch well controls and updating well state to - // get reasonable initial conditions for the wells - updateWellControls(xw); - - // Create the primary variables. - SolutionState state = variableState(x, xw); - - if (initial_assembly) { - // Create the (constant, derivativeless) initial state. - SolutionState state0 = state; - makeConstantState(state0); - // Compute initial accumulation contributions - // and well connection pressures. - computeAccum(state0, 0); - computeWellConnectionPressures(state0, xw); - } - - // DISKVAL(state.pressure); - // DISKVAL(state.saturation[0]); - // DISKVAL(state.saturation[1]); - // DISKVAL(state.saturation[2]); - // DISKVAL(state.rs); - // DISKVAL(state.rv); - // DISKVAL(state.qs); - // DISKVAL(state.bhp); - - // -------- Mass balance equations -------- - - // Compute b_p and the accumulation term b_p*s_p for each phase, - // except gas. For gas, we compute b_g*s_g + Rs*b_o*s_o. - // These quantities are stored in rq_[phase].accum[1]. - // The corresponding accumulation terms from the start of - // the timestep (b^0_p*s^0_p etc.) were already computed - // on the initial call to assemble() and stored in rq_[phase].accum[0]. - computeAccum(state, 1); - - // Set up the common parts of the mass balance equations - // for each active phase. - const V transi = subset(geo_.transmissibility(), ops_.internal_faces); - const std::vector kr = computeRelPerm(state); - for (int phaseIdx = 0; phaseIdx < fluid_.numPhases(); ++phaseIdx) { - computeMassFlux(phaseIdx, transi, kr[canph_[phaseIdx]], state.canonical_phase_pressures[canph_[phaseIdx]], state); - residual_.material_balance_eq[ phaseIdx ] = - pvdt*(rq_[phaseIdx].accum[1] - rq_[phaseIdx].accum[0]) - + ops_.div*rq_[phaseIdx].mflux; - } - - // -------- Extra (optional) rs and rv contributions to the mass balance equations -------- - - // Add the extra (flux) terms to the mass balance equations - // From gas dissolved in the oil phase (rs) and oil vaporized in the gas phase (rv) - // The extra terms in the accumulation part of the equation are already handled. - if (active_[ Oil ] && active_[ Gas ]) { - const int po = fluid_.phaseUsage().phase_pos[ Oil ]; - const int pg = fluid_.phaseUsage().phase_pos[ Gas ]; - - const UpwindSelector upwindOil(grid_, ops_, - rq_[po].head.value()); - const ADB rs_face = upwindOil.select(state.rs); - - const UpwindSelector upwindGas(grid_, ops_, - rq_[pg].head.value()); - const ADB rv_face = upwindGas.select(state.rv); - - residual_.material_balance_eq[ pg ] += ops_.div * (rs_face * rq_[po].mflux); - residual_.material_balance_eq[ po ] += ops_.div * (rv_face * rq_[pg].mflux); - - // DUMP(residual_.material_balance_eq[ Gas ]); - - } - - // Add polymer equation. - if (has_polymer_) { - residual_.material_balance_eq[poly_pos_] = pvdt*(rq_[poly_pos_].accum[1] - rq_[poly_pos_].accum[0]) - + ops_.div*rq_[poly_pos_].mflux; - } - - // Add contribution from wells and set up the well equations. - V aliveWells; - addWellEq(state, xw, aliveWells, polymer_inflow); - addWellControlEq(state, xw, aliveWells); - } - - - - - - template - void FullyImplicitBlackoilPolymerSolver::addWellEq(const SolutionState& state, - WellStateFullyImplicitBlackoil& xw, - V& aliveWells, - const std::vector& polymer_inflow) - { - if( ! wellsActive() ) return ; - - const int nc = Opm::AutoDiffGrid::numCells(grid_); - const int np = wells().number_of_phases; - const int nw = wells().number_of_wells; - const int nperf = wells().well_connpos[nw]; - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - V Tw = Eigen::Map(wells().WI, nperf); - const std::vector well_cells(wells().well_cells, wells().well_cells + nperf); - - // pressure diffs computed already (once per step, not changing per iteration) - const V& cdp = well_perforation_pressure_diffs_; - - // Extract needed quantities for the perforation cells - const ADB& p_perfcells = subset(state.pressure, well_cells); - const ADB& rv_perfcells = subset(state.rv,well_cells); - const ADB& rs_perfcells = subset(state.rs,well_cells); - std::vector mob_perfcells(np, ADB::null()); - std::vector b_perfcells(np, ADB::null()); - for (int phase = 0; phase < np; ++phase) { - mob_perfcells[phase] = subset(rq_[phase].mob,well_cells); - b_perfcells[phase] = subset(rq_[phase].b,well_cells); - } - - // Perforation pressure - const ADB perfpressure = (wops_.w2p * state.bhp) + cdp; - std::vector perfpressure_d(perfpressure.value().data(), perfpressure.value().data() + nperf); - xw.perfPress() = perfpressure_d; - - // Pressure drawdown (also used to determine direction of flow) - const ADB drawdown = p_perfcells - perfpressure; - - // Compute vectors with zero and ones that - // selects the wanted quantities. - - // selects injection perforations - V selectInjectingPerforations = V::Zero(nperf); - // selects producing perforations - V selectProducingPerforations = V::Zero(nperf); - for (int c = 0; c < nperf; ++c){ - if (drawdown.value()[c] < 0) - selectInjectingPerforations[c] = 1; - else - selectProducingPerforations[c] = 1; - } - - // HANDLE FLOW INTO WELLBORE - - // compute phase volumetric rates at standard conditions - std::vector cq_ps(np, ADB::null()); - for (int phase = 0; phase < np; ++phase) { - const ADB cq_p = -(selectProducingPerforations * Tw) * (mob_perfcells[phase] * drawdown); - cq_ps[phase] = b_perfcells[phase] * cq_p; - } - if (active_[Oil] && active_[Gas]) { - const int oilpos = pu.phase_pos[Oil]; - const int gaspos = pu.phase_pos[Gas]; - const ADB cq_psOil = cq_ps[oilpos]; - const ADB cq_psGas = cq_ps[gaspos]; - cq_ps[gaspos] += rs_perfcells * cq_psOil; - cq_ps[oilpos] += rv_perfcells * cq_psGas; - } - - // HANDLE FLOW OUT FROM WELLBORE - - // Using total mobilities - ADB total_mob = mob_perfcells[0]; - for (int phase = 1; phase < np; ++phase) { - total_mob += mob_perfcells[phase]; - } - // injection perforations total volume rates - const ADB cqt_i = -(selectInjectingPerforations * Tw) * (total_mob * drawdown); - - // compute wellbore mixture for injecting perforations - // The wellbore mixture depends on the inflow from the reservoar - // and the well injection rates. - - // compute avg. and total wellbore phase volumetric rates at standard conds - const DataBlock compi = Eigen::Map(wells().comp_frac, nw, np); - std::vector wbq(np, ADB::null()); - ADB wbqt = ADB::constant(V::Zero(nw)); - for (int phase = 0; phase < np; ++phase) { - const ADB& q_ps = wops_.p2w * cq_ps[phase]; - const ADB& q_s = subset(state.qs, Span(nw, 1, phase*nw)); - Selector injectingPhase_selector(q_s.value(), Selector::GreaterZero); - const int pos = pu.phase_pos[phase]; - wbq[phase] = (compi.col(pos) * injectingPhase_selector.select(q_s,ADB::constant(V::Zero(nw)))) - q_ps; - wbqt += wbq[phase]; - } - - // compute wellbore mixture at standard conditions. - Selector notDeadWells_selector(wbqt.value(), Selector::Zero); - std::vector cmix_s(np, ADB::null()); - for (int phase = 0; phase < np; ++phase) { - const int pos = pu.phase_pos[phase]; - cmix_s[phase] = wops_.w2p * notDeadWells_selector.select(ADB::constant(compi.col(pos)), wbq[phase]/wbqt); - } - - // compute volume ratio between connection at standard conditions - ADB volumeRatio = ADB::constant(V::Zero(nperf)); - const ADB d = V::Constant(nperf,1.0) - rv_perfcells * rs_perfcells; - for (int phase = 0; phase < np; ++phase) { - ADB tmp = cmix_s[phase]; - - if (phase == Oil && active_[Gas]) { - const int gaspos = pu.phase_pos[Gas]; - tmp = tmp - rv_perfcells * cmix_s[gaspos] / d; - } - if (phase == Gas && active_[Oil]) { - const int oilpos = pu.phase_pos[Oil]; - tmp = tmp - rs_perfcells * cmix_s[oilpos] / d; - } - volumeRatio += tmp / b_perfcells[phase]; - } - - // injecting connections total volumerates at standard conditions - ADB cqt_is = cqt_i/volumeRatio; - - // connection phase volumerates at standard conditions - std::vector cq_s(np, ADB::null()); - for (int phase = 0; phase < np; ++phase) { - cq_s[phase] = cq_ps[phase] + cmix_s[phase]*cqt_is; - } - - // Add well contributions to mass balance equations - for (int phase = 0; phase < np; ++phase) { - residual_.material_balance_eq[phase] -= superset(cq_s[phase],well_cells,nc); - } - - // Add well contributions to polymer mass balance equation - if (has_polymer_) { - const ADB mc = computeMc(state); - const V polyin = Eigen::Map(&polymer_inflow[0], nc); - const V poly_in_perf = subset(polyin, well_cells); - const V poly_mc_perf = subset(mc, well_cells).value(); - residual_.material_balance_eq[poly_pos_] -= superset(cq_ps[pu.phase_pos[Water]] * poly_mc_perf - + cmix_s[pu.phase_pos[Water]] * cqt_is*poly_in_perf, - well_cells, nc); - } - - - // WELL EQUATIONS - ADB qs = state.qs; - for (int phase = 0; phase < np; ++phase) { - qs -= superset(wops_.p2w * cq_s[phase], Span(nw, 1, phase*nw), nw*np); - - } - - // check for dead wells (used in the well controll equations) - aliveWells = V::Constant(nw, 1.0); - for (int w = 0; w < nw; ++w) { - if (wbqt.value()[w] == 0) { - aliveWells[w] = 0.0; - } - } - - // Update the perforation phase rates (used to calculate the pressure drop in the wellbore) - V cq = superset(cq_s[0].value(), Span(nperf, np, 0), nperf*np); - for (int phase = 1; phase < np; ++phase) { - cq += superset(cq_s[phase].value(), Span(nperf, np, phase), nperf*np); - } - - std::vector cq_d(cq.data(), cq.data() + nperf*np); - xw.perfPhaseRates() = cq_d; - - residual_.well_flux_eq = qs; - } - - - - - - namespace detail - { - double rateToCompare(const std::vector& well_phase_flow_rate, - const int well, - const int num_phases, - const double* distr) - { - double rate = 0.0; - for (int phase = 0; phase < num_phases; ++phase) { - // Important: well_phase_flow_rate is ordered with all phase rates for first - // well first, then all phase rates for second well etc. - rate += well_phase_flow_rate[well*num_phases + phase] * distr[phase]; - } - return rate; - } - - bool constraintBroken(const std::vector& bhp, - const std::vector& well_phase_flow_rate, - const int well, - const int num_phases, - const WellType& well_type, - const WellControls* wc, - const int ctrl_index) - { - const WellControlType ctrl_type = well_controls_iget_type(wc, ctrl_index); - const double target = well_controls_iget_target(wc, ctrl_index); - const double* distr = well_controls_iget_distr(wc, ctrl_index); - - bool broken = false; - - switch (well_type) { - case INJECTOR: - { - switch (ctrl_type) { - case BHP: - broken = bhp[well] > target; - break; - - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - broken = rateToCompare(well_phase_flow_rate, - well, num_phases, distr) > target; - break; - } - } - break; - - case PRODUCER: - { - switch (ctrl_type) { - case BHP: - broken = bhp[well] < target; - break; - - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - // Note that the rates compared below are negative, - // so breaking the constraints means: too high flow rate - // (as for injection). - broken = rateToCompare(well_phase_flow_rate, - well, num_phases, distr) < target; - break; - } - } - break; - - default: - OPM_THROW(std::logic_error, "Can only handle INJECTOR and PRODUCER wells."); - } - - return broken; - } - } // namespace detail - - - - - template - void FullyImplicitBlackoilPolymerSolver::updateWellControls(WellStateFullyImplicitBlackoil& xw) const - { - if( ! wellsActive() ) return ; - - std::string modestring[3] = { "BHP", "RESERVOIR_RATE", "SURFACE_RATE" }; - // Find, for each well, if any constraints are broken. If so, - // switch control to first broken constraint. - const int np = wells().number_of_phases; - const int nw = wells().number_of_wells; - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - // The current control in the well state overrides - // the current control set in the Wells struct, which - // is instead treated as a default. - int current = xw.currentControls()[w]; - // Loop over all controls except the current one, and also - // skip any RESERVOIR_RATE controls, since we cannot - // handle those. - const int nwc = well_controls_get_num(wc); - int ctrl_index = 0; - for (; ctrl_index < nwc; ++ctrl_index) { - if (ctrl_index == current) { - // This is the currently used control, so it is - // used as an equation. So this is not used as an - // inequality constraint, and therefore skipped. - continue; - } - if (detail::constraintBroken(xw.bhp(), xw.wellRates(), w, np, wells().type[w], wc, ctrl_index)) { - // ctrl_index will be the index of the broken constraint after the loop. - break; - } - } - if (ctrl_index != nwc) { - // Constraint number ctrl_index was broken, switch to it. - if (terminal_output_) - { - std::cout << "Switching control mode for well " << wells().name[w] - << " from " << modestring[well_controls_iget_type(wc, current)] - << " to " << modestring[well_controls_iget_type(wc, ctrl_index)] << std::endl; - } - xw.currentControls()[w] = ctrl_index; - current = xw.currentControls()[w]; - } - - // Updating well state and primary variables. - // Target values are used as initial conditions for BHP and SURFACE_RATE - const double target = well_controls_iget_target(wc, current); - const double* distr = well_controls_iget_distr(wc, current); - switch (well_controls_iget_type(wc, current)) { - case BHP: - xw.bhp()[w] = target; - break; - - case RESERVOIR_RATE: - // No direct change to any observable quantity at - // surface condition. In this case, use existing - // flow rates as initial conditions as reservoir - // rate acts only in aggregate. - break; - - case SURFACE_RATE: - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - xw.wellRates()[np*w + phase] = target * distr[phase]; - } - } - break; - } - - } - } - - - - template - void FullyImplicitBlackoilPolymerSolver::addWellControlEq(const SolutionState& state, - const WellStateFullyImplicitBlackoil& xw, - const V& aliveWells) - { - if( ! wellsActive() ) return; - - const int np = wells().number_of_phases; - const int nw = wells().number_of_wells; - - V bhp_targets = V::Zero(nw); - V rate_targets = V::Zero(nw); - M rate_distr(nw, np*nw); - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - // The current control in the well state overrides - // the current control set in the Wells struct, which - // is instead treated as a default. - const int current = xw.currentControls()[w]; - - switch (well_controls_iget_type(wc, current)) { - case BHP: - { - bhp_targets (w) = well_controls_iget_target(wc, current); - rate_targets(w) = -1e100; - } - break; - - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - { - // RESERVOIR and SURFACE rates look the same, from a - // high-level point of view, in the system of - // simultaneous linear equations. - - const double* const distr = - well_controls_iget_distr(wc, current); - - for (int p = 0; p < np; ++p) { - rate_distr.insert(w, p*nw + w) = distr[p]; - } - - bhp_targets (w) = -1.0e100; - rate_targets(w) = well_controls_iget_target(wc, current); - } - break; - } - } - const ADB bhp_residual = state.bhp - bhp_targets; - const ADB rate_residual = rate_distr * state.qs - rate_targets; - // Choose bhp residual for positive bhp targets. - Selector bhp_selector(bhp_targets); - residual_.well_eq = bhp_selector.select(bhp_residual, rate_residual); - // For wells that are dead (not flowing), and therefore not communicating - // with the reservoir, we set the equation to be equal to the well's total - // flow. This will be a solution only if the target rate is also zero. - M rate_summer(nw, np*nw); - for (int w = 0; w < nw; ++w) { - for (int phase = 0; phase < np; ++phase) { - rate_summer.insert(w, phase*nw + w) = 1.0; - } - } - Selector alive_selector(aliveWells, Selector::NotEqualZero); - residual_.well_eq = alive_selector.select(residual_.well_eq, rate_summer * state.qs); - // DUMP(residual_.well_eq); - } - - - - - - template - V FullyImplicitBlackoilPolymerSolver::solveJacobianSystem() const - { - return linsolver_.computeNewtonIncrement(residual_); - } - - - - - - namespace detail - { - - double infinityNorm( const ADB& a ) - { - if( a.value().size() > 0 ) { - return a.value().matrix().lpNorm (); - } - else { // this situation can occur when no wells are present - return 0.0; - } - } - - } // namespace detail - - - - - - template - void FullyImplicitBlackoilPolymerSolver::updateState(const V& dx, - PolymerBlackoilState& state, - WellStateFullyImplicitBlackoil& well_state) - { - using namespace Opm::AutoDiffGrid; - const int np = fluid_.numPhases(); - const int nc = numCells(grid_); - const int nw = wellsActive() ? wells().number_of_wells : 0; - const V null; - assert(null.size() == 0); - const V zero = V::Zero(nc); - - // store cell status in vectors - V isRs = V::Zero(nc,1); - V isRv = V::Zero(nc,1); - V isSg = V::Zero(nc,1); - if (active_[Gas]) { - for (int c = 0; c < nc; ++c) { - switch (primalVariable_[c]) { - case PrimalVariables::RS: - isRs[c] = 1; - break; - - case PrimalVariables::RV: - isRv[c] = 1; - break; - - default: - isSg[c] = 1; - break; - } - } - } - - // Extract parts of dx corresponding to each part. - const V dp = subset(dx, Span(nc)); - int varstart = nc; - const V dsw = active_[Water] ? subset(dx, Span(nc, 1, varstart)) : null; - varstart += dsw.size(); - - const V dxvar = active_[Gas] ? subset(dx, Span(nc, 1, varstart)): null; - varstart += dxvar.size(); - - const V dc = (has_polymer_) ? subset(dx, Span(nc, 1, varstart)) : null; - varstart += dc.size(); - - const V dqs = subset(dx, Span(np*nw, 1, varstart)); - varstart += dqs.size(); - const V dbhp = subset(dx, Span(nw, 1, varstart)); - varstart += dbhp.size(); - assert(varstart == dx.size()); - - // Pressure update. - const double dpmaxrel = dpMaxRel(); - const V p_old = Eigen::Map(&state.pressure()[0], nc, 1); - const V absdpmax = dpmaxrel*p_old.abs(); - const V dp_limited = sign(dp) * dp.abs().min(absdpmax); - const V p = (p_old - dp_limited).max(zero); - std::copy(&p[0], &p[0] + nc, state.pressure().begin()); - - - // Saturation updates. - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - const DataBlock s_old = Eigen::Map(& state.saturation()[0], nc, np); - const double dsmax = dsMax(); - - V so; - V sw; - V sg; - - { - V maxVal = zero; - V dso = zero; - if (active_[Water]){ - maxVal = dsw.abs().max(maxVal); - dso = dso - dsw; - } - - V dsg; - if (active_[Gas]){ - dsg = isSg * dxvar - isRv * dsw; - maxVal = dsg.abs().max(maxVal); - dso = dso - dsg; - } - - maxVal = dso.abs().max(maxVal); - - V step = dsmax/maxVal; - step = step.min(1.); - - if (active_[Water]) { - const int pos = pu.phase_pos[ Water ]; - const V sw_old = s_old.col(pos); - sw = sw_old - step * dsw; - } - - if (active_[Gas]) { - const int pos = pu.phase_pos[ Gas ]; - const V sg_old = s_old.col(pos); - sg = sg_old - step * dsg; - } - - const int pos = pu.phase_pos[ Oil ]; - const V so_old = s_old.col(pos); - so = so_old - step * dso; - } - - // Appleyard chop process. - auto ixg = sg < 0; - for (int c = 0; c < nc; ++c) { - if (ixg[c]) { - sw[c] = sw[c] / (1-sg[c]); - so[c] = so[c] / (1-sg[c]); - sg[c] = 0; - } - } - - - auto ixo = so < 0; - for (int c = 0; c < nc; ++c) { - if (ixo[c]) { - sw[c] = sw[c] / (1-so[c]); - sg[c] = sg[c] / (1-so[c]); - so[c] = 0; - } - } - - auto ixw = sw < 0; - for (int c = 0; c < nc; ++c) { - if (ixw[c]) { - so[c] = so[c] / (1-sw[c]); - sg[c] = sg[c] / (1-sw[c]); - sw[c] = 0; - } - } - - const V sumSat = sw + so + sg; - sw = sw / sumSat; - so = so / sumSat; - sg = sg / sumSat; - - // Update the state - for (int c = 0; c < nc; ++c) { - state.saturation()[c*np + pu.phase_pos[ Water ]] = sw[c]; - } - - for (int c = 0; c < nc; ++c) { - state.saturation()[c*np + pu.phase_pos[ Gas ]] = sg[c]; - } - - if (active_[ Oil ]) { - const int pos = pu.phase_pos[ Oil ]; - for (int c = 0; c < nc; ++c) { - state.saturation()[c*np + pos] = so[c]; - } - } - - // Update rs and rv - const double drmaxrel = drMaxRel(); - V rs; - if (has_disgas_) { - const V rs_old = Eigen::Map(&state.gasoilratio()[0], nc); - const V drs = isRs * dxvar; - const V drs_limited = sign(drs) * drs.abs().min(rs_old.abs()*drmaxrel); - rs = rs_old - drs_limited; - } - V rv; - if (has_vapoil_) { - const V rv_old = Eigen::Map(&state.rv()[0], nc); - const V drv = isRv * dxvar; - const V drv_limited = sign(drv) * drv.abs().min(rv_old.abs()*drmaxrel); - rv = rv_old - drv_limited; - } - - - // Sg is used as primal variable for water only cells. - const double epsilon = std::sqrt(std::numeric_limits::epsilon()); - auto watOnly = sw > (1 - epsilon); - - // phase translation sg <-> rs - std::fill(primalVariable_.begin(), primalVariable_.end(), PrimalVariables::Sg); - - if (has_disgas_) { - const V rsSat0 = fluidRsSat(p_old, s_old.col(pu.phase_pos[Oil]), cells_); - const V rsSat = fluidRsSat(p, so, cells_); - // The obvious case - auto hasGas = (sg > 0 && isRs == 0); - - // Set oil saturated if previous rs is sufficiently large - const V rs_old = Eigen::Map(&state.gasoilratio()[0], nc); - auto gasVaporized = ( (rs > rsSat * (1+epsilon) && isRs == 1 ) && (rs_old > rsSat0 * (1-epsilon)) ); - auto useSg = watOnly || hasGas || gasVaporized; - for (int c = 0; c < nc; ++c) { - if (useSg[c]) { - rs[c] = rsSat[c]; - } else { - primalVariable_[c] = PrimalVariables::RS; - } - } - - } - - // phase transitions so <-> rv - if (has_vapoil_) { - - // The gas pressure is needed for the rvSat calculations - const V gaspress_old = computeGasPressure(p_old, s_old.col(Water), s_old.col(Oil), s_old.col(Gas)); - const V gaspress = computeGasPressure(p, sw, so, sg); - const V rvSat0 = fluidRvSat(gaspress_old, s_old.col(pu.phase_pos[Oil]), cells_); - const V rvSat = fluidRvSat(gaspress, so, cells_); - - // The obvious case - auto hasOil = (so > 0 && isRv == 0); - - // Set oil saturated if previous rv is sufficiently large - const V rv_old = Eigen::Map(&state.rv()[0], nc); - auto oilCondensed = ( (rv > rvSat * (1+epsilon) && isRv == 1) && (rv_old > rvSat0 * (1-epsilon)) ); - auto useSg = watOnly || hasOil || oilCondensed; - for (int c = 0; c < nc; ++c) { - if (useSg[c]) { - rv[c] = rvSat[c]; - } else { - primalVariable_[c] = PrimalVariables::RV; - } - } - - } - - // Update the state - if (has_disgas_) { - std::copy(&rs[0], &rs[0] + nc, state.gasoilratio().begin()); - } - - if (has_vapoil_) { - std::copy(&rv[0], &rv[0] + nc, state.rv().begin()); - } - - //Polymer concentration updates. - if (has_polymer_) { - const V c_old = Eigen::Map(&state.concentration()[0], nc, 1); - const V c = (c_old - dc).max(zero); - std::copy(&c[0], &c[0] + nc, state.concentration().begin()); - } - - if( wellsActive() ) - { - // Qs update. - // Since we need to update the wellrates, that are ordered by wells, - // from dqs which are ordered by phase, the simplest is to compute - // dwr, which is the data from dqs but ordered by wells. - const DataBlock wwr = Eigen::Map(dqs.data(), np, nw).transpose(); - const V dwr = Eigen::Map(wwr.data(), nw*np); - const V wr_old = Eigen::Map(&well_state.wellRates()[0], nw*np); - const V wr = wr_old - dwr; - std::copy(&wr[0], &wr[0] + wr.size(), well_state.wellRates().begin()); - - // Bhp update. - const V bhp_old = Eigen::Map(&well_state.bhp()[0], nw, 1); - const V dbhp_limited = sign(dbhp) * dbhp.abs().min(bhp_old.abs()*dpmaxrel); - const V bhp = bhp_old - dbhp_limited; - std::copy(&bhp[0], &bhp[0] + bhp.size(), well_state.bhp().begin()); - } - - // Update phase conditions used for property calculations. - updatePhaseCondFromPrimalVariable(); - } - - - - - - template - std::vector - FullyImplicitBlackoilPolymerSolver::computeRelPerm(const SolutionState& state) const - { - using namespace Opm::AutoDiffGrid; - const int nc = numCells(grid_); - - const ADB zero = ADB::constant(V::Zero(nc)); - - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - const ADB& sw = (active_[ Water ] - ? state.saturation[ pu.phase_pos[ Water ] ] - : zero); - - const ADB& so = (active_[ Oil ] - ? state.saturation[ pu.phase_pos[ Oil ] ] - : zero); - - const ADB& sg = (active_[ Gas ] - ? state.saturation[ pu.phase_pos[ Gas ] ] - : zero); - - return fluid_.relperm(sw, so, sg, cells_); - } - - - - - - template - std::vector - FullyImplicitBlackoilPolymerSolver::computePressures(const SolutionState& state) const - { - using namespace Opm::AutoDiffGrid; - const int nc = numCells(grid_); - - const ADB null = ADB::constant(V::Zero(nc)); - - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - const ADB& sw = (active_[ Water ] - ? state.saturation[ pu.phase_pos[ Water ] ] - : null); - - const ADB& so = (active_[ Oil ] - ? state.saturation[ pu.phase_pos[ Oil ] ] - : null); - - const ADB& sg = (active_[ Gas ] - ? state.saturation[ pu.phase_pos[ Gas ] ] - : null); - return computePressures(state.pressure, sw, so, sg); - - - } - - - - - - template - std::vector - FullyImplicitBlackoilPolymerSolver:: - computePressures(const ADB& po, - const ADB& sw, - const ADB& so, - const ADB& sg) const - { - // convert the pressure offsets to the capillary pressures - std::vector pressure = fluid_.capPress(sw, so, sg, cells_); - for (int phaseIdx = 0; phaseIdx < BlackoilPhases::MaxNumPhases; ++phaseIdx) { - // The reference pressure is always the liquid phase (oil) pressure. - if (phaseIdx == BlackoilPhases::Liquid) - continue; - pressure[phaseIdx] = pressure[phaseIdx] - pressure[BlackoilPhases::Liquid]; - } - - // Since pcow = po - pw, but pcog = pg - po, - // we have - // pw = po - pcow - // pg = po + pcgo - // This is an unfortunate inconsistency, but a convention we must handle. - for (int phaseIdx = 0; phaseIdx < BlackoilPhases::MaxNumPhases; ++phaseIdx) { - if (phaseIdx == BlackoilPhases::Aqua) { - pressure[phaseIdx] = po - pressure[phaseIdx]; - } else { - pressure[phaseIdx] += po; - } - } - - return pressure; - } - - - - - - template - V - FullyImplicitBlackoilPolymerSolver::computeGasPressure(const V& po, - const V& sw, - const V& so, - const V& sg) const - { - assert (active_[Gas]); - std::vector cp = fluid_.capPress(ADB::constant(sw), - ADB::constant(so), - ADB::constant(sg), - cells_); - return cp[Gas].value() + po; - } - - template - void - FullyImplicitBlackoilPolymerSolver::computeMassFlux(const int actph , - const V& transi, - const ADB& kr , - const ADB& phasePressure, - const SolutionState& state) - { - const int canonicalPhaseIdx = canph_[ actph ]; - - const std::vector cond = phaseCondition(); - - const ADB tr_mult = transMult(state.pressure); - const ADB mu = fluidViscosity(canonicalPhaseIdx, phasePressure, state.temperature, state.rs, state.rv,cond, cells_); - - rq_[ actph ].mob = tr_mult * kr / mu; - - const ADB rho = fluidDensity(canonicalPhaseIdx, phasePressure, state.temperature, state.rs, state.rv,cond, cells_); - - ADB& head = rq_[ actph ].head; - - // compute gravity potensial using the face average as in eclipse and MRST - const ADB rhoavg = ops_.caver * rho; - - ADB dp = ops_.ngrad * phasePressure - geo_.gravity()[2] * (rhoavg * (ops_.ngrad * geo_.z().matrix())); - - if (use_threshold_pressure_) { - applyThresholdPressures(dp); - } - - head = transi*dp; - - if (canonicalPhaseIdx == Water) { - if(has_polymer_) { - const ADB cmax = ADB::constant(cmax_, state.concentration.blockPattern()); - const ADB mc = computeMc(state); - ADB krw_eff = polymer_props_ad_.effectiveRelPerm(state.concentration, - cmax, - kr, - state.saturation[canonicalPhaseIdx]); - ADB inv_wat_eff_visc = polymer_props_ad_.effectiveInvWaterVisc(state.concentration, mu.value().data()); - rq_[actph].mob = tr_mult * krw_eff * inv_wat_eff_visc; - rq_[poly_pos_].mob = tr_mult * mc * krw_eff * inv_wat_eff_visc; - rq_[poly_pos_].b = rq_[actph].b; - rq_[poly_pos_].head = rq_[actph].head; - UpwindSelector upwind(grid_, ops_, rq_[poly_pos_].head.value()); - rq_[poly_pos_].mflux = upwind.select(rq_[poly_pos_].b * rq_[poly_pos_].mob) * rq_[poly_pos_].head; - } - } - - //head = transi*(ops_.ngrad * phasePressure) + gflux; - - UpwindSelector upwind(grid_, ops_, head.value()); - - const ADB& b = rq_[ actph ].b; - const ADB& mob = rq_[ actph ].mob; - rq_[ actph ].mflux = upwind.select(b * mob) * head; - // DUMP(rq_[ actph ].mob); - // DUMP(rq_[ actph ].mflux); - } - - - - template - void - FullyImplicitBlackoilPolymerSolver::applyThresholdPressures(ADB& dp) - { - // We support reversible threshold pressures only. - // Method: if the potential difference is lower (in absolute - // value) than the threshold for any face, then the potential - // (and derivatives) is set to zero. If it is above the - // threshold, the threshold pressure is subtracted from the - // absolute potential (the potential is moved towards zero). - - // Identify the set of faces where the potential is under the - // threshold, that shall have zero flow. Storing the bool - // Array as a V (a double Array) with 1 and 0 elements, a - // 1 where flow is allowed, a 0 where it is not. - const V high_potential = (dp.value().abs() >= threshold_pressures_by_interior_face_).template cast(); - - // Create a sparse vector that nullifies the low potential elements. - const M keep_high_potential = spdiag(high_potential); - - // Find the current sign for the threshold modification - const V sign_dp = sign(dp.value()); - const V threshold_modification = sign_dp * threshold_pressures_by_interior_face_; - - // Modify potential and nullify where appropriate. - dp = keep_high_potential * (dp - threshold_modification); - } - - - - - - template - double - FullyImplicitBlackoilPolymerSolver::residualNorm() const - { - double globalNorm = 0; - std::vector::const_iterator quantityIt = residual_.material_balance_eq.begin(); - const std::vector::const_iterator endQuantityIt = residual_.material_balance_eq.end(); - for (; quantityIt != endQuantityIt; ++quantityIt) { - const double quantityResid = (*quantityIt).value().matrix().norm(); - if (!std::isfinite(quantityResid)) { - const int trouble_phase = quantityIt - residual_.material_balance_eq.begin(); - OPM_THROW(Opm::NumericalProblem, - "Encountered a non-finite residual in material balance equation " - << trouble_phase); - } - globalNorm = std::max(globalNorm, quantityResid); - } - globalNorm = std::max(globalNorm, residual_.well_flux_eq.value().matrix().norm()); - globalNorm = std::max(globalNorm, residual_.well_eq.value().matrix().norm()); - - return globalNorm; - } - - - template - std::vector - FullyImplicitBlackoilPolymerSolver::computeResidualNorms() const - { - std::vector residualNorms; - - std::vector::const_iterator massBalanceIt = residual_.material_balance_eq.begin(); - const std::vector::const_iterator endMassBalanceIt = residual_.material_balance_eq.end(); - - for (; massBalanceIt != endMassBalanceIt; ++massBalanceIt) { - const double massBalanceResid = detail::infinityNorm( (*massBalanceIt) ); - if (!std::isfinite(massBalanceResid)) { - OPM_THROW(Opm::NumericalProblem, - "Encountered a non-finite residual"); - } - residualNorms.push_back(massBalanceResid); - } - - // the following residuals are not used in the oscillation detection now - const double wellFluxResid = detail::infinityNorm( residual_.well_flux_eq ); - if (!std::isfinite(wellFluxResid)) { - OPM_THROW(Opm::NumericalProblem, - "Encountered a non-finite residual"); - } - residualNorms.push_back(wellFluxResid); - - const double wellResid = detail::infinityNorm( residual_.well_eq ); - if (!std::isfinite(wellResid)) { - OPM_THROW(Opm::NumericalProblem, - "Encountered a non-finite residual"); - } - residualNorms.push_back(wellResid); - - return residualNorms; - } - - template - void - FullyImplicitBlackoilPolymerSolver::detectNewtonOscillations(const std::vector>& residual_history, - const int it, const double relaxRelTol, - bool& oscillate, bool& stagnate) const - { - // The detection of oscillation in two primary variable results in the report of the detection - // of oscillation for the solver. - // Only the saturations are used for oscillation detection for the black oil model. - // Stagnate is not used for any treatment here. - - if ( it < 2 ) { - oscillate = false; - stagnate = false; - return; - } - - stagnate = true; - int oscillatePhase = 0; - const std::vector& F0 = residual_history[it]; - const std::vector& F1 = residual_history[it - 1]; - const std::vector& F2 = residual_history[it - 2]; - for (int p= 0; p < fluid_.numPhases(); ++p){ - const double d1 = std::abs((F0[p] - F2[p]) / F0[p]); - const double d2 = std::abs((F0[p] - F1[p]) / F0[p]); - - oscillatePhase += (d1 < relaxRelTol) && (relaxRelTol < d2); - - // Process is 'stagnate' unless at least one phase - // exhibits significant residual change. - stagnate = (stagnate && !(std::abs((F1[p] - F2[p]) / F2[p]) > 1.0e-3)); - } - - oscillate = (oscillatePhase > 1); - } - - - template - void - FullyImplicitBlackoilPolymerSolver::stablizeNewton(V& dx, V& dxOld, const double omega, - const RelaxType relax_type) const - { - // The dxOld is updated with dx. - // If omega is equal to 1., no relaxtion will be appiled. - - const V tempDxOld = dxOld; - dxOld = dx; - - switch (relax_type) { - case DAMPEN: - if (omega == 1.) { - return; - } - dx = dx*omega; - return; - case SOR: - if (omega == 1.) { - return; - } - dx = dx*omega + (1.-omega)*tempDxOld; - return; - default: - OPM_THROW(std::runtime_error, "Can only handle DAMPEN and SOR relaxation type."); - } - - return; - } - - template - double - FullyImplicitBlackoilPolymerSolver::convergenceReduction(const Eigen::Array& B, - const Eigen::Array& tempV, - const Eigen::Array& R, - std::array& R_sum, - std::array& maxCoeff, - std::array& B_avg, - int nc) const - { - // Do the global reductions -#if HAVE_MPI - if ( linsolver_.parallelInformation().type() == typeid(ParallelISTLInformation) ) - { - const ParallelISTLInformation& info = - boost::any_cast(linsolver_.parallelInformation()); - - // Compute the global number of cells and porevolume - std::vector v(nc, 1); - auto nc_and_pv = std::tuple(0, 0.0); - auto nc_and_pv_operators = std::make_tuple(Opm::Reduction::makeGlobalSumFunctor(), - Opm::Reduction::makeGlobalSumFunctor()); - auto nc_and_pv_containers = std::make_tuple(v, geo_.poreVolume()); - info.computeReduction(nc_and_pv_containers, nc_and_pv_operators, nc_and_pv); - - for ( int idx=0; idx(0.0 ,0.0 ,0.0); - auto containers = std::make_tuple(B.col(idx), - tempV.col(idx), - R.col(idx)); - auto operators = std::make_tuple(Opm::Reduction::makeGlobalSumFunctor(), - Opm::Reduction::makeGlobalMaxFunctor(), - Opm::Reduction::makeGlobalSumFunctor()); - info.computeReduction(containers, operators, values); - B_avg[idx] = std::get<0>(values)/std::get<0>(nc_and_pv); - maxCoeff[idx] = std::get<1>(values); - R_sum[idx] = std::get<2>(values); - } - else - { - R_sum[idx] = B_avg[idx] = maxCoeff[idx] = 0.0; - } - } - // Compute pore volume - return std::get<1>(nc_and_pv); - } - else -#endif - { - for ( int idx=0; idx - bool - FullyImplicitBlackoilPolymerSolver::getConvergence(const double dt, const int iteration) - { - const double tol_mb = param_.tolerance_mb_; - const double tol_cnv = param_.tolerance_cnv_; - const double tol_wells = param_.tolerance_wells_; - - const int nc = Opm::AutoDiffGrid::numCells(grid_); - const Opm::PhaseUsage& pu = fluid_.phaseUsage(); - - const V pv = geo_.poreVolume(); - - const std::vector cond = phaseCondition(); - - std::array CNV = {{0., 0., 0., 0.}}; - std::array R_sum = {{0., 0., 0., 0.}}; - std::array B_avg = {{0., 0., 0., 0.}}; - std::array maxCoeff = {{0., 0., 0., 0.}}; - std::array mass_balance_residual = {{0., 0., 0., 0.}}; - std::size_t cols = MaxNumPhases+1; // needed to pass the correct type to Eigen - Eigen::Array B(nc, cols); - Eigen::Array R(nc, cols); - Eigen::Array tempV(nc, cols); - - for ( int idx=0; idx maxResidualAllowed() || - std::isnan(mass_balance_residual[Oil]) || mass_balance_residual[Oil] > maxResidualAllowed() || - std::isnan(mass_balance_residual[Gas]) || mass_balance_residual[Gas] > maxResidualAllowed() || - std::isnan(mass_balance_residual[MaxNumPhases]) || mass_balance_residual[MaxNumPhases] > maxResidualAllowed() || - std::isnan(CNV[Water]) || CNV[Water] > maxResidualAllowed() || - std::isnan(CNV[Oil]) || CNV[Oil] > maxResidualAllowed() || - std::isnan(CNV[Gas]) || CNV[Gas] > maxResidualAllowed() || - std::isnan(CNV[MaxNumPhases]) || CNV[MaxNumPhases] > maxResidualAllowed() || - std::isnan(residualWellFlux) || residualWellFlux > maxResidualAllowed() || - std::isnan(residualWell) || residualWell > maxResidualAllowed() ) - { - OPM_THROW(Opm::NumericalProblem,"One of the residuals is NaN or to large!"); - } - - if ( terminal_output_ ) - { - // Only rank 0 does print to std::cout - if (iteration == 0) { - std::cout << "\nIter MB(OIL) MB(WATER) MB(GAS) CNVW CNVO CNVG CNVP WELL-FLOW WELL-CNTRL\n"; - } - const std::streamsize oprec = std::cout.precision(3); - const std::ios::fmtflags oflags = std::cout.setf(std::ios::scientific); - std::cout << std::setw(4) << iteration - << std::setw(11) << mass_balance_residual[Water] - << std::setw(11) << mass_balance_residual[Oil] - << std::setw(11) << mass_balance_residual[Gas] - << std::setw(11) << mass_balance_residual[MaxNumPhases] - << std::setw(11) << CNV[Water] - << std::setw(11) << CNV[Oil] - << std::setw(11) << CNV[Gas] - << std::setw(11) << CNV[MaxNumPhases] - << std::setw(11) << residualWellFlux - << std::setw(11) << residualWell - << std::endl; - std::cout.precision(oprec); - std::cout.flags(oflags); - } - return converged; - } - - - template - ADB - FullyImplicitBlackoilPolymerSolver::fluidViscosity(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const - { - switch (phase) { - case Water: - return fluid_.muWat(p, temp, cells); - case Oil: { - return fluid_.muOil(p, temp, rs, cond, cells); - } - case Gas: - return fluid_.muGas(p, temp, rv, cond, cells); - default: - OPM_THROW(std::runtime_error, "Unknown phase index " << phase); - } - } - - - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::fluidReciprocFVF(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const - { - switch (phase) { - case Water: - return fluid_.bWat(p, temp, cells); - case Oil: { - return fluid_.bOil(p, temp, rs, cond, cells); - } - case Gas: - return fluid_.bGas(p, temp, rv, cond, cells); - default: - OPM_THROW(std::runtime_error, "Unknown phase index " << phase); - } - } - - - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::fluidDensity(const int phase, - const ADB& p , - const ADB& temp , - const ADB& rs , - const ADB& rv , - const std::vector& cond, - const std::vector& cells) const - { - const double* rhos = fluid_.surfaceDensity(); - ADB b = fluidReciprocFVF(phase, p, temp, rs, rv, cond, cells); - ADB rho = V::Constant(p.size(), 1, rhos[phase]) * b; - if (phase == Oil && active_[Gas]) { - // It is correct to index into rhos with canonical phase indices. - rho += V::Constant(p.size(), 1, rhos[Gas]) * rs * b; - } - if (phase == Gas && active_[Oil]) { - // It is correct to index into rhos with canonical phase indices. - rho += V::Constant(p.size(), 1, rhos[Oil]) * rv * b; - } - return rho; - } - - - - - - template - V - FullyImplicitBlackoilPolymerSolver::fluidRsSat(const V& p, - const V& satOil, - const std::vector& cells) const - { - return fluid_.rsSat(ADB::constant(p), ADB::constant(satOil), cells).value(); - } - - - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::fluidRsSat(const ADB& p, - const ADB& satOil, - const std::vector& cells) const - { - return fluid_.rsSat(p, satOil, cells); - } - - - template - V - FullyImplicitBlackoilPolymerSolver::fluidRvSat(const V& p, - const V& satOil, - const std::vector& cells) const - { - return fluid_.rvSat(ADB::constant(p), ADB::constant(satOil), cells).value(); - } - - - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::fluidRvSat(const ADB& p, - const ADB& satOil, - const std::vector& cells) const - { - return fluid_.rvSat(p, satOil, cells); - } - - template - ADB - FullyImplicitBlackoilPolymerSolver::computeMc(const SolutionState& state) const - { - return polymer_props_ad_.polymerWaterVelocityRatio(state.concentration); - } - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::poroMult(const ADB& p) const - { - const int n = p.size(); - if (rock_comp_props_ && rock_comp_props_->isActive()) { - V pm(n); - V dpm(n); - for (int i = 0; i < n; ++i) { - pm[i] = rock_comp_props_->poroMult(p.value()[i]); - dpm[i] = rock_comp_props_->poroMultDeriv(p.value()[i]); - } - ADB::M dpm_diag = spdiag(dpm); - const int num_blocks = p.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - fastSparseProduct(dpm_diag, p.derivative()[block], jacs[block]); - } - return ADB::function(std::move(pm), std::move(jacs)); - } else { - return ADB::constant(V::Constant(n, 1.0)); - } - } - - - - - - template - ADB - FullyImplicitBlackoilPolymerSolver::transMult(const ADB& p) const - { - const int n = p.size(); - if (rock_comp_props_ && rock_comp_props_->isActive()) { - V tm(n); - V dtm(n); - for (int i = 0; i < n; ++i) { - tm[i] = rock_comp_props_->transMult(p.value()[i]); - dtm[i] = rock_comp_props_->transMultDeriv(p.value()[i]); - } - ADB::M dtm_diag = spdiag(dtm); - const int num_blocks = p.numBlocks(); - std::vector jacs(num_blocks); - for (int block = 0; block < num_blocks; ++block) { - fastSparseProduct(dtm_diag, p.derivative()[block], jacs[block]); - } - return ADB::function(std::move(tm), std::move(jacs)); - } else { - return ADB::constant(V::Constant(n, 1.0)); - } - } - - - /* - template - void - FullyImplicitBlackoilSolver:: - classifyCondition(const SolutionState& state, - std::vector& cond ) const - { - const PhaseUsage& pu = fluid_.phaseUsage(); - - if (active_[ Gas ]) { - // Oil/Gas or Water/Oil/Gas system - const int po = pu.phase_pos[ Oil ]; - const int pg = pu.phase_pos[ Gas ]; - - const V& so = state.saturation[ po ].value(); - const V& sg = state.saturation[ pg ].value(); - - cond.resize(sg.size()); - - for (V::Index c = 0, e = sg.size(); c != e; ++c) { - if (so[c] > 0) { cond[c].setFreeOil (); } - if (sg[c] > 0) { cond[c].setFreeGas (); } - if (active_[ Water ]) { cond[c].setFreeWater(); } - } - } - else { - // Water/Oil system - assert (active_[ Water ]); - - const int po = pu.phase_pos[ Oil ]; - const V& so = state.saturation[ po ].value(); - - cond.resize(so.size()); - - for (V::Index c = 0, e = so.size(); c != e; ++c) { - cond[c].setFreeWater(); - - if (so[c] > 0) { cond[c].setFreeOil(); } - } - } - } */ - - - template - void - FullyImplicitBlackoilPolymerSolver::classifyCondition(const PolymerBlackoilState& state) - { - using namespace Opm::AutoDiffGrid; - const int nc = numCells(grid_); - const int np = state.numPhases(); - - const PhaseUsage& pu = fluid_.phaseUsage(); - const DataBlock s = Eigen::Map(& state.saturation()[0], nc, np); - if (active_[ Gas ]) { - // Oil/Gas or Water/Oil/Gas system - const V so = s.col(pu.phase_pos[ Oil ]); - const V sg = s.col(pu.phase_pos[ Gas ]); - - for (V::Index c = 0, e = sg.size(); c != e; ++c) { - if (so[c] > 0) { phaseCondition_[c].setFreeOil (); } - if (sg[c] > 0) { phaseCondition_[c].setFreeGas (); } - if (active_[ Water ]) { phaseCondition_[c].setFreeWater(); } - } - } - else { - // Water/Oil system - assert (active_[ Water ]); - - const V so = s.col(pu.phase_pos[ Oil ]); - - - for (V::Index c = 0, e = so.size(); c != e; ++c) { - phaseCondition_[c].setFreeWater(); - - if (so[c] > 0) { phaseCondition_[c].setFreeOil(); } - } - } - - - } - - template - void - FullyImplicitBlackoilPolymerSolver::updatePrimalVariableFromState(const PolymerBlackoilState& state) - { - using namespace Opm::AutoDiffGrid; - const int nc = numCells(grid_); - const int np = state.numPhases(); - - const PhaseUsage& pu = fluid_.phaseUsage(); - const DataBlock s = Eigen::Map(& state.saturation()[0], nc, np); - - // Water/Oil/Gas system - assert (active_[ Gas ]); - - // reset the primary variables if RV and RS is not set Sg is used as primary variable. - primalVariable_.resize(nc); - std::fill(primalVariable_.begin(), primalVariable_.end(), PrimalVariables::Sg); - - const V sg = s.col(pu.phase_pos[ Gas ]); - const V so = s.col(pu.phase_pos[ Oil ]); - const V sw = s.col(pu.phase_pos[ Water ]); - - const double epsilon = std::sqrt(std::numeric_limits::epsilon()); - auto watOnly = sw > (1 - epsilon); - auto hasOil = so > 0; - auto hasGas = sg > 0; - - // For oil only cells Rs is used as primal variable. For cells almost full of water - // the default primal variable (Sg) is used. - if (has_disgas_) { - for (V::Index c = 0, e = sg.size(); c != e; ++c) { - if ( !watOnly[c] && hasOil[c] && !hasGas[c] ) {primalVariable_[c] = PrimalVariables::RS; } - } - } - - // For gas only cells Rv is used as primal variable. For cells almost full of water - // the default primal variable (Sg) is used. - if (has_vapoil_) { - for (V::Index c = 0, e = so.size(); c != e; ++c) { - if ( !watOnly[c] && hasGas[c] && !hasOil[c] ) {primalVariable_[c] = PrimalVariables::RV; } - } - } - updatePhaseCondFromPrimalVariable(); - } - - - - - - /// Update the phaseCondition_ member based on the primalVariable_ member. - template - void - FullyImplicitBlackoilPolymerSolver::updatePhaseCondFromPrimalVariable() - { - if (! active_[Gas]) { - OPM_THROW(std::logic_error, "updatePhaseCondFromPrimarVariable() logic requires active gas phase."); - } - const int nc = primalVariable_.size(); - for (int c = 0; c < nc; ++c) { - phaseCondition_[c] = PhasePresence(); // No free phases. - phaseCondition_[c].setFreeWater(); // Not necessary for property calculation usage. - switch (primalVariable_[c]) { - case PrimalVariables::Sg: - phaseCondition_[c].setFreeOil(); - phaseCondition_[c].setFreeGas(); - break; - case PrimalVariables::RS: - phaseCondition_[c].setFreeOil(); - break; - case PrimalVariables::RV: - phaseCondition_[c].setFreeGas(); - break; - default: - OPM_THROW(std::logic_error, "Unknown primary variable enum value in cell " << c << ": " << primalVariable_[c]); - } - } - } - - - - -} // namespace Opm