From 910fe0318ce135953da482d2161cbeaca15623cd Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Jun 2017 11:34:07 +0200 Subject: [PATCH 001/104] adding the WellInteface will be the base class for different well models. --- CMakeLists_files.cmake | 2 + opm/autodiff/WellInterface.cpp | 246 +++++++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 164 ++++++++++++++++++++++ 3 files changed, 412 insertions(+) create mode 100644 opm/autodiff/WellInterface.cpp create mode 100644 opm/autodiff/WellInterface.hpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 222edb60e..c31f4fdd9 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -49,6 +49,7 @@ list (APPEND MAIN_SOURCE_FILES opm/autodiff/VFPInjProperties.cpp opm/autodiff/WellMultiSegment.cpp opm/autodiff/MultisegmentWells.cpp + opm/autodiff/WellInterface.cpp opm/autodiff/MissingFeatures.cpp opm/polymer/PolymerState.cpp opm/polymer/PolymerBlackoilState.cpp @@ -239,6 +240,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/autodiff/WellHelpers.hpp opm/autodiff/StandardWells.hpp opm/autodiff/StandardWells_impl.hpp + opm/autodiff/WellInterface.hpp opm/autodiff/StandardWellsDense.hpp opm/autodiff/StandardWellsSolvent.hpp opm/autodiff/StandardWellsSolvent_impl.hpp diff --git a/opm/autodiff/WellInterface.cpp b/opm/autodiff/WellInterface.cpp new file mode 100644 index 000000000..caee8ac3c --- /dev/null +++ b/opm/autodiff/WellInterface.cpp @@ -0,0 +1,246 @@ +/* + Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 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 . +*/ + +#include "config.h" + + +#include + + +namespace Opm +{ + + + WellInterface:: + WellInterface(const Well* well, const size_t time_step, const Wells* wells) + { + + // TODO: trying to use wells struct as little as possible here, be prepared to + // remove the wells struct in future + const std::string& well_name = well->name(); + + // looking for the location of the well in the wells struct + int index_well; + for (index_well = 0; index_well < wells->number_of_wells; ++index_well) { + if (well_name == std::string(wells->name[index_well])) { + break; + } + } + + // should not enter the constructor if the well does not exist in the wells struct + // here, just another assertion. + assert(index_well != wells->number_of_wells); + + name_ = well_name; + index_of_well_ = index_well; + well_type_ = wells->type[index_well]; + number_of_phases_ = wells->number_of_phases; + + // copying the comp_frac + { + comp_frac_.resize(number_of_phases_); + const int index_begin = index_well * number_of_phases_; + std::copy(wells->comp_frac + index_begin, + wells->comp_frac + index_begin + number_of_phases_, comp_frac_.begin() ); + } + + well_controls_ = wells->ctrls[index_well]; + + // perforations related + { + const int perf_index_begin = wells->well_connpos[index_well]; + const int perf_index_end = wells->well_connpos[index_well + 1]; + number_of_perforations_ = perf_index_end - perf_index_begin; + + well_cell_.resize(number_of_perforations_); + std::copy(wells->well_cells + perf_index_begin, + wells->well_cells + perf_index_end, + well_cell_.begin() ); + + well_index_.resize(number_of_perforations_); + std::copy(wells->WI + perf_index_begin, + wells->WI + perf_index_end, + well_index_.begin() ); + + // TODO: not sure about the processing of depth for perforations here + // Will revisit here later. There are different ways and the definition for different wells + // can be different, it is possible that we need to remove this from the WellInterface + perf_depth_.resize(number_of_perforations_, 0.); + const auto& completion_set = well->getCompletions(time_step); + for (int i = 0; i < number_of_perforations_; ++i) { + perf_depth_[i] = completion_set.get(i).getCenterDepth(); + } + } + } + + + + + + void + WellInterface:: + init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg) + { + phase_usage_ = phase_usage_arg; + active_ = active_arg; + vfp_properties_ = vfp_properties_arg; + gravity_ = gravity_arg; + } + + + + + + const std::string& + WellInterface:: + name() const + { + return name_; + } + + + + + + int + WellInterface:: + indexOfWell() const + { + return index_of_well_; + } + + + + + + WellType + WellInterface:: + wellType() const + { + return well_type_; + } + + + + + + int + WellInterface:: + numberOfPhases() const + { + return number_of_phases_; + } + + + + + const std::vector& + WellInterface:: + compFrac() const + { + return comp_frac_; + } + + + + + + WellControls* + WellInterface:: + wellControls() const + { + return well_controls_; + } + + + + + + int + WellInterface:: + numberOfPerforations() const + { + return number_of_perforations_; + } + + + + + + const std::vector& + WellInterface:: + wellIndex() const + { + return well_index_; + } + + + + + + const std::vector& + WellInterface:: + perfDepth() const + { + return perf_depth_; + } + + + + + + const std::vector& + WellInterface:: + wellCells() const + { + return well_cell_; + } + + + + + + const std::vector& + WellInterface:: + active() const + { + assert(active_); + + return *active_; + } + + + + + + const PhaseUsage& + WellInterface:: + phaseUsage() const + { + assert(phase_usage_); + + return *phase_usage_; + } + + +} diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp new file mode 100644 index 000000000..45200170d --- /dev/null +++ b/opm/autodiff/WellInterface.hpp @@ -0,0 +1,164 @@ +/* + Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 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_WELLINTERFACE_HEADER_INCLUDED +#define OPM_WELLINTERFACE_HEADER_INCLUDED + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Opm +{ + + + class SimulatorFullyImplicitBlackoilEbos; + + class WellInterface + { + public: + + // TODO: Simulator will probably enter template parameter through TypeTag later + using Simulator = SimulatorFullyImplicitBlackoilEbos; + using WellState = WellStateFullyImplicitBlackoilDense; + + /// Constructor + WellInterface(const Well* well, const size_t time_step, const Wells* wells); + + /// Well name. + const std::string& name() const; + + /// The index of the well in Wells struct + // It is used to locate the inforation in Wells and also WellState for now. + int indexOfWell() const; + + /// Well type, INJECTOR or PRODUCER. + WellType wellType() const; + + /// number of phases + int numberOfPhases() const; + + /// Component fractions for each phase for the well + const std::vector& compFrac() const; + + /// Well controls + // TODO: later to see whether we need to return const. + WellControls* wellControls() const; + + /// Number of the perforations + int numberOfPerforations() const; + + /// Well productivity index for each perforation. + const std::vector& wellIndex() const; + + /// Depth of perforations + const std::vector& perfDepth() const; + + /// Indices of the grid cells/blocks that perforations are completed within. + const std::vector& wellCells() const; + + // TODO: the following function should be able to be removed by refactoring the well class + // It is probably only needed for StandardWell + /// the densities of the fluid in each perforation + virtual const std::vector& perfDensities() const = 0; + virtual std::vector& perfDensities() = 0; + + /// the pressure difference between different perforations + virtual const std::vector& perfPressureDiffs() const = 0; + virtual std::vector& perfPressureDiffs() = 0; + + + virtual void assembleWellEq(Simulator& ebos_simulator, + const double dt, + WellState& well_state, + bool only_wells) = 0; + + void init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg); + + // TODO: temporary + virtual void setWellVariables(const WellState& well_state) = 0; + + const std::vector& active() const; + + const PhaseUsage& phaseUsage() const; + + protected: + // well name + std::string name_; + + // the index of well in Wells struct + int index_of_well_; + + // well type + // INJECTOR or PRODUCER + enum WellType well_type_; + + // number of phases + int number_of_phases_; + + // component fractions for each well + // typically, it should apply to injection wells + std::vector comp_frac_; + + // controls for this well + // TODO: later will check whehter to let it stay with pointer + struct WellControls* well_controls_; + + // number of the perforations for this well + int number_of_perforations_; + + // well index for each perforation + std::vector well_index_; + + // depth for each perforation + std::vector perf_depth_; + + // cell index for each well perforation + std::vector well_cell_; + + const PhaseUsage* phase_usage_; + + const std::vector* active_; + + const VFPProperties* vfp_properties_; + + double gravity_; + }; + +} + +#endif // OPM_WELLINTERFACE_HEADER_INCLUDED From 0cf6699591862cb3c5b4a2a20f127a02e7e17d77 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Jun 2017 11:41:03 +0200 Subject: [PATCH 002/104] adding StandardWell class copied from the old implementation, which is the starting point for the new refactoring --- CMakeLists_files.cmake | 2 + opm/autodiff/StandardWell.cpp | 292 ++++++++++++++++++++++++++++++++++ opm/autodiff/StandardWell.hpp | 126 +++++++++++++++ 3 files changed, 420 insertions(+) create mode 100644 opm/autodiff/StandardWell.cpp create mode 100644 opm/autodiff/StandardWell.hpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index c31f4fdd9..85bb58a4d 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -50,6 +50,7 @@ list (APPEND MAIN_SOURCE_FILES opm/autodiff/WellMultiSegment.cpp opm/autodiff/MultisegmentWells.cpp opm/autodiff/WellInterface.cpp + opm/autodiff/StandardWell.hpp opm/autodiff/MissingFeatures.cpp opm/polymer/PolymerState.cpp opm/polymer/PolymerBlackoilState.cpp @@ -241,6 +242,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/autodiff/StandardWells.hpp opm/autodiff/StandardWells_impl.hpp opm/autodiff/WellInterface.hpp + opm/autodiff/StandardWell.cpp opm/autodiff/StandardWellsDense.hpp opm/autodiff/StandardWellsSolvent.hpp opm/autodiff/StandardWellsSolvent_impl.hpp diff --git a/opm/autodiff/StandardWell.cpp b/opm/autodiff/StandardWell.cpp new file mode 100644 index 000000000..5a8568b2b --- /dev/null +++ b/opm/autodiff/StandardWell.cpp @@ -0,0 +1,292 @@ +/* + Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 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 . +*/ + +#include "config.h" + +#include + + + +namespace Opm +{ + StandardWell:: + StandardWell(const Well* well, const size_t time_step, const Wells* wells) + : WellInterface(well, time_step, wells) + , perf_densities_(number_of_perforations_) + , perf_pressure_diffs_(number_of_perforations_) + , well_variables_(blocksize) // the number of the primary variables + { + dune_B_.setBuildMode( Mat::row_wise ); + dune_C_.setBuildMode( Mat::row_wise ); + inv_dune_D_.setBuildMode( Mat::row_wise ); + } + + + + + + const std::vector& + StandardWell:: + perfDensities() const + { + return perf_densities_; + } + + + + + + std::vector& + StandardWell:: + perfDensities() + { + return perf_densities_; + } + + + + + + const std::vector& + StandardWell:: + perfPressureDiffs() const + { + return perf_pressure_diffs_; + } + + + + + + std::vector& + StandardWell:: + perfPressureDiffs() + { + return perf_pressure_diffs_; + } + + + + + + void + StandardWell:: + assembleWellEq(Simulator& ebos_simulator, + const double dt, + WellState& well_state, + bool only_wells) + { + } + + + + + + void StandardWell:: + setWellVariables(const WellState& well_state) + { + const int np = number_of_phases_; + const int nw = well_state.bhp().size(); + // TODO: it should be the number of primary variables + // TODO: this is from the old version of StandardWellsDense, it is a coincidence, 3 phases and 3 primary variables + // TODO: it needs to be careful. + // TODO: the following code has to be rewritten later for correctness purpose. + for (int phase = 0; phase < np; ++phase) { + well_variables_[phase] = 0.0; + well_variables_[phase].setValue(well_state.wellSolutions()[index_of_well_ + nw * phase]); + well_variables_[phase].setDerivative(blocksize + phase, 1.0); + } + } + + + + + + StandardWell::EvalWell + StandardWell:: + getBhp() const + { + const WellControls* wc = well_controls_; + if (well_controls_get_current_type(wc) == BHP) { + EvalWell bhp = 0.0; + const double target_rate = well_controls_get_current_target(wc); + bhp.setValue(target_rate); + return bhp; + } else if (well_controls_get_current_type(wc) == THP) { + const int control = well_controls_get_current(wc); + const double thp = well_controls_get_current_target(wc); + const double alq = well_controls_iget_alq(wc, control); + const int table_id = well_controls_iget_vfp(wc, control); + EvalWell aqua = 0.0; + EvalWell liquid = 0.0; + EvalWell vapour = 0.0; + EvalWell bhp = 0.0; + double vfp_ref_depth = 0.0; + + const Opm::PhaseUsage& pu = phaseUsage(); + + if (active()[ Water ]) { + aqua = getQs(pu.phase_pos[ Water]); + } + if (active()[ Oil ]) { + liquid = getQs(pu.phase_pos[ Oil ]); + } + if (active()[ Gas ]) { + vapour = getQs(pu.phase_pos[ Gas ]); + } + if (wellType() == INJECTOR) { + bhp = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp); + vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); + } else { + bhp = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq); + vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); + } + + // pick the density in the top layer + const double rho = perf_densities_[0]; + // TODO: not sure whether it is always correct + const double well_ref_depth = perf_depth_[0]; + // const double dp = wellhelpers::computeHydrostaticCorrection(wells(), wellIdx, vfp_ref_depth, rho, gravity_); + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + bhp -= dp; + return bhp; + } + + return well_variables_[XvarWell]; + } + + + + + + StandardWell::EvalWell + StandardWell:: + getQs(const int phase) const + { + EvalWell qs = 0.0; + + const WellControls* wc = well_controls_; + const int np = number_of_phases_; + + // the target from the well controls + const double target = well_controls_get_current_target(wc); + + // TODO: the formulation for the injectors decides it only work with single phase + // surface rate injection control. Improvement will be required. + // Injectors + if (wellType() == INJECTOR) { + // TODO: we should not rely on comp_frac anymore, it should depend on the values in distr + const double comp_frac = wells().comp_frac[np*wellIdx + phaseIdx]; + if (comp_frac == 0.0) { + return qs; + } + + if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { + return wellVariables_[XvarWell]; + } + + // rate control + // TODO: if it is reservoir volume rate, it should be wrong here + qs.setValue(target); + return qs; + } + + + // Producers + if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { + return wellVariables_[XvarWell] * wellVolumeFractionScaled(phase); + } + + } + + + + + + + StandardWell::EvalWell + StandardWell:: + wellVolumeFractionScaled(const int phase) const + { + // TODO: we should be able to set the g for the well based on the control type + // instead of using explicit code for g all the times + const WellControls* wc = well_controls_; + if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { + const double* distr = well_controls_get_current_distr(wc); + if (distr[phase] > 0.) { + return wellVolumeFraction(phase) / distr[phase]; + } else { + // TODO: not sure why return EvalWell(0.) causing problem here + // Probably due to the wrong Jacobians. + return wellVolumeFraction(phase); + } + } + std::vector g = {1,1,0.01}; + return (wellVolumeFraction(phase) / g[phase]); + } + + + + + + StandardWell::EvalWell + StandardWell:: + wellVolumeFraction(const int phase) const + { + if (phase == Water) { + return wellVariables_[WFrac]; + } + + if (phase == Gas) { + return wellVariables_[GFrac]; + } + + // Oil fraction + EvalWell well_fraction = 1.0; + if (active_[Water]) { + well_fraction -= wellVariables_[WFrac]; + } + + if (active_[Gas]) { + well_fraction -= wellVariables_[GFrac]; + } + return well_fraction; + } + + + + + + StandardWell::EvalWell + StandardWell:: + wellSurfaceVolumeFraction(const int phase) const + { + EvalWell sum_volume_fraction_scaled = 0.; + const int np = number_of_phases_; + for (int p = 0; p < np; ++p) { + sum_volume_fraction_scaled += wellVolumeFractionScaled(p); + } + + assert(sum_volume_fraction_scaled.value() != 0.); + + return wellVolumeFractionScaled(phase) / sum_volume_fraction_scaled; + } + +} diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp new file mode 100644 index 000000000..f2da9043a --- /dev/null +++ b/opm/autodiff/StandardWell.hpp @@ -0,0 +1,126 @@ +/* + Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 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_STANDARDWELL_HEADER_INCLUDED +#define OPM_STANDARDWELL_HEADER_INCLUDED + + +#include "config.h" + +#include + +#include +#include +#include + +#include +#include + +namespace Opm +{ + + class StandardWell: public WellInterface + { + + public: + using WellInterface::Simulator; + using WellInterface::WellState; + + // the positions of the primary variables for StandardWell + // there are three primary variables, the second and the third ones are F_w and F_g + // the first one can be total rate (G_t) or bhp, based on the control + enum WellVariablePositions { + XvarWell = 0, + WFrac = 1, + GFrac = 2 + }; + + // for now, using the matrix and block version in StandardWellsDense. + // TODO: for bettern generality, it should contain blocksize_field and blocksize_well. + // They are allowed to be different and it will create four types of matrix blocks and two types of + // vector blocks. + + /* const static int blocksize = 3; + typedef double Scalar; + typedef Dune::FieldVector VectorBlockType; + typedef Dune::FieldMatrix MatrixBlockType; + typedef Dune::BCRSMatrix Mat; + typedef Dune::BlockVector BVector; + typedef DenseAd::Evaluation EvalWell; */ + /* using WellInterface::EvalWell; + using WellInterface::BVector; + using WellInterface::Mat; + using WellInterface::MatrixBlockType; + using WellInterface::VectorBlockType; */ + + StandardWell(const Well* well, const size_t time_step, const Wells* wells); + + /// the densities of the fluid in each perforation + virtual const std::vector& perfDensities() const; + virtual std::vector& perfDensities(); + + /// the pressure difference between different perforations + virtual const std::vector& perfPressureDiffs() const; + virtual std::vector& perfPressureDiffs(); + + virtual void assembleWellEq(Simulator& ebos_simulator, + const double dt, + WellState& well_state, + bool only_wells); + + virtual void setWellVariables(const WellState& well_state); + + EvalWell wellVolumeFractionScaled(const int phase) const; + + EvalWell wellVolumeFraction(const int phase) const; + + EvalWell wellSurfaceVolumeFraction(const int phase) const; + + protected: + // densities of the fluid in each perforation + std::vector perf_densities_; + // pressure drop between different perforations + std::vector perf_pressure_diffs_; + + // TODO: probably, they should be moved to the WellInterface, when + // we decide the template paramters. + // two off-diagonal matrices + Mat dune_B_; + Mat dune_C_; + // diagonal matrix for the well + Mat inv_dune_D_; + + BVector res_well_; + + std::vector well_variables_; + + // TODO: this function should be moved to the base class. + // while it faces chanllenges for MSWell later, since the calculation of bhp + // based on THP is never implemented for MSWell yet. + EvalWell getBhp() const; + + // TODO: it is also possible to be moved to the base class. + EvalWell getQs(const int phase) const; + }; + +} + +#endif // OPM_STANDARDWELL_HEADER_INCLUDED From 2d025030914e8952b484eced694ea7a592869ff7 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Jun 2017 17:19:49 +0200 Subject: [PATCH 003/104] more or less getting the old implementation in and compile now. --- CMakeLists_files.cmake | 2 +- opm/autodiff/StandardWell.cpp | 30 ++++++------ opm/autodiff/StandardWell.hpp | 15 +++++- opm/autodiff/StandardWellsDense.hpp | 14 ++++++ opm/autodiff/StandardWellsDense_impl.hpp | 59 ++++++++++++++++++++++++ opm/autodiff/WellHelpers.hpp | 11 ++++- opm/autodiff/WellInterface.cpp | 2 +- opm/autodiff/WellInterface.hpp | 2 +- 8 files changed, 116 insertions(+), 19 deletions(-) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 85bb58a4d..a9afd66a0 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -50,7 +50,7 @@ list (APPEND MAIN_SOURCE_FILES opm/autodiff/WellMultiSegment.cpp opm/autodiff/MultisegmentWells.cpp opm/autodiff/WellInterface.cpp - opm/autodiff/StandardWell.hpp + opm/autodiff/StandardWell.cpp opm/autodiff/MissingFeatures.cpp opm/polymer/PolymerState.cpp opm/polymer/PolymerBlackoilState.cpp diff --git a/opm/autodiff/StandardWell.cpp b/opm/autodiff/StandardWell.cpp index 5a8568b2b..4106ca1f1 100644 --- a/opm/autodiff/StandardWell.cpp +++ b/opm/autodiff/StandardWell.cpp @@ -27,11 +27,11 @@ namespace Opm { StandardWell:: - StandardWell(const Well* well, const size_t time_step, const Wells* wells) + StandardWell(const Well* well, const int time_step, const Wells* wells) : WellInterface(well, time_step, wells) , perf_densities_(number_of_perforations_) , perf_pressure_diffs_(number_of_perforations_) - , well_variables_(blocksize) // the number of the primary variables + , well_variables_(numWellEq) // the number of the primary variables { dune_B_.setBuildMode( Mat::row_wise ); dune_C_.setBuildMode( Mat::row_wise ); @@ -111,7 +111,7 @@ namespace Opm for (int phase = 0; phase < np; ++phase) { well_variables_[phase] = 0.0; well_variables_[phase].setValue(well_state.wellSolutions()[index_of_well_ + nw * phase]); - well_variables_[phase].setDerivative(blocksize + phase, 1.0); + well_variables_[phase].setDerivative(numEq + phase, 1.0); } } @@ -182,7 +182,9 @@ namespace Opm { EvalWell qs = 0.0; - const WellControls* wc = well_controls_; + return qs; // temporary + + /* const WellControls* wc = well_controls_; const int np = number_of_phases_; // the target from the well controls @@ -199,7 +201,7 @@ namespace Opm } if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return wellVariables_[XvarWell]; + return well_variables_[XvarWell]; } // rate control @@ -211,8 +213,8 @@ namespace Opm // Producers if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { - return wellVariables_[XvarWell] * wellVolumeFractionScaled(phase); - } + return well_variables_[XvarWell] * wellVolumeFractionScaled(phase); + } */ } @@ -251,21 +253,21 @@ namespace Opm wellVolumeFraction(const int phase) const { if (phase == Water) { - return wellVariables_[WFrac]; + return well_variables_[WFrac]; } if (phase == Gas) { - return wellVariables_[GFrac]; + return well_variables_[GFrac]; } // Oil fraction EvalWell well_fraction = 1.0; - if (active_[Water]) { - well_fraction -= wellVariables_[WFrac]; + if (active()[Water]) { + well_fraction -= well_variables_[WFrac]; } - if (active_[Gas]) { - well_fraction -= wellVariables_[GFrac]; + if (active()[Gas]) { + well_fraction -= well_variables_[GFrac]; } return well_fraction; } @@ -287,6 +289,6 @@ namespace Opm assert(sum_volume_fraction_scaled.value() != 0.); return wellVolumeFractionScaled(phase) / sum_volume_fraction_scaled; - } + } } diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index f2da9043a..4ac1708d4 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -53,6 +53,19 @@ namespace Opm GFrac = 2 }; + + typedef double Scalar; + // static const int numEq = BlackoilIndices::numEq; + static const int numEq = 3; + static const int numWellEq = numEq; //number of wellEq is the same as numEq in the model + static const int solventCompIdx = 3; //TODO get this from ebos + typedef Dune::FieldVector VectorBlockType; + typedef Dune::FieldMatrix MatrixBlockType; + typedef Dune::BCRSMatrix Mat; + typedef Dune::BlockVector BVector; + typedef DenseAd::Evaluation EvalWell; + typedef DenseAd::Evaluation Eval; + // for now, using the matrix and block version in StandardWellsDense. // TODO: for bettern generality, it should contain blocksize_field and blocksize_well. // They are allowed to be different and it will create four types of matrix blocks and two types of @@ -71,7 +84,7 @@ namespace Opm using WellInterface::MatrixBlockType; using WellInterface::VectorBlockType; */ - StandardWell(const Well* well, const size_t time_step, const Wells* wells); + StandardWell(const Well* well, const int time_step, const Wells* wells); /// the densities of the fluid in each perforation virtual const std::vector& perfDensities() const; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 58674c1db..2b104c8e1 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include + namespace Opm { enum WellVariablePositions { @@ -296,6 +298,18 @@ enum WellVariablePositions { const Wells* wells_; const std::vector< const Well* > wells_ecl_; + // a vector of all the wells. + // eventually, the wells_ above should be gone. + // the name is just temporary + // later, might make share_ptr const later. + // TODO: forget why make it share_ptr instead of unique_ptr + std::vector > well_container_; + + std::vector > + createWellContainer(const std::vector& wells_ecl, + const Wells* wells_arg, + const int time_step); + // Well collection is used to enforce the group control WellCollection* well_collection_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 11df056c3..54dff01f8 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1,4 +1,5 @@ +#include namespace Opm { @@ -15,6 +16,7 @@ namespace Opm { : wells_active_(wells_arg!=nullptr) , wells_(wells_arg) , wells_ecl_(wells_ecl) + , well_container_(createWellContainer(wells_ecl, wells_arg, current_timeIdx) ) , well_collection_(well_collection) , param_(param) , terminal_output_(terminal_output) @@ -144,6 +146,63 @@ namespace Opm { + + template + std::vector > + StandardWellsDense:: + createWellContainer(const std::vector& wells_ecl, + const Wells* wells_arg, + const int time_step) + { + std::vector > wells_container; + + // There might be no wells in the process + if (localWellsActive()) { + const int nw = wells_arg->number_of_wells; + + wells_container.reserve(nw); + + // With the following way, it will have the same order with wells struct + // Hopefully, it can generate the same residual history with master branch + for (int w = 0; w < nw; ++w) { + const std::string well_name = std::string(wells_arg->name[w]); + + // finding the location of the well in wells_ecl + const int nw_wells_ecl = wells_ecl.size(); + int index_well = 0; + for (; index_well < nw_wells_ecl; ++index_well) { + if (well_name == wells_ecl[index_well]->name()) { + break; + } + } + + // It should be able to find in wells_ecl. + if (index_well == nw_wells_ecl) { + OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl "); + } + + const Well* well_ecl = wells_ecl[index_well]; + if (well_ecl->getStatus(time_step) == WellCommon::SHUT) { + continue; + } + + if (well_ecl->isMultiSegment(time_step)) { + OPM_THROW(Opm::NumericalProblem, "Not handling Multisegment Wells for now"); + } + + // Basically, we are handling all the wells as StandardWell for the moment + // TODO: to be changed when we begin introducing MultisegmentWell + wells_container.push_back(std::make_shared(well_ecl, time_step, wells_arg) ); + } + } + + return wells_container; + } + + + + + template SimulatorReport StandardWellsDense:: diff --git a/opm/autodiff/WellHelpers.hpp b/opm/autodiff/WellHelpers.hpp index c26168ef3..8bd6349f6 100644 --- a/opm/autodiff/WellHelpers.hpp +++ b/opm/autodiff/WellHelpers.hpp @@ -35,7 +35,7 @@ namespace Opm { namespace wellhelpers { - + inline double rateToCompare(const std::vector& well_phase_flow_rate, const int well, @@ -147,6 +147,15 @@ namespace Opm { return dp; } + inline + double computeHydrostaticCorrection(const double well_ref_depth, const double vfp_ref_depth, + const double rho, const double gravity) { + const double dh = vfp_ref_depth - well_ref_depth; + const double dp = rho * gravity * dh; + + return dp; + } + template inline Vector computeHydrostaticCorrection(const Wells& wells, const Vector vfp_ref_depth, diff --git a/opm/autodiff/WellInterface.cpp b/opm/autodiff/WellInterface.cpp index caee8ac3c..e005714a7 100644 --- a/opm/autodiff/WellInterface.cpp +++ b/opm/autodiff/WellInterface.cpp @@ -29,7 +29,7 @@ namespace Opm WellInterface:: - WellInterface(const Well* well, const size_t time_step, const Wells* wells) + WellInterface(const Well* well, const int time_step, const Wells* wells) { // TODO: trying to use wells struct as little as possible here, be prepared to diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 45200170d..6000b07d9 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -54,7 +54,7 @@ namespace Opm using WellState = WellStateFullyImplicitBlackoilDense; /// Constructor - WellInterface(const Well* well, const size_t time_step, const Wells* wells); + WellInterface(const Well* well, const int time_step, const Wells* wells); /// Well name. const std::string& name() const; From 182bf315f3817331f6c4300c217c48af1a55a81a Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 19 Jun 2017 12:43:08 +0200 Subject: [PATCH 004/104] implementing getQs() and getBhp() within StandardWell. It does not compile. Now it is pretty clear that anything related to Evalulation should go to each individual well model (StandardWell or MS well ) and not stay with the Wells. --- opm/autodiff/StandardWell.cpp | 122 ++++++++++++++-- opm/autodiff/StandardWellsDense_impl.hpp | 178 +---------------------- 2 files changed, 115 insertions(+), 185 deletions(-) diff --git a/opm/autodiff/StandardWell.cpp b/opm/autodiff/StandardWell.cpp index 4106ca1f1..08bb08974 100644 --- a/opm/autodiff/StandardWell.cpp +++ b/opm/autodiff/StandardWell.cpp @@ -163,7 +163,6 @@ namespace Opm const double rho = perf_densities_[0]; // TODO: not sure whether it is always correct const double well_ref_depth = perf_depth_[0]; - // const double dp = wellhelpers::computeHydrostaticCorrection(wells(), wellIdx, vfp_ref_depth, rho, gravity_); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); bhp -= dp; return bhp; @@ -182,20 +181,40 @@ namespace Opm { EvalWell qs = 0.0; - return qs; // temporary - - /* const WellControls* wc = well_controls_; + const WellControls* wc = well_controls_; const int np = number_of_phases_; + const double target_rate = well_controls_get_current_target(wc); - // the target from the well controls - const double target = well_controls_get_current_target(wc); + // TODO: we need to introduce numComponents() for StandardWell + // assert(phase < numComponents()); + const auto pu = phase_usage_; // TODO: the formulation for the injectors decides it only work with single phase // surface rate injection control. Improvement will be required. - // Injectors if (wellType() == INJECTOR) { - // TODO: we should not rely on comp_frac anymore, it should depend on the values in distr - const double comp_frac = wells().comp_frac[np*wellIdx + phaseIdx]; + // TODO: adding the handling related to solvent + /* if (has_solvent_ ) { + // TODO: investigate whether the use of the comp_frac is justified. + double comp_frac = 0.0; + if (compIdx == solventCompIdx) { // solvent + comp_frac = wells().comp_frac[np*wellIdx + pu.phase_pos[ Gas ]] * wsolvent(wellIdx); + } else if (compIdx == pu.phase_pos[ Gas ]) { + comp_frac = wells().comp_frac[np*wellIdx + compIdx] * (1.0 - wsolvent(wellIdx)); + } else { + comp_frac = wells().comp_frac[np*wellIdx + compIdx]; + } + if (comp_frac == 0.0) { + return qs; //zero + } + + if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { + return comp_frac * well_variables_[nw*XvarWell + wellIdx]; + } + + qs.setValue(comp_frac * target_rate); + return qs; + } */ + const double comp_frac = compFrac()[phase]; if (comp_frac == 0.0) { return qs; } @@ -203,19 +222,92 @@ namespace Opm if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { return well_variables_[XvarWell]; } - - // rate control - // TODO: if it is reservoir volume rate, it should be wrong here - qs.setValue(target); + qs.setValue(target_rate); return qs; } - // Producers if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { return well_variables_[XvarWell] * wellVolumeFractionScaled(phase); - } */ + } + if (well_controls_get_current_type(wc) == SURFACE_RATE) { + // checking how many phases are included in the rate control + // to decide wheter it is a single phase rate control or not + const double* distr = well_controls_get_current_distr(wc); + int num_phases_under_rate_control = 0; + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + num_phases_under_rate_control += 1; + } + } + + // there should be at least one phase involved + assert(num_phases_under_rate_control > 0); + + // when it is a single phase rate limit + if (num_phases_under_rate_control == 1) { + + // looking for the phase under control + int phase_under_control = -1; + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + phase_under_control = phase; + break; + } + } + + assert(phase_under_control >= 0); + + EvalWell wellVolumeFractionScaledPhaseUnderControl = wellVolumeFractionScaled(phase_under_control); + // TODO: handling solvent related later + /* if (has_solvent_ && phase_under_control == Gas) { + // for GRAT controlled wells solvent is included in the target + wellVolumeFractionScaledPhaseUnderControl += wellVolumeFractionScaled(solventCompIdx); + } */ + + if (phase == phase_under_control) { + /* if (has_solvent_ && phase_under_control == Gas) { + qs.setValue(target_rate * wellVolumeFractionScaled(Gas).value() / wellVolumeFractionScaledPhaseUnderControl.value() ); + return qs; + } */ + qs.setValue(target_rate); + return qs; + } + + // TODO: not sure why the single phase under control will have near zero fraction + const double eps = 1e-6; + if (wellVolumeFractionScaledPhaseUnderControl < eps) { + return qs; + } + return (target_rate * wellVolumeFractionScaled(phase) / wellVolumeFractionScaledPhaseUnderControl); + } + + // when it is a combined two phase rate limit, such like LRAT + // we neec to calculate the rate for the certain phase + if (num_phases_under_rate_control == 2) { + EvalWell combined_volume_fraction = 0.; + for (int p = 0; p < np; ++p) { + if (distr[p] == 1.0) { + combined_volume_fraction += wellVolumeFractionScaled(p); + } + } + return (target_rate * wellVolumeFractionScaled(phase) / combined_volume_fraction); + } + + // TODO: three phase surface rate control is not tested yet + if (num_phases_under_rate_control == 3) { + return target_rate * wellSurfaceVolumeFraction(phase); + } + } else if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { + // ReservoirRate + return target_rate * wellVolumeFractionScaled(phase); + } else { + OPM_THROW(std::logic_error, "Unknown control type for well " << name()); + } + + // avoid warning of condition reaches end of non-void function + return qs; } diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 54dff01f8..2fd60b8c0 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -850,6 +850,10 @@ namespace Opm { eval.setDerivative(numEq + eqIdx, 1.0); } } + + for (auto& well_interface : well_container_) { + well_interface->setWellVariables(xw); + } } @@ -2224,52 +2228,7 @@ namespace Opm { typename StandardWellsDense::EvalWell StandardWellsDense:: getBhp(const int wellIdx) const { - const WellControls* wc = wells().ctrls[wellIdx]; - if (well_controls_get_current_type(wc) == BHP) { - EvalWell bhp = 0.0; - const double target_rate = well_controls_get_current_target(wc); - bhp.setValue(target_rate); - return bhp; - } else if (well_controls_get_current_type(wc) == THP) { - const int control = well_controls_get_current(wc); - const double thp = well_controls_get_current_target(wc); - const double alq = well_controls_iget_alq(wc, control); - const int table_id = well_controls_iget_vfp(wc, control); - EvalWell aqua = 0.0; - EvalWell liquid = 0.0; - EvalWell vapour = 0.0; - EvalWell bhp = 0.0; - double vfp_ref_depth = 0.0; - - const Opm::PhaseUsage& pu = phase_usage_; - - if (active_[ Water ]) { - aqua = getQs(wellIdx, pu.phase_pos[ Water]); - } - if (active_[ Oil ]) { - liquid = getQs(wellIdx, pu.phase_pos[ Oil ]); - } - if (active_[ Gas ]) { - vapour = getQs(wellIdx, pu.phase_pos[ Gas ]); - } - if (wells().type[wellIdx] == INJECTOR) { - bhp = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp); - vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); - } else { - bhp = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq); - vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); - } - - // pick the density in the top layer - const int perf = wells().well_connpos[wellIdx]; - const double rho = well_perforation_densities_[perf]; - const double dp = wellhelpers::computeHydrostaticCorrection(wells(), wellIdx, vfp_ref_depth, rho, gravity_); - bhp -= dp; - return bhp; - } - - const int nw = wells().number_of_wells; - return wellVariables_[nw*XvarWell + wellIdx]; + return well_container_(wellIdx)->getBhp(); } @@ -2281,130 +2240,9 @@ namespace Opm { StandardWellsDense:: getQs(const int wellIdx, const int compIdx) const { - EvalWell qs = 0.0; - const WellControls* wc = wells().ctrls[wellIdx]; - const int np = wells().number_of_phases; - assert(compIdx < numComponents()); - const int nw = wells().number_of_wells; - const auto pu = phase_usage_; - const double target_rate = well_controls_get_current_target(wc); - - // TODO: the formulation for the injectors decides it only work with single phase - // surface rate injection control. Improvement will be required. - if (wells().type[wellIdx] == INJECTOR) { - if (has_solvent_ ) { - double comp_frac = 0.0; - if (has_solvent_ && compIdx == solventSaturationIdx) { // solvent - comp_frac = wells().comp_frac[np*wellIdx + pu.phase_pos[ Gas ]] * wsolvent(wellIdx); - } else if (compIdx == pu.phase_pos[ Gas ]) { - comp_frac = wells().comp_frac[np*wellIdx + compIdx] * (1.0 - wsolvent(wellIdx)); - } else { - comp_frac = wells().comp_frac[np*wellIdx + compIdx]; - } - if (comp_frac == 0.0) { - return qs; //zero - } - - if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return comp_frac * wellVariables_[nw*XvarWell + wellIdx]; - } - - qs.setValue(comp_frac * target_rate); - return qs; - } - const double comp_frac = wells().comp_frac[np*wellIdx + compIdx]; - if (comp_frac == 0.0) { - return qs; - } - - if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return wellVariables_[nw*XvarWell + wellIdx]; - } - qs.setValue(target_rate); - return qs; - } - - // Producers - if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { - return wellVariables_[nw*XvarWell + wellIdx] * wellVolumeFractionScaled(wellIdx, compIdx); - } - - if (well_controls_get_current_type(wc) == SURFACE_RATE) { - // checking how many phases are included in the rate control - // to decide wheter it is a single phase rate control or not - const double* distr = well_controls_get_current_distr(wc); - int num_phases_under_rate_control = 0; - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - num_phases_under_rate_control += 1; - } - } - - // there should be at least one phase involved - assert(num_phases_under_rate_control > 0); - - // when it is a single phase rate limit - if (num_phases_under_rate_control == 1) { - - // looking for the phase under control - int phase_under_control = -1; - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - phase_under_control = phase; - break; - } - } - - assert(phase_under_control >= 0); - - EvalWell wellVolumeFractionScaledPhaseUnderControl = wellVolumeFractionScaled(wellIdx, phase_under_control); - if (has_solvent_ && phase_under_control == Gas) { - // for GRAT controlled wells solvent is included in the target - wellVolumeFractionScaledPhaseUnderControl += wellVolumeFractionScaled(wellIdx, solventSaturationIdx); - } - - if (compIdx == phase_under_control) { - if (has_solvent_ && phase_under_control == Gas) { - qs.setValue(target_rate * wellVolumeFractionScaled(wellIdx, Gas).value() / wellVolumeFractionScaledPhaseUnderControl.value() ); - return qs; - } - qs.setValue(target_rate); - return qs; - } - - // TODO: not sure why the single phase under control will have near zero fraction - const double eps = 1e-6; - if (wellVolumeFractionScaledPhaseUnderControl < eps) { - return qs; - } - return (target_rate * wellVolumeFractionScaled(wellIdx, compIdx) / wellVolumeFractionScaledPhaseUnderControl); - } - - // when it is a combined two phase rate limit, such like LRAT - // we neec to calculate the rate for the certain phase - if (num_phases_under_rate_control == 2) { - EvalWell combined_volume_fraction = 0.; - for (int p = 0; p < np; ++p) { - if (distr[p] == 1.0) { - combined_volume_fraction += wellVolumeFractionScaled(wellIdx, p); - } - } - return (target_rate * wellVolumeFractionScaled(wellIdx, compIdx) / combined_volume_fraction); - } - - // TODO: three phase surface rate control is not tested yet - if (num_phases_under_rate_control == 3) { - return target_rate * wellSurfaceVolumeFraction(wellIdx, compIdx); - } - } else if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { - // ReservoirRate - return target_rate * wellVolumeFractionScaled(wellIdx, compIdx); - } else { - OPM_THROW(std::logic_error, "Unknown control type for well " << wells().name[wellIdx]); - } - - // avoid warning of condition reaches end of non-void function - return qs; + // TODO: incoporate the change from the new PR to the getQs + // in StandardWell + return well_container_(wellIdx)->getQs(compIdx); } From 1a4ceeec66617c89d351b6be7700855e1436e995 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 19 Jun 2017 14:49:49 +0200 Subject: [PATCH 005/104] makding the StandardWell and WellInterface templated with the template parameter TypeTag. --- CMakeLists_files.cmake | 2 - opm/autodiff/StandardWell.hpp | 27 ++++++- ...StandardWell.cpp => StandardWell_impl.hpp} | 75 ++++++++++--------- opm/autodiff/StandardWellsDense.hpp | 5 +- opm/autodiff/StandardWellsDense_impl.hpp | 9 ++- opm/autodiff/WellInterface.hpp | 3 + ...llInterface.cpp => WellInterface_impl.hpp} | 47 +++++++----- 7 files changed, 103 insertions(+), 65 deletions(-) rename opm/autodiff/{StandardWell.cpp => StandardWell_impl.hpp} (88%) rename opm/autodiff/{WellInterface.cpp => WellInterface_impl.hpp} (85%) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index a9afd66a0..cb36ffeca 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -49,8 +49,6 @@ list (APPEND MAIN_SOURCE_FILES opm/autodiff/VFPInjProperties.cpp opm/autodiff/WellMultiSegment.cpp opm/autodiff/MultisegmentWells.cpp - opm/autodiff/WellInterface.cpp - opm/autodiff/StandardWell.cpp opm/autodiff/MissingFeatures.cpp opm/polymer/PolymerState.cpp opm/polymer/PolymerBlackoilState.cpp diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 4ac1708d4..564a823e9 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -37,13 +37,15 @@ namespace Opm { - class StandardWell: public WellInterface + template + class StandardWell: public WellInterface { public: - using WellInterface::Simulator; - using WellInterface::WellState; - + // using WellInterface::Simulator; + // using WellInterface::WellState; + typedef typename WellInterface::Simulator Simulator; + typedef typename WellInterface::WellState WellState; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g // the first one can be total rate (G_t) or bhp, based on the control @@ -107,7 +109,22 @@ namespace Opm EvalWell wellSurfaceVolumeFraction(const int phase) const; + using WellInterface::phaseUsage; + using WellInterface::active; + using WellInterface::numberOfPerforations; + using WellInterface::indexOfWell; + using WellInterface::name; + using WellInterface::wellType; + using WellInterface::wellControls; + using WellInterface::compFrac; + using WellInterface::numberOfPhases; + using WellInterface::perfDepth; + protected: + + using WellInterface::vfp_properties_; + using WellInterface::gravity_; + // densities of the fluid in each perforation std::vector perf_densities_; // pressure drop between different perforations @@ -136,4 +153,6 @@ namespace Opm } +#include "StandardWell_impl.hpp" + #endif // OPM_STANDARDWELL_HEADER_INCLUDED diff --git a/opm/autodiff/StandardWell.cpp b/opm/autodiff/StandardWell_impl.hpp similarity index 88% rename from opm/autodiff/StandardWell.cpp rename to opm/autodiff/StandardWell_impl.hpp index 08bb08974..a1b109641 100644 --- a/opm/autodiff/StandardWell.cpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -18,19 +18,15 @@ along with OPM. If not, see . */ -#include "config.h" - -#include - - namespace Opm { - StandardWell:: + template + StandardWell:: StandardWell(const Well* well, const int time_step, const Wells* wells) - : WellInterface(well, time_step, wells) - , perf_densities_(number_of_perforations_) - , perf_pressure_diffs_(number_of_perforations_) + : WellInterface(well, time_step, wells) + , perf_densities_(numberOfPerforations()) + , perf_pressure_diffs_(numberOfPerforations()) , well_variables_(numWellEq) // the number of the primary variables { dune_B_.setBuildMode( Mat::row_wise ); @@ -42,8 +38,9 @@ namespace Opm + template const std::vector& - StandardWell:: + StandardWell:: perfDensities() const { return perf_densities_; @@ -53,8 +50,9 @@ namespace Opm + template std::vector& - StandardWell:: + StandardWell:: perfDensities() { return perf_densities_; @@ -64,8 +62,9 @@ namespace Opm + template const std::vector& - StandardWell:: + StandardWell:: perfPressureDiffs() const { return perf_pressure_diffs_; @@ -75,8 +74,9 @@ namespace Opm + template std::vector& - StandardWell:: + StandardWell:: perfPressureDiffs() { return perf_pressure_diffs_; @@ -86,8 +86,9 @@ namespace Opm + template void - StandardWell:: + StandardWell:: assembleWellEq(Simulator& ebos_simulator, const double dt, WellState& well_state, @@ -99,10 +100,11 @@ namespace Opm - void StandardWell:: + template + void StandardWell:: setWellVariables(const WellState& well_state) { - const int np = number_of_phases_; + const int np = numberOfPhases(); const int nw = well_state.bhp().size(); // TODO: it should be the number of primary variables // TODO: this is from the old version of StandardWellsDense, it is a coincidence, 3 phases and 3 primary variables @@ -110,7 +112,7 @@ namespace Opm // TODO: the following code has to be rewritten later for correctness purpose. for (int phase = 0; phase < np; ++phase) { well_variables_[phase] = 0.0; - well_variables_[phase].setValue(well_state.wellSolutions()[index_of_well_ + nw * phase]); + well_variables_[phase].setValue(well_state.wellSolutions()[indexOfWell() + nw * phase]); well_variables_[phase].setDerivative(numEq + phase, 1.0); } } @@ -119,11 +121,12 @@ namespace Opm - StandardWell::EvalWell - StandardWell:: + template + typename StandardWell::EvalWell + StandardWell:: getBhp() const { - const WellControls* wc = well_controls_; + const WellControls* wc = wellControls(); if (well_controls_get_current_type(wc) == BHP) { EvalWell bhp = 0.0; const double target_rate = well_controls_get_current_target(wc); @@ -162,7 +165,7 @@ namespace Opm // pick the density in the top layer const double rho = perf_densities_[0]; // TODO: not sure whether it is always correct - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = perfDepth()[0]; const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); bhp -= dp; return bhp; @@ -175,19 +178,20 @@ namespace Opm - StandardWell::EvalWell - StandardWell:: + template + typename StandardWell::EvalWell + StandardWell:: getQs(const int phase) const { EvalWell qs = 0.0; - const WellControls* wc = well_controls_; - const int np = number_of_phases_; + const WellControls* wc = wellControls(); + const int np = numberOfPhases(); const double target_rate = well_controls_get_current_target(wc); // TODO: we need to introduce numComponents() for StandardWell // assert(phase < numComponents()); - const auto pu = phase_usage_; + const auto pu = phaseUsage(); // TODO: the formulation for the injectors decides it only work with single phase // surface rate injection control. Improvement will be required. @@ -315,13 +319,14 @@ namespace Opm - StandardWell::EvalWell - StandardWell:: + template + typename StandardWell::EvalWell + StandardWell:: wellVolumeFractionScaled(const int phase) const { // TODO: we should be able to set the g for the well based on the control type // instead of using explicit code for g all the times - const WellControls* wc = well_controls_; + const WellControls* wc = wellControls(); if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { const double* distr = well_controls_get_current_distr(wc); if (distr[phase] > 0.) { @@ -340,8 +345,9 @@ namespace Opm - StandardWell::EvalWell - StandardWell:: + template + typename StandardWell::EvalWell + StandardWell:: wellVolumeFraction(const int phase) const { if (phase == Water) { @@ -368,12 +374,13 @@ namespace Opm - StandardWell::EvalWell - StandardWell:: + template + typename StandardWell::EvalWell + StandardWell:: wellSurfaceVolumeFraction(const int phase) const { EvalWell sum_volume_fraction_scaled = 0.; - const int np = number_of_phases_; + const int np = numberOfPhases(); for (int p = 0; p < np; ++p) { sum_volume_fraction_scaled += wellVolumeFractionScaled(p); } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 2b104c8e1..52f607dac 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -303,9 +303,10 @@ enum WellVariablePositions { // the name is just temporary // later, might make share_ptr const later. // TODO: forget why make it share_ptr instead of unique_ptr - std::vector > well_container_; + std::vector > > well_container_; - std::vector > + // TODO: forgot why returning a vector here + std::vector > > createWellContainer(const std::vector& wells_ecl, const Wells* wells_arg, const int time_step); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 2fd60b8c0..5b390236b 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -148,13 +148,13 @@ namespace Opm { template - std::vector > + std::vector > > StandardWellsDense:: createWellContainer(const std::vector& wells_ecl, const Wells* wells_arg, const int time_step) { - std::vector > wells_container; + std::vector > > wells_container; // There might be no wells in the process if (localWellsActive()) { @@ -192,7 +192,7 @@ namespace Opm { // Basically, we are handling all the wells as StandardWell for the moment // TODO: to be changed when we begin introducing MultisegmentWell - wells_container.push_back(std::make_shared(well_ecl, time_step, wells_arg) ); + wells_container.push_back(std::make_shared >(well_ecl, time_step, wells_arg) ); } } @@ -2228,7 +2228,8 @@ namespace Opm { typename StandardWellsDense::EvalWell StandardWellsDense:: getBhp(const int wellIdx) const { - return well_container_(wellIdx)->getBhp(); + // return well_container_(wellIdx)->getBhp(); + return 0.0; // TODO: for debugging } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 6000b07d9..5f60ca469 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -45,6 +45,7 @@ namespace Opm class SimulatorFullyImplicitBlackoilEbos; + template class WellInterface { public: @@ -161,4 +162,6 @@ namespace Opm } +#include "WellInterface_impl.hpp" + #endif // OPM_WELLINTERFACE_HEADER_INCLUDED diff --git a/opm/autodiff/WellInterface.cpp b/opm/autodiff/WellInterface_impl.hpp similarity index 85% rename from opm/autodiff/WellInterface.cpp rename to opm/autodiff/WellInterface_impl.hpp index e005714a7..d10ebbc38 100644 --- a/opm/autodiff/WellInterface.cpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -18,17 +18,13 @@ along with OPM. If not, see . */ -#include "config.h" - - -#include - namespace Opm { - WellInterface:: + template + WellInterface:: WellInterface(const Well* well, const int time_step, const Wells* wells) { @@ -94,8 +90,9 @@ namespace Opm + template void - WellInterface:: + WellInterface:: init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, @@ -111,8 +108,9 @@ namespace Opm + template const std::string& - WellInterface:: + WellInterface:: name() const { return name_; @@ -122,8 +120,9 @@ namespace Opm + template int - WellInterface:: + WellInterface:: indexOfWell() const { return index_of_well_; @@ -133,8 +132,9 @@ namespace Opm + template WellType - WellInterface:: + WellInterface:: wellType() const { return well_type_; @@ -144,8 +144,9 @@ namespace Opm + template int - WellInterface:: + WellInterface:: numberOfPhases() const { return number_of_phases_; @@ -154,8 +155,9 @@ namespace Opm + template const std::vector& - WellInterface:: + WellInterface:: compFrac() const { return comp_frac_; @@ -165,8 +167,9 @@ namespace Opm + template WellControls* - WellInterface:: + WellInterface:: wellControls() const { return well_controls_; @@ -176,8 +179,9 @@ namespace Opm + template int - WellInterface:: + WellInterface:: numberOfPerforations() const { return number_of_perforations_; @@ -187,8 +191,9 @@ namespace Opm + template const std::vector& - WellInterface:: + WellInterface:: wellIndex() const { return well_index_; @@ -198,8 +203,9 @@ namespace Opm + template const std::vector& - WellInterface:: + WellInterface:: perfDepth() const { return perf_depth_; @@ -209,8 +215,9 @@ namespace Opm + template const std::vector& - WellInterface:: + WellInterface:: wellCells() const { return well_cell_; @@ -220,8 +227,9 @@ namespace Opm + template const std::vector& - WellInterface:: + WellInterface:: active() const { assert(active_); @@ -233,8 +241,9 @@ namespace Opm + template const PhaseUsage& - WellInterface:: + WellInterface:: phaseUsage() const { assert(phase_usage_); From ff2ada66bc4b10d20a804565debbd80d2b20bc04 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 19 Jun 2017 15:05:15 +0200 Subject: [PATCH 006/104] adding the three swapping functions to WellInterface They should only be used to change the order related to the reservoir variables, so they should be same for all the well models and should be put in the WelInterface. --- opm/autodiff/WellInterface.hpp | 14 +++++++-- opm/autodiff/WellInterface_impl.hpp | 44 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 5f60ca469..750215c07 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -50,10 +50,14 @@ namespace Opm { public: - // TODO: Simulator will probably enter template parameter through TypeTag later - using Simulator = SimulatorFullyImplicitBlackoilEbos; using WellState = WellStateFullyImplicitBlackoilDense; + typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator; + typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; + typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; + + static const int solventCompIdx = 3; //TODO get this from ebos + /// Constructor WellInterface(const Well* well, const int time_step, const Wells* wells); @@ -117,6 +121,12 @@ namespace Opm const PhaseUsage& phaseUsage() const; + int flowPhaseToEbosCompIdx( const int phaseIdx ) const; + + int flowToEbosPvIdx( const int flowPv ) const; + + int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; + protected: // well name std::string name_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index d10ebbc38..21afcb96d 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -252,4 +252,48 @@ namespace Opm } + + + + template + int + WellInterface:: + flowPhaseToEbosCompIdx( const int phaseIdx ) const + { + const int phaseToComp[ 4 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx, solventCompIdx }; + return phaseToComp[ phaseIdx ]; + } + + + + + + template + int + WellInterface:: + flowToEbosPvIdx( const int flowPv ) const + { + const int flowToEbos[ 4 ] = { + BlackoilIndices::pressureSwitchIdx, + BlackoilIndices::waterSaturationIdx, + BlackoilIndices::compositionSwitchIdx, + BlackoilIndices::solventSaturationIdx + }; + return flowToEbos[ flowPv ]; + } + + + + + + template + int + WellInterface:: + flowPhaseToEbosPhaseIdx( const int phaseIdx ) const + { + assert(phaseIdx < 3); + const int flowToEbos[ 3 ] = { FluidSystem::waterPhaseIdx, FluidSystem::oilPhaseIdx, FluidSystem::gasPhaseIdx }; + return flowToEbos[ phaseIdx ]; + } + } From 266442b0bdcc5bc83ec28c4e6dad4ed8d32a5893 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 19 Jun 2017 15:24:49 +0200 Subject: [PATCH 007/104] adding extendEval() to StandardWell For this type of functions that related to Evaluation should be implemented within individual well model. --- opm/autodiff/StandardWell.hpp | 3 +++ opm/autodiff/StandardWell_impl.hpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 564a823e9..9e899b0fa 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -109,6 +109,8 @@ namespace Opm EvalWell wellSurfaceVolumeFraction(const int phase) const; + EvalWell extendEval(const Eval& in) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -119,6 +121,7 @@ namespace Opm using WellInterface::compFrac; using WellInterface::numberOfPhases; using WellInterface::perfDepth; + using WellInterface::flowToEbosPvIdx; protected: diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a1b109641..ad50c5aef 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -390,4 +390,21 @@ namespace Opm return wellVolumeFractionScaled(phase) / sum_volume_fraction_scaled; } + + + + + template + typename StandardWell::EvalWell + StandardWell:: + extendEval(const Eval& in) const + { + EvalWell out = 0.0; + out.setValue(in.value()); + for(int eqIdx = 0; eqIdx < numEq;++eqIdx) { + out.setDerivative(eqIdx, in.derivative(flowToEbosPvIdx(eqIdx))); + } + return out; + } + } From 7223163155aac14aed99c0346d087020e33d5807 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 19 Jun 2017 16:46:06 +0200 Subject: [PATCH 008/104] handing the numComponents in WellInterface --- opm/autodiff/StandardWell.hpp | 13 +++++++++-- opm/autodiff/StandardWell_impl.hpp | 36 +++++++++++++++++++++-------- opm/autodiff/WellInterface.hpp | 7 ++++++ opm/autodiff/WellInterface_impl.hpp | 33 ++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 9e899b0fa..581938d1a 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -44,8 +44,10 @@ namespace Opm public: // using WellInterface::Simulator; // using WellInterface::WellState; - typedef typename WellInterface::Simulator Simulator; - typedef typename WellInterface::WellState WellState; + using Simulator = typename WellInterface::Simulator; + using WellState = typename WellInterface::WellState; + using IntensiveQuantities = typename WellInterface::IntensiveQuantities; + // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g // the first one can be total rate (G_t) or bhp, based on the control @@ -111,6 +113,12 @@ namespace Opm EvalWell extendEval(const Eval& in) const; + // TODO: to check whether all the paramters are required + void computePerfRate(const IntensiveQuantities& intQuants, + const std::vector& mob_perfcells_dense, + const EvalWell& bhp, const double& cdp, + const bool& allow_cf, std::vector& cq_s) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -122,6 +130,7 @@ namespace Opm using WellInterface::numberOfPhases; using WellInterface::perfDepth; using WellInterface::flowToEbosPvIdx; + using WellInterface::numComponents; protected: diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index ad50c5aef..ad30603a8 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -104,16 +104,16 @@ namespace Opm void StandardWell:: setWellVariables(const WellState& well_state) { - const int np = numberOfPhases(); const int nw = well_state.bhp().size(); - // TODO: it should be the number of primary variables - // TODO: this is from the old version of StandardWellsDense, it is a coincidence, 3 phases and 3 primary variables - // TODO: it needs to be careful. - // TODO: the following code has to be rewritten later for correctness purpose. - for (int phase = 0; phase < np; ++phase) { - well_variables_[phase] = 0.0; - well_variables_[phase].setValue(well_state.wellSolutions()[indexOfWell() + nw * phase]); - well_variables_[phase].setDerivative(numEq + phase, 1.0); + const int numComp = numComponents(); + for (int eqIdx = 0; eqIdx < numComp; ++eqIdx) { + const unsigned int idx = nw * eqIdx + indexOfWell(); + assert( eqIdx < well_variables_.size() ); + assert( idx < well_state.wellSolutions().size() ); + + well_variables_[eqIdx] = 0.0; + well_variables_[eqIdx].setValue(well_state.wellSolutions()[idx]); + well_variables_[eqIdx].setDerivative(numEq + eqIdx, 1.0); } } @@ -407,4 +407,22 @@ namespace Opm return out; } + + + + + template + void + StandardWell:: + computePerfRate(const IntensiveQuantities& intQuants, + const std::vector& mob_perfcells_dense, + const EvalWell& bhp, const double& cdp, + const bool& allow_cf, std::vector& cq_s) const + { + + + + + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 750215c07..078a018c0 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -55,8 +55,10 @@ namespace Opm typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; + typedef typename GET_PROP_TYPE(TypeTag, IntensiveQuantities) IntensiveQuantities; static const int solventCompIdx = 3; //TODO get this from ebos + static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); /// Constructor WellInterface(const Well* well, const int time_step, const Wells* wells); @@ -127,7 +129,12 @@ namespace Opm int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; + int numPhases() const; + + int numComponents() const; + protected: + // TODO: some variables shared by all the wells should be made static // well name std::string name_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 21afcb96d..9ea094541 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -296,4 +296,37 @@ namespace Opm return flowToEbos[ phaseIdx ]; } + + + + + template + int + WellInterface:: + numPhases() const + { + return number_of_phases_; + } + + + + + + template + int + WellInterface:: + numComponents() const + { + if (numPhases() == 2) { + return 2; + } + + int numComp = FluidSystem::numComponents; + + if (has_solvent) { + numComp ++; + } + return numComp; + } + } From 194285333730764b8f5bac544cb7514d6e7a88df Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 20 Jun 2017 10:54:33 +0200 Subject: [PATCH 009/104] adding function computePerfRate to StandardWell --- opm/autodiff/StandardWell.hpp | 6 +- opm/autodiff/StandardWell_impl.hpp | 150 ++++++++++++++++++++++++++--- opm/autodiff/WellInterface.hpp | 2 + 3 files changed, 142 insertions(+), 16 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 581938d1a..c81c47ce3 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -47,6 +47,7 @@ namespace Opm using Simulator = typename WellInterface::Simulator; using WellState = typename WellInterface::WellState; using IntensiveQuantities = typename WellInterface::IntensiveQuantities; + using FluidSystem = typename WellInterface::FluidSystem; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -116,7 +117,7 @@ namespace Opm // TODO: to check whether all the paramters are required void computePerfRate(const IntensiveQuantities& intQuants, const std::vector& mob_perfcells_dense, - const EvalWell& bhp, const double& cdp, + const double Tw, const EvalWell& bhp, const double& cdp, const bool& allow_cf, std::vector& cq_s) const; using WellInterface::phaseUsage; @@ -130,7 +131,10 @@ namespace Opm using WellInterface::numberOfPhases; using WellInterface::perfDepth; using WellInterface::flowToEbosPvIdx; + using WellInterface::flowPhaseToEbosPhaseIdx; using WellInterface::numComponents; + using WellInterface::numPhases; + using WellInterface::has_solvent; protected: diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index ad30603a8..2a6f388aa 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -322,23 +322,29 @@ namespace Opm template typename StandardWell::EvalWell StandardWell:: - wellVolumeFractionScaled(const int phase) const + wellVolumeFractionScaled(const int compIdx) const { // TODO: we should be able to set the g for the well based on the control type // instead of using explicit code for g all the times const WellControls* wc = wellControls(); if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { + + if (has_solvent && compIdx == solventCompIdx) { + return wellVolumeFraction(compIdx); + } const double* distr = well_controls_get_current_distr(wc); - if (distr[phase] > 0.) { - return wellVolumeFraction(phase) / distr[phase]; + assert(compIdx < 3); + if (distr[compIdx] > 0.) { + return wellVolumeFraction(compIdx) / distr[compIdx]; } else { // TODO: not sure why return EvalWell(0.) causing problem here // Probably due to the wrong Jacobians. - return wellVolumeFraction(phase); + return wellVolumeFraction(compIdx); } } - std::vector g = {1,1,0.01}; - return (wellVolumeFraction(phase) / g[phase]); + + std::vector g = {1, 1, 0.01, 0.01}; + return (wellVolumeFraction(compIdx) / g[compIdx]); } @@ -348,16 +354,20 @@ namespace Opm template typename StandardWell::EvalWell StandardWell:: - wellVolumeFraction(const int phase) const + wellVolumeFraction(const int compIdx) const { - if (phase == Water) { + if (compIdx == Water) { return well_variables_[WFrac]; } - if (phase == Gas) { + if (compIdx == Gas) { return well_variables_[GFrac]; } + if (compIdx == solventCompIdx) { + return well_variables_[SFrac]; + } + // Oil fraction EvalWell well_fraction = 1.0; if (active()[Water]) { @@ -367,6 +377,9 @@ namespace Opm if (active()[Gas]) { well_fraction -= well_variables_[GFrac]; } + if (has_solvent) { + well_fraction -= well_variables_[SFrac]; + } return well_fraction; } @@ -377,17 +390,17 @@ namespace Opm template typename StandardWell::EvalWell StandardWell:: - wellSurfaceVolumeFraction(const int phase) const + wellSurfaceVolumeFraction(const int compIdx) const { EvalWell sum_volume_fraction_scaled = 0.; - const int np = numberOfPhases(); - for (int p = 0; p < np; ++p) { - sum_volume_fraction_scaled += wellVolumeFractionScaled(p); + const int numComp = numComponents(); + for (int idx = 0; idx < numComp; ++idx) { + sum_volume_fraction_scaled += wellVolumeFractionScaled(idx); } assert(sum_volume_fraction_scaled.value() != 0.); - return wellVolumeFractionScaled(phase) / sum_volume_fraction_scaled; + return wellVolumeFractionScaled(compIdx) / sum_volume_fraction_scaled; } @@ -416,13 +429,120 @@ namespace Opm StandardWell:: computePerfRate(const IntensiveQuantities& intQuants, const std::vector& mob_perfcells_dense, - const EvalWell& bhp, const double& cdp, + const double Tw, const EvalWell& bhp, const double& cdp, const bool& allow_cf, std::vector& cq_s) const { + const Opm::PhaseUsage& pu = phaseUsage(); + const int np = numPhases(); + const int numComp = numComponents(); + std::vector cmix_s(numComp,0.0); + for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { + cmix_s[componentIdx] = wellSurfaceVolumeFraction(componentIdx); + } + auto& fs = intQuants.fluidState(); + EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); + EvalWell rs = extendEval(fs.Rs()); + EvalWell rv = extendEval(fs.Rv()); + std::vector b_perfcells_dense(numComp, 0.0); + for (int phase = 0; phase < np; ++phase) { + int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); + b_perfcells_dense[phase] = extendEval(fs.invB(ebosPhaseIdx)); + } + if (has_solvent) { + b_perfcells_dense[solventCompIdx] = extendEval(intQuants.solventInverseFormationVolumeFactor()); + } + // Pressure drawdown (also used to determine direction of flow) + EvalWell well_pressure = bhp + cdp; + EvalWell drawdown = pressure - well_pressure; + // producing perforations + if ( drawdown.value() > 0 ) { + //Do nothing if crossflow is not allowed + if (!allow_cf && wellType() == INJECTOR) { + return; + } + // compute component volumetric rates at standard conditions + for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { + const EvalWell cq_p = - Tw * (mob_perfcells_dense[componentIdx] * drawdown); + cq_s[componentIdx] = b_perfcells_dense[componentIdx] * cq_p; + } + + if (active()[Oil] && active()[Gas]) { + const int oilpos = pu.phase_pos[Oil]; + const int gaspos = pu.phase_pos[Gas]; + const EvalWell cq_sOil = cq_s[oilpos]; + const EvalWell cq_sGas = cq_s[gaspos]; + cq_s[gaspos] += rs * cq_sOil; + cq_s[oilpos] += rv * cq_sGas; + } + + } else { + //Do nothing if crossflow is not allowed + if (!allow_cf && wellType() == PRODUCER) { + return; + } + + // Using total mobilities + EvalWell total_mob_dense = mob_perfcells_dense[0]; + for (int componentIdx = 1; componentIdx < numComp; ++componentIdx) { + total_mob_dense += mob_perfcells_dense[componentIdx]; + } + + // injection perforations total volume rates + const EvalWell cqt_i = - Tw * (total_mob_dense * drawdown); + + // compute volume ratio between connection at standard conditions + EvalWell volumeRatio = 0.0; + if (active()[Water]) { + const int watpos = pu.phase_pos[Water]; + volumeRatio += cmix_s[watpos] / b_perfcells_dense[watpos]; + } + + if (has_solvent) { + volumeRatio += cmix_s[solventCompIdx] / b_perfcells_dense[solventCompIdx]; + } + + if (active()[Oil] && active()[Gas]) { + const int oilpos = pu.phase_pos[Oil]; + const int gaspos = pu.phase_pos[Gas]; + + // Incorporate RS/RV factors if both oil and gas active + const EvalWell d = 1.0 - rv * rs; + + if (d.value() == 0.0) { + OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << name() << " during flux calcuation" + << " with rs " << rs << " and rv " << rv); + } + + const EvalWell tmp_oil = (cmix_s[oilpos] - rv * cmix_s[gaspos]) / d; + //std::cout << "tmp_oil " < + #include #include From 1174d2de54cb5aaff596a507d694897d8b6c3e54 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 21 Jun 2017 14:07:11 +0200 Subject: [PATCH 010/104] adding assembleWellEq to StandardWell F0_ is not initialized yet. --- opm/autodiff/StandardWell.hpp | 46 +++- opm/autodiff/StandardWell_impl.hpp | 280 +++++++++++++++++++++-- opm/autodiff/StandardWellsDense_impl.hpp | 8 + opm/autodiff/WellInterface.hpp | 24 +- opm/autodiff/WellInterface_impl.hpp | 25 +- 5 files changed, 349 insertions(+), 34 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index c81c47ce3..3dfdab135 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -48,6 +48,7 @@ namespace Opm using WellState = typename WellInterface::WellState; using IntensiveQuantities = typename WellInterface::IntensiveQuantities; using FluidSystem = typename WellInterface::FluidSystem; + using MaterialLaw = typename WellInterface::MaterialLaw; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -99,11 +100,6 @@ namespace Opm virtual const std::vector& perfPressureDiffs() const; virtual std::vector& perfPressureDiffs(); - virtual void assembleWellEq(Simulator& ebos_simulator, - const double dt, - WellState& well_state, - bool only_wells); - virtual void setWellVariables(const WellState& well_state); EvalWell wellVolumeFractionScaled(const int phase) const; @@ -120,26 +116,52 @@ namespace Opm const double Tw, const EvalWell& bhp, const double& cdp, const bool& allow_cf, std::vector& cq_s) const; + void assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells); + + bool allow_cross_flow(const Simulator& ebosSimulator) const; + + void getMobility(const Simulator& ebosSimulator, + const int perf, + std::vector& mob) const; + + // TODO: the parameters need to be optimized/adjusted + void init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg, + const int num_cells); + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; + using WellInterface::wellCells; + using WellInterface::saturationTableNumber; using WellInterface::indexOfWell; using WellInterface::name; using WellInterface::wellType; + using WellInterface::allowCrossFlow; using WellInterface::wellControls; using WellInterface::compFrac; using WellInterface::numberOfPhases; using WellInterface::perfDepth; using WellInterface::flowToEbosPvIdx; using WellInterface::flowPhaseToEbosPhaseIdx; + using WellInterface::flowPhaseToEbosCompIdx; using WellInterface::numComponents; using WellInterface::numPhases; using WellInterface::has_solvent; + using WellInterface::wellIndex; protected: + void localInvert(Mat& istlA) const; + using WellInterface::vfp_properties_; using WellInterface::gravity_; + using WellInterface::well_efficiency_factor_; // densities of the fluid in each perforation std::vector perf_densities_; @@ -149,14 +171,20 @@ namespace Opm // TODO: probably, they should be moved to the WellInterface, when // we decide the template paramters. // two off-diagonal matrices - Mat dune_B_; - Mat dune_C_; + Mat duneB_; + Mat duneC_; // diagonal matrix for the well - Mat inv_dune_D_; + Mat invDuneD_; - BVector res_well_; + // several vector used in the matrix calculation + mutable BVector Cx_; + mutable BVector invDrw_; + mutable BVector scaleAddRes_; + + BVector resWell_; std::vector well_variables_; + std::vector F0_; // TODO: this function should be moved to the base class. // while it faces chanllenges for MSWell later, since the calculation of bhp diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 2a6f388aa..7c4493a1a 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -29,9 +29,62 @@ namespace Opm , perf_pressure_diffs_(numberOfPerforations()) , well_variables_(numWellEq) // the number of the primary variables { - dune_B_.setBuildMode( Mat::row_wise ); - dune_C_.setBuildMode( Mat::row_wise ); - inv_dune_D_.setBuildMode( Mat::row_wise ); + duneB_.setBuildMode( Mat::row_wise ); + duneC_.setBuildMode( Mat::row_wise ); + invDuneD_.setBuildMode( Mat::row_wise ); + } + + + + + + template + void + StandardWell:: + init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg, + const int num_cells) + { + WellInterface(phase_usage_arg, active_arg, + vfp_properties_arg, gravity_arg, num_cells); + + // setup sparsity pattern for the matrices + // TODO: C and B are opposite compared with the notations used in the paper. + //[A B^T [x = [ res + // C D] x_well] res_well] + // set the size of the matrices + invDuneD_.setSize(1, 1, 1); + duneC_.setSize(1, num_cells, numberOfPerforations()); + duneB_.setSize(1, num_cells, numberOfPerforations()); + + for (auto row=invDuneD_.createbegin(), end = invDuneD_.createend(); row!=end; ++row) { + // Add nonzeros for diagonal + row.insert(row.index()); + } + + for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { + // Add nonzeros for diagonal + for (int perf = 0 ; perf < numberOfPerforations(); ++perf) { + const int cell_idx = wellCells()[perf]; + row.insert(cell_idx); + } + } + + // make the B^T matrix + for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { + for (int perf = 0; perf < numberOfPerforations(); ++perf) { + const int cell_idx = wellCells()[perf]; + row.insert(cell_idx); + } + } + + resWell_.resize(1); + + // resize temporary class variables + Cx_.resize( duneC_.N() ); + invDrw_.resize( invDuneD_.N() ); } @@ -86,20 +139,6 @@ namespace Opm - template - void - StandardWell:: - assembleWellEq(Simulator& ebos_simulator, - const double dt, - WellState& well_state, - bool only_wells) - { - } - - - - - template void StandardWell:: setWellVariables(const WellState& well_state) @@ -545,4 +584,211 @@ namespace Opm } } + + + + + template + void + StandardWell:: + assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells) + { + // TODO: accessing well_state information is the only place to use nw at the moment + const int nw = well_state.bhp().size(); + const int numComp = numComponents(); + const int np = numPhases(); + + // clear all entries + duneB_ = 0.0; + duneC_ = 0.0; + invDuneD_ = 0.0; + resWell_ = 0.0; + + auto& ebosJac = ebosSimulator.model().linearizer().matrix(); + auto& ebosResid = ebosSimulator.model().linearizer().residual(); + + // TODO: it probably can be static member for StandardWell + const double volume = 0.002831684659200; // 0.1 cu ft; + + const bool allow_cf = allow_cross_flow(ebosSimulator); + + const EvalWell& bhp = getBhp(); + + for (int perf = 0; perf < numberOfPerforations(); ++perf) { + + const int cell_idx = wellCells()[perf]; + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/ 0)); + std::vector cq_s(numComp,0.0); + std::vector mob(numComp, 0.0); + getMobility(ebosSimulator, perf, mob); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + + for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { + // the cq_s entering mass balance equations need to consider the efficiency factors. + const EvalWell cq_s_effective = cq_s[componentIdx] * well_efficiency_factor_; + + if (!only_wells) { + // subtract sum of component fluxes in the reservoir equation. + // need to consider the efficiency factor + ebosResid[cell_idx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.value(); + } + + // subtract sum of phase fluxes in the well equations. + resWell_[0][componentIdx] -= cq_s[componentIdx].value(); + + // assemble the jacobians + for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { + if (!only_wells) { + // also need to consider the efficiency factor when manipulating the jacobians. + ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + duneB_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix + duneC_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + } + invDuneD_[0][0][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); + } + + // add trivial equation for 2p cases (Only support water + oil) + if (numComp == 2) { + assert(!active()[ Gas ]); + invDuneD_[0][0][Gas][Gas] = 1.0; + } + + // Store the perforation phase flux for later usage. + if (componentIdx == solventCompIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) + well_state.perfRateSolvent()[perf] = cq_s[componentIdx].value(); + } else { + well_state.perfPhaseRates()[perf*np + componentIdx] = cq_s[componentIdx].value(); + } + } + + // Store the perforation pressure for later usage. + well_state.perfPress()[perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; + } + + // add vol * dF/dt + Q to the well equations; + for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { + // TODO: the F0_ here is not initialized yet here, which should happen in the first iteration, so it should happen in the assemble function + EvalWell resWell_loc = (wellSurfaceVolumeFraction(componentIdx) - F0_[componentIdx]) * volume / dt; + resWell_loc += getQs(componentIdx); + for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { + invDuneD_[0][0][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); + } + resWell_[0][componentIdx] += resWell_loc.value(); + } + + // do the local inversion of D. + localInvert( invDuneD_ ); + } + + + + + + template + bool + StandardWell:: + allow_cross_flow(const Simulator& ebosSimulator) const + { + if (allowCrossFlow()) { + return true; + } + + // TODO: investigate the justification of the following situation + + // check for special case where all perforations have cross flow + // then the wells must allow for cross flow + for (int perf = 0; perf < numberOfPerforations(); ++perf) { + const int cell_idx = wellCells()[perf]; + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); + const auto& fs = intQuants.fluidState(); + EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); + EvalWell bhp = getBhp(); + + // Pressure drawdown (also used to determine direction of flow) + EvalWell well_pressure = bhp + perfPressureDiffs()[perf]; + EvalWell drawdown = pressure - well_pressure; + + if (drawdown.value() < 0 && wellType() == INJECTOR) { + return false; + } + + if (drawdown.value() > 0 && wellType() == PRODUCER) { + return false; + } + } + return true; + } + + + + + + template + void + StandardWell:: + getMobility(const Simulator& ebosSimulator, + const int perf, + std::vector& mob) const + { + const int np = numberOfPhases(); + const int cell_idx = wellCells()[perf]; + assert (int(mob.size()) == numComponents()); + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); + const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); + + // either use mobility of the perforation cell or calcualte its own + // based on passing the saturation table index + const int satid = saturationTableNumber()[perf] - 1; + const int satid_elem = materialLawManager->satnumRegionIdx(cell_idx); + if( satid == satid_elem ) { // the same saturation number is used. i.e. just use the mobilty from the cell + + for (int phase = 0; phase < np; ++phase) { + int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); + mob[phase] = extendEval(intQuants.mobility(ebosPhaseIdx)); + } + if (has_solvent) { + mob[solventCompIdx] = extendEval(intQuants.solventMobility()); + } + } else { + + const auto& paramsCell = materialLawManager->connectionMaterialLawParams(satid, cell_idx); + Eval relativePerms[3] = { 0.0, 0.0, 0.0 }; + MaterialLaw::relativePermeabilities(relativePerms, paramsCell, intQuants.fluidState()); + + // reset the satnumvalue back to original + materialLawManager->connectionMaterialLawParams(satid_elem, cell_idx); + + // compute the mobility + for (int phase = 0; phase < np; ++phase) { + int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); + mob[phase] = extendEval(relativePerms[ebosPhaseIdx] / intQuants.fluidState().viscosity(ebosPhaseIdx)); + } + + // this may not work if viscosity and relperms has been modified? + if (has_solvent) { + OPM_THROW(std::runtime_error, "individual mobility for wells does not work in combination with solvent"); + } + } + } + + + + + + template + void + StandardWell:: + localInvert(Mat& istlA) const + { + for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) { + for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) { + //std::cout << (*col) << std::endl; + (*col).invert(); + } + } + } + } diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 5b390236b..6d1c32eaf 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -128,6 +128,14 @@ namespace Opm { } } + // do the initialization work + + // do the initialization for all the wells + // TODO: to see whether we can postpone of the intialization of the well containers to + // optimize the usage of the following several member variables + for (auto& well : well_container_) { + well->init(&phase_usage_, &active_, vfp_properties_, gravity_, nc); + } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index db12d0cfc..a44a9fce0 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -58,6 +58,7 @@ namespace Opm typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; typedef typename GET_PROP_TYPE(TypeTag, IntensiveQuantities) IntensiveQuantities; + typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; static const int solventCompIdx = 3; //TODO get this from ebos static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); @@ -107,16 +108,12 @@ namespace Opm virtual const std::vector& perfPressureDiffs() const = 0; virtual std::vector& perfPressureDiffs() = 0; - - virtual void assembleWellEq(Simulator& ebos_simulator, - const double dt, - WellState& well_state, - bool only_wells) = 0; - + // TODO: the parameters need to be optimized/adjusted void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, - const double gravity_arg); + const double gravity_arg, + const int num_cells); // TODO: temporary virtual void setWellVariables(const WellState& well_state) = 0; @@ -135,6 +132,11 @@ namespace Opm int numComponents() const; + bool allowCrossFlow() const; + + // TODO: for this kind of function, maybe can make a function with parameter perf + const std::vector& saturationTableNumber() const; + protected: // TODO: some variables shared by all the wells should be made static // well name @@ -147,6 +149,9 @@ namespace Opm // INJECTOR or PRODUCER enum WellType well_type_; + // whether the well allows crossflow + bool allow_cf_; + // number of phases int number_of_phases_; @@ -167,9 +172,14 @@ namespace Opm // depth for each perforation std::vector perf_depth_; + double well_efficiency_factor_; + // cell index for each well perforation std::vector well_cell_; + // saturation table nubmer for each well perforation + std::vector saturation_table_number_; + const PhaseUsage* phase_usage_; const std::vector* active_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 9ea094541..4948ba944 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -47,6 +47,7 @@ namespace Opm name_ = well_name; index_of_well_ = index_well; well_type_ = wells->type[index_well]; + allow_cf_ = wells->allow_cf[index_well]; number_of_phases_ = wells->number_of_phases; // copying the comp_frac @@ -75,6 +76,12 @@ namespace Opm wells->WI + perf_index_end, well_index_.begin() ); + saturation_table_number_.resize(number_of_perforations_); + std::copy(wells->sat_table_id + perf_index_begin, + wells->sat_table_id + perf_index_end, + saturation_table_number_.begin() ); + + // TODO: not sure about the processing of depth for perforations here // Will revisit here later. There are different ways and the definition for different wells // can be different, it is possible that we need to remove this from the WellInterface @@ -84,6 +91,9 @@ namespace Opm perf_depth_[i] = completion_set.get(i).getCenterDepth(); } } + + well_efficiency_factor_ = 1.0; + // TODO: need to calculate based on wellCollections, or it should happen in the Well Model side. } @@ -96,7 +106,8 @@ namespace Opm init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, - const double gravity_arg) + const double gravity_arg, + const int /* num_cells */) { phase_usage_ = phase_usage_arg; active_ = active_arg; @@ -179,6 +190,18 @@ namespace Opm + template + const std::vector& + WellInterface:: + saturationTableNumber() const + { + return saturation_table_number_; + } + + + + + template int WellInterface:: From 1d9d70ee02ebb4cb3de2769c5e265728693bc3df Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 22 Jun 2017 15:37:54 +0200 Subject: [PATCH 011/104] adding the updateWellState to StandardWell this function is very long and need to debug very carefully and it should be split for better readability for sure. --- opm/autodiff/StandardWell.hpp | 8 +- opm/autodiff/StandardWell_impl.hpp | 313 +++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 1 + 3 files changed, 320 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 3dfdab135..723c09c89 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -23,8 +23,6 @@ #define OPM_STANDARDWELL_HEADER_INCLUDED -#include "config.h" - #include #include @@ -134,6 +132,11 @@ namespace Opm const double gravity_arg, const int num_cells); + // Update the well_state based on solution + void updateWellState(const BVector& dwells, + const BlackoilModelParameters& param, + WellState& well_state) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -157,6 +160,7 @@ namespace Opm protected: + // TODO: maybe this function can go to some helper file. void localInvert(Mat& istlA) const; using WellInterface::vfp_properties_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 7c4493a1a..90029e122 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -778,6 +778,319 @@ namespace Opm + template + void + StandardWell:: + updateWellState(const BVector& dwells, + const BlackoilModelParameters& param, + WellState& well_state) const + { + const int np = numberOfPhases(); + const int nw = well_state.bhp().size(); + const double dFLimit = param.dbhp_max_rel_; + const double dBHPLimit = param.dwell_fraction_max_; + + std::vector xvar_well_old(numWellEq); + // TODO: better way to handle this? + for (int i = 0; i < numWellEq; ++i) { + xvar_well_old[i] = well_state.wellSolutions()[i * nw + indexOfWell()]; + } + + // update the second and third well variable (The flux fractions) + std::vector F(np,0.0); + if (active()[ Water ]) { + const int sign2 = dwells[0][WFrac] > 0 ? 1: -1; + const double dx2_limited = sign2 * std::min(std::abs(dwells[0][WFrac]),dFLimit); + well_state.wellSolutions()[WFrac * nw + indexOfWell()] = xvar_well_old[WFrac] - dx2_limited; + } + + if (active()[ Gas ]) { + const int sign3 = dwells[0][GFrac] > 0 ? 1: -1; + const double dx3_limited = sign3 * std::min(std::abs(dwells[0][GFrac]),dFLimit); + well_state.wellSolutions()[GFrac*nw + indexOfWell()] = xvar_well_old[GFrac] - dx3_limited; + } + + if (has_solvent) { + const int sign4 = dwells[0][SFrac] > 0 ? 1: -1; + const double dx4_limited = sign4 * std::min(std::abs(dwells[0][SFrac]),dFLimit); + well_state.wellSolutions()[SFrac*nw + indexOfWell()] = xvar_well_old[SFrac] - dx4_limited; + } + + assert(active()[ Oil ]); + F[Oil] = 1.0; + + if (active()[ Water ]) { + F[Water] = well_state.wellSolutions()[WFrac*nw + indexOfWell()]; + F[Oil] -= F[Water]; + } + + if (active()[ Gas ]) { + F[Gas] = well_state.wellSolutions()[GFrac*nw + indexOfWell()]; + F[Oil] -= F[Gas]; + } + + double F_solvent = 0.0; + if (has_solvent) { + F_solvent = well_state.wellSolutions()[SFrac*nw + indexOfWell()]; + F[Oil] -= F_solvent; + } + + if (active()[ Water ]) { + if (F[Water] < 0.0) { + if (active()[ Gas ]) { + F[Gas] /= (1.0 - F[Water]); + } + if (has_solvent) { + F_solvent /= (1.0 - F[Water]); + } + F[Oil] /= (1.0 - F[Water]); + F[Water] = 0.0; + } + } + + if (active()[ Gas ]) { + if (F[Gas] < 0.0) { + if (active()[ Water ]) { + F[Water] /= (1.0 - F[Gas]); + } + if (has_solvent) { + F_solvent /= (1.0 - F[Gas]); + } + F[Oil] /= (1.0 - F[Gas]); + F[Gas] = 0.0; + } + } + + if (F[Oil] < 0.0) { + if (active()[ Water ]) { + F[Water] /= (1.0 - F[Oil]); + } + if (active()[ Gas ]) { + F[Gas] /= (1.0 - F[Oil]); + } + if (has_solvent) { + F_solvent /= (1.0 - F[Oil]); + } + F[Oil] = 0.0; + } + + if (active()[ Water ]) { + well_state.wellSolutions()[WFrac*nw + indexOfWell()] = F[Water]; + } + if (active()[ Gas ]) { + well_state.wellSolutions()[GFrac*nw + indexOfWell()] = F[Gas]; + } + if(has_solvent) { + well_state.wellSolutions()[SFrac*nw + indexOfWell()] = F_solvent; + } + + // F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent. + // More testing is needed to make sure this is correct for well groups and THP. + if (has_solvent){ + F[Gas] += F_solvent; + } + + // The interpretation of the first well variable depends on the well control + const WellControls* wc = wellControls(); + + // TODO: we should only maintain one current control either from the well_state or from well_controls struct. + // Either one can be more favored depending on the final strategy for the initilzation of the well control + const int current = well_state.currentControls()[indexOfWell()]; + const double target_rate = well_controls_iget_target(wc, current); + + std::vector g = {1,1,0.01}; + if (well_controls_iget_type(wc, current) == RESERVOIR_RATE) { + const double* distr = well_controls_iget_distr(wc, current); + for (int p = 0; p < np; ++p) { + if (distr[p] > 0.) { // For injection wells, there only one non-zero distr value + F[p] /= distr[p]; + } else { + F[p] = 0.; + } + } + } else { + for (int p = 0; p < np; ++p) { + F[p] /= g[p]; + } + } + + switch (well_controls_iget_type(wc, current)) { + case THP: // The BHP and THP both uses the total rate as first well variable. + case BHP: + { + well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; + + switch (wellType()) { + case INJECTOR: + for (int p = 0; p < np; ++p) { + const double comp_frac = compFrac()[p]; + well_state.wellRates()[indexOfWell() * np + p] = comp_frac * well_state.wellSolutions()[nw*XvarWell + indexOfWell()]; + } + break; + case PRODUCER: + for (int p = 0; p < np; ++p) { + well_state.wellRates()[indexOfWell() * np + p] = well_state.wellSolutions()[nw*XvarWell + indexOfWell()] * F[p]; + } + break; + } + + if (well_controls_iget_type(wc, current) == THP) { + + // Calculate bhp from thp control and well rates + double aqua = 0.0; + double liquid = 0.0; + double vapour = 0.0; + + const Opm::PhaseUsage& pu = phaseUsage(); + + if (active()[ Water ]) { + aqua = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Water ] ]; + } + if (active()[ Oil ]) { + liquid = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Oil ] ]; + } + if (active()[ Gas ]) { + vapour = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Gas ] ]; + } + + const int vfp = well_controls_iget_vfp(wc, current); + const double& thp = well_controls_iget_target(wc, current); + const double& alq = well_controls_iget_alq(wc, current); + + // Set *BHP* target by calculating bhp from THP + const WellType& well_type = wellType(); + // pick the density in the top layer + const double rho = perf_densities_[0]; + const double well_ref_depth = perfDepth()[0]; + + if (well_type == INJECTOR) { + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + well_state.bhp()[indexOfWell()] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; + } + else if (well_type == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + well_state.bhp()[indexOfWell()] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; + } + else { + OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); + } + } + } + break; + case SURFACE_RATE: // Both rate controls use bhp as first well variable + case RESERVOIR_RATE: + { + const int sign1 = dwells[0][XvarWell] > 0 ? 1: -1; + const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[nw*XvarWell + indexOfWell()])*dBHPLimit); + well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = std::max(xvar_well_old[nw*XvarWell + indexOfWell()] - dx1_limited,1e5); + well_state.bhp()[indexOfWell()] = well_state.wellSolutions()[nw*XvarWell + indexOfWell()]; + + if (well_controls_iget_type(wc, current) == SURFACE_RATE) { + if (wellType() == PRODUCER) { + + const double* distr = well_controls_iget_distr(wc, current); + + double F_target = 0.0; + for (int p = 0; p < np; ++p) { + F_target += distr[p] * F[p]; + } + for (int p = 0; p < np; ++p) { + well_state.wellRates()[np * indexOfWell() + p] = F[p] * target_rate / F_target; + } + } else { + + for (int p = 0; p < np; ++p) { + well_state.wellRates()[indexOfWell() * np + p] = compFrac()[p] * target_rate; + } + } + } else { // RESERVOIR_RATE + for (int p = 0; p < np; ++p) { + well_state.wellRates()[np * indexOfWell() + p] = F[p] * target_rate; + } + } + } + break; + } // end of switch (well_controls_iget_type(wc, current)) + + // for the wells having a THP constaint, we should update their thp value + // If it is under THP control, it will be set to be the target value. Otherwise, + // the thp value will be calculated based on the bhp value, assuming the bhp value is correctly calculated. + const int nwc = well_controls_get_num(wc); + // Looping over all controls until we find a THP constraint + int ctrl_index = 0; + for ( ; ctrl_index < nwc; ++ctrl_index) { + if (well_controls_iget_type(wc, ctrl_index) == THP) { + // the current control + const int current = well_state.currentControls()[indexOfWell()]; + // If under THP control at the moment + if (current == ctrl_index) { + const double thp_target = well_controls_iget_target(wc, current); + well_state.thp()[indexOfWell()] = thp_target; + } else { // otherwise we calculate the thp from the bhp value + double aqua = 0.0; + double liquid = 0.0; + double vapour = 0.0; + + const Opm::PhaseUsage& pu = phaseUsage(); + + if (active()[ Water ]) { + aqua = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Water ] ]; + } + if (active()[ Oil ]) { + liquid = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Oil ] ]; + } + if (active()[ Gas ]) { + vapour = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Gas ] ]; + } + + const double alq = well_controls_iget_alq(wc, ctrl_index); + const int table_id = well_controls_iget_vfp(wc, ctrl_index); + + const WellType& well_type = wellType(); + const double rho = perf_densities_[0]; + const double well_ref_depth = perfDepth()[0]; + if (well_type == INJECTOR) { + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + const double bhp = well_state.bhp()[indexOfWell()]; + + well_state.thp()[indexOfWell()] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp + dp); + } else if (well_type == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + const double bhp = well_state.bhp()[indexOfWell()]; + + well_state.thp()[indexOfWell()] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp + dp, alq); + } else { + OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); + } + } + + // the THP control is found, we leave the loop now + break; + } + } // end of for loop for seaching THP constraints + + // no THP constraint found + if (ctrl_index == nwc) { // not finding a THP contstraints + well_state.thp()[indexOfWell()] = 0.0; + } + } + + + + + template void StandardWell:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index a44a9fce0..5fc0bbffa 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include From f293e8133690cc78d0797354f474f0cd8c8aac04 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 10:58:46 +0200 Subject: [PATCH 012/104] adding function updateWellStateWithTarget to StandardWell without dealing with wsolvent function. It can be just a member variable since we are handling well one by one individually. --- opm/autodiff/StandardWell.hpp | 8 + opm/autodiff/StandardWell_impl.hpp | 222 ++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 2 + opm/autodiff/WellInterface_impl.hpp | 15 ++ 4 files changed, 247 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 723c09c89..b3885aaff 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -137,6 +137,10 @@ namespace Opm const BlackoilModelParameters& param, WellState& well_state) const; + // TODO: later will check wheter we need current + void updateWellStateWithTarget(const int current, + WellState& xw) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -157,15 +161,19 @@ namespace Opm using WellInterface::numPhases; using WellInterface::has_solvent; using WellInterface::wellIndex; + using WellInterface::wsolvent; protected: // TODO: maybe this function can go to some helper file. void localInvert(Mat& istlA) const; + // TODO: decide wether to use member function to refer to private member later using WellInterface::vfp_properties_; using WellInterface::gravity_; using WellInterface::well_efficiency_factor_; + using WellInterface::active_; + using WellInterface::phase_usage_; // densities of the fluid in each perforation std::vector perf_densities_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 90029e122..0f189800e 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1104,4 +1104,226 @@ namespace Opm } } + + + + + template + void + StandardWell:: + updateWellStateWithTarget(const int current, + WellState& xw) const + { + // number of phases + const int np = numberOfPhases(); + const int well_index = indexOfWell(); + const WellControls* wc = wellControls(); + // Updating well state and primary variables. + // Target values are used as initial conditions for BHP, THP, 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()[well_index] = target; + // TODO: similar to the way below to handle THP + // we should not something related to thp here when there is thp constraint + break; + + case THP: { + xw.thp()[well_index] = target; + + double aqua = 0.0; + double liquid = 0.0; + double vapour = 0.0; + + const Opm::PhaseUsage& pu = phase_usage_; + + if (active_[ Water ]) { + aqua = xw.wellRates()[well_index*np + pu.phase_pos[ Water ] ]; + } + if (active_[ Oil ]) { + liquid = xw.wellRates()[well_index*np + pu.phase_pos[ Oil ] ]; + } + if (active_[ Gas ]) { + vapour = xw.wellRates()[well_index*np + pu.phase_pos[ Gas ] ]; + } + + const int table_id = well_controls_iget_vfp(wc, current); + const double& thp = well_controls_iget_target(wc, current); + const double& alq = well_controls_iget_alq(wc, current); + + //Set *BHP* target by calculating bhp from THP + + // pick the density in the top layer + const double rho = perf_densities_[0]; + const double well_ref_depth = perfDepth()[0]; + + // TODO: make the following a function and we call it so many times. + if (wellType() == INJECTOR) { + + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + xw.bhp()[well_index] = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp) - dp; + } + else if (wellType() == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + + xw.bhp()[well_index] = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq) - dp; + } + else { + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + } + break; + } + + case RESERVOIR_RATE: // intentional fall-through + case SURFACE_RATE: + // checking the number of the phases under control + int numPhasesWithTargetsUnderThisControl = 0; + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + numPhasesWithTargetsUnderThisControl += 1; + } + } + + assert(numPhasesWithTargetsUnderThisControl > 0); + + if (wellType() == INJECTOR) { + // assign target value as initial guess for injectors + // only handles single phase control at the moment + assert(numPhasesWithTargetsUnderThisControl == 1); + + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.) { + xw.wellRates()[np*well_index + phase] = target / distr[phase]; + } else { + xw.wellRates()[np * well_index + phase] = 0.; + } + } + } else if (wellType() == PRODUCER) { + // update the rates of phases under control based on the target, + // and also update rates of phases not under control to keep the rate ratio, + // assuming the mobility ratio does not change for the production wells + double original_rates_under_phase_control = 0.0; + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + original_rates_under_phase_control += xw.wellRates()[np * well_index + phase] * distr[phase]; + } + } + + if (original_rates_under_phase_control != 0.0 ) { + double scaling_factor = target / original_rates_under_phase_control; + + for (int phase = 0; phase < np; ++phase) { + xw.wellRates()[np * well_index + phase] *= scaling_factor; + } + } else { // scaling factor is not well defied when original_rates_under_phase_control is zero + // separating targets equally between phases under control + const double target_rate_divided = target / numPhasesWithTargetsUnderThisControl; + for (int phase = 0; phase < np; ++phase) { + if (distr[phase] > 0.0) { + xw.wellRates()[np * well_index + phase] = target_rate_divided / distr[phase]; + } else { + // this only happens for SURFACE_RATE control + xw.wellRates()[np * well_index + phase] = target_rate_divided; + } + } + } + } else { + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + } + + break; + } // end of switch + + + std::vector g = {1.0, 1.0, 0.01}; + if (well_controls_iget_type(wc, current) == RESERVOIR_RATE) { + for (int phase = 0; phase < np; ++phase) { + g[phase] = distr[phase]; + } + } + + // the number of wells + const int nw = xw.bhp().size(); + + switch (well_controls_iget_type(wc, current)) { + case THP: + case BHP: { + xw.wellSolutions()[nw*XvarWell + well_index] = 0.0; + if (wellType() == INJECTOR) { + for (int p = 0; p < np; ++p) { + xw.wellSolutions()[nw*XvarWell + well_index] += xw.wellRates()[np*well_index + p] * compFrac()[p]; + } + } else { + for (int p = 0; p < np; ++p) { + xw.wellSolutions()[nw*XvarWell + well_index] += g[p] * xw.wellRates()[np*well_index + p]; + } + } + break; + } + case RESERVOIR_RATE: // Intentional fall-through + case SURFACE_RATE: + xw.wellSolutions()[nw*XvarWell + well_index] = xw.bhp()[well_index]; + break; + } // end of switch + + double tot_well_rate = 0.0; + for (int p = 0; p < np; ++p) { + tot_well_rate += g[p] * xw.wellRates()[np*well_index + p]; + } + if(std::abs(tot_well_rate) > 0) { + if (active_[ Water ]) { + xw.wellSolutions()[WFrac*nw + well_index] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; + } + if (active_[ Gas ]) { + xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (1.0 - wsolvent()) * xw.wellRates()[np*well_index + Gas] / tot_well_rate ; + } + if (has_solvent) { + xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * wsolvent() * xw.wellRates()[np*well_index + Gas] / tot_well_rate ; + } + } else { // tot_well_rate == 0 + if (wellType() == INJECTOR) { + // only single phase injection handled + if (active_[Water]) { + if (distr[Water] > 0.0) { + xw.wellSolutions()[WFrac * nw + well_index] = 1.0; + } else { + xw.wellSolutions()[WFrac * nw + well_index] = 0.0; + } + } + + if (active_[Gas]) { + if (distr[Gas] > 0.0) { + xw.wellSolutions()[GFrac * nw + well_index] = 1.0 - wsolvent(); + if (has_solvent) { + xw.wellSolutions()[SFrac * nw + well_index] = wsolvent(); + } + } else { + xw.wellSolutions()[GFrac * nw + well_index] = 0.0; + } + } + + // TODO: it is possible to leave injector as a oil well, + // when F_w and F_g both equals to zero, not sure under what kind of circumstance + // this will happen. + } else if (wellType() == PRODUCER) { // producers + // TODO: the following are not addressed for the solvent case yet + if (active_[Water]) { + xw.wellSolutions()[WFrac * nw + well_index] = 1.0 / np; + } + if (active_[Gas]) { + xw.wellSolutions()[GFrac * nw + well_index] = 1.0 / np; + } + } else { + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + } + } + } + + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 5fc0bbffa..214d836da 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -138,6 +138,8 @@ namespace Opm // TODO: for this kind of function, maybe can make a function with parameter perf const std::vector& saturationTableNumber() const; + const double wsolvent() const; + protected: // TODO: some variables shared by all the wells should be made static // well name diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 4948ba944..52188fa0a 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -352,4 +352,19 @@ namespace Opm return numComp; } + + + + template + const double + WellInterface:: + wsolvent() const + { + // TODO: not handling it for the moment + // TODO: it needs information from the well_ecl + // TODO: will decide on well_ecl role later. + // It can be just one member variable and no need to deal with well_ecl at all + return 0.0; + } + } From 9c65684bf0b64cdda3bd40cda0e1d7be80843abd Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 11:42:59 +0200 Subject: [PATCH 013/104] adding updateWellControl to StandardWell TODO: check whehter it should go to WellInterface. --- opm/autodiff/StandardWell.hpp | 4 ++ opm/autodiff/StandardWell_impl.hpp | 90 ++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b3885aaff..1e628b488 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -141,6 +141,10 @@ namespace Opm void updateWellStateWithTarget(const int current, WellState& xw) const; + // TODO: this should go to the WellInterface, while updateWellStateWithTarget + // will need touch different types of well_state, we will see. + void updateWellControl(WellState& xw) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 0f189800e..53d08ba9e 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1326,4 +1326,94 @@ namespace Opm } + + + + template + void + StandardWell:: + updateWellControl(WellState& xw) const + { + const int np = numberOfPhases(); + const int nw = xw.bhp().size(); + const int w = indexOfWell(); + + const int old_control_index = xw.currentControls()[w]; + + // Find, for each well, if any constraints are broken. If so, + // switch control to first broken constraint. + WellControls* wc = wellControls(); + + // 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); + // the current control index + int current = xw.currentControls()[w]; + 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 (wellhelpers::constraintBroken( + xw.bhp(), xw.thp(), xw.wellRates(), + w, np, wellType(), 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. + xw.currentControls()[w] = ctrl_index; + current = xw.currentControls()[w]; + well_controls_set_current( wc, current); + } + + // update whether well is under group control + /* if (wellCollection()->groupControlActive()) { + // get well node in the well collection + WellNode& well_node = well_collection_->findWellNode(std::string(wells().name[w])); + + // update whehter the well is under group control or individual control + if (well_node.groupControlIndex() >= 0 && current == well_node.groupControlIndex()) { + // under group control + well_node.setIndividualControl(false); + } else { + // individual control + well_node.setIndividualControl(true); + } + } */ + + // the new well control indices after all the related updates, + const int updated_control_index = xw.currentControls()[w]; + + // checking whether control changed + wellhelpers::WellSwitchingLogger logger; + if (updated_control_index != old_control_index) { + logger.wellSwitched(name(), + well_controls_iget_type(wc, old_control_index), + well_controls_iget_type(wc, updated_control_index)); + } + + if (updated_control_index != old_control_index) { // || well_collection_->groupControlActive()) { + updateWellStateWithTarget(updated_control_index, xw); + } + + // upate the well targets following group controls + // it will not change the control mode, only update the targets + /* if (wellCollection()->groupControlActive()) { + applyVREPGroupControl(xw); + wellCollection()->updateWellTargets(xw.wellRates()); + for (int w = 0; w < nw; ++w) { + const WellControls* wc = wells().ctrls[w]; + updateWellStateWithTarget(wc, updated_control_index[w], w, xw); + } + } */ + } + + } From d3378ab40326542861f02b16f50691529478b582 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 12:24:50 +0200 Subject: [PATCH 014/104] adding function computeAverageFormationFactor to StandardWellsDense which is seperated from getWellConvergence for better flexiblity later. --- opm/autodiff/StandardWellsDense.hpp | 2 + opm/autodiff/StandardWellsDense_impl.hpp | 53 +++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 52f607dac..1e56b0adc 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -436,6 +436,8 @@ enum WellVariablePositions { void computeRepRadiusPerfLength(const Grid& grid); + void computeAverageFormationFactor(Simulator& ebosSimulator, + std::vector& B_avg) const; }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 6d1c32eaf..fe911ab29 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1141,10 +1141,12 @@ namespace Opm { const int np = numPhases(); const int numComp = numComponents(); + std::vector< Scalar > B_avg( numComp, Scalar() ); + computeAverageFormationFactor(ebosSimulator, B_avg); + const double tol_wells = param_.tolerance_wells_; const double maxResidualAllowed = param_.max_residual_allowed_; - std::vector< Scalar > B_avg( numComp, Scalar() ); std::vector< Scalar > maxNormWell(numComp, Scalar() ); auto& grid = ebosSimulator.gridManager().grid(); @@ -1191,6 +1193,7 @@ namespace Opm { } } + const auto& grid = ebosSimulator.gridManager().grid(); grid.comm().max(maxNormWell.data(), maxNormWell.size()); Vector well_flux_residual(numComp); @@ -3192,4 +3195,52 @@ namespace Opm { + + template + void + StandardWellsDense:: + computeAverageFormationFactor(Simulator& ebosSimulator, + std::vector& B_avg) const + { + const int np = numPhases(); + const int numComp = numComponents(); + + const auto& grid = ebosSimulator.gridManager().grid(); + const auto& gridView = grid.leafGridView(); + ElementContext elemCtx(ebosSimulator); + const auto& elemEndIt = gridView.template end(); + + for (auto elemIt = gridView.template begin(); + elemIt != elemEndIt; ++elemIt) + { + elemCtx.updatePrimaryStencil(*elemIt); + elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0); + + const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0); + const auto& fs = intQuants.fluidState(); + + for ( int phaseIdx = 0; phaseIdx < np; ++phaseIdx ) + { + auto& B = B_avg[ phaseIdx ]; + const int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phaseIdx); + + B += 1 / fs.invB(ebosPhaseIdx).value(); + } + if (has_solvent_) { + auto& B = B_avg[ solventCompIdx ]; + B += 1 / intQuants.solventInverseFormationVolumeFactor().value(); + } + } + + // compute global average + grid.comm().sum(B_avg.data(), B_avg.size()); + for(auto& bval: B_avg) + { + bval/=global_nc_; + } + } + + + + } // namespace Opm From 20c36d19c19dacd5fcfd604ff596a6a98c3aeedc Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 14:55:56 +0200 Subject: [PATCH 015/104] adding computePropertiesForWellConnectionPressures to StandardWell --- opm/autodiff/StandardWell.hpp | 10 +++ opm/autodiff/StandardWell_impl.hpp | 108 ++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 5 ++ opm/autodiff/WellInterface_impl.hpp | 1 + 4 files changed, 124 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 1e628b488..2c0f44258 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -178,6 +178,7 @@ namespace Opm using WellInterface::well_efficiency_factor_; using WellInterface::active_; using WellInterface::phase_usage_; + using WellInterface::first_perf_; // densities of the fluid in each perforation std::vector perf_densities_; @@ -209,6 +210,15 @@ namespace Opm // TODO: it is also possible to be moved to the base class. EvalWell getQs(const int phase) const; + + // calculate the properties for the well connections + // to calulate the pressure difference between well connections. + void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw, + std::vector& b_perf, + std::vector& rsmax_perf, + std::vector& rvmax_perf, + std::vector& surf_dens_perf) const; }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 53d08ba9e..6a2bfb8a5 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1416,4 +1416,112 @@ namespace Opm } + + + + template + void + StandardWell:: + computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw, + std::vector& b_perf, + std::vector& rsmax_perf, + std::vector& rvmax_perf, + std::vector& surf_dens_perf) const + { + const int nperf = numberOfPerforations(); + // TODO: can make this a member? + const int nw = xw.bhp().size(); + const int numComp = numComponents(); + const PhaseUsage& pu = phase_usage_; + b_perf.resize(nperf*numComp); + surf_dens_perf.resize(nperf*numComp); + const int w = indexOfWell(); + + //rs and rv are only used if both oil and gas is present + if (pu.phase_used[BlackoilPhases::Vapour] && pu.phase_pos[BlackoilPhases::Liquid]) { + rsmax_perf.resize(nperf); + rvmax_perf.resize(nperf); + } + + // Compute the average pressure in each well block + for (int perf = 0; perf < nperf; ++perf) { + const int cell_idx = wellCells()[perf]; + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); + const auto& fs = intQuants.fluidState(); + + // TODO: this is another place to show why WellState need to be a vector of WellState. + // TODO: to check why should be perf - 1 + const double p_above = perf == 0 ? xw.bhp()[w] : xw.perfPress()[first_perf_ + perf - 1]; + const double p_avg = (xw.perfPress()[perf] + p_above)/2; + const double temperature = fs.temperature(FluidSystem::oilPhaseIdx).value(); + + if (pu.phase_used[BlackoilPhases::Aqua]) { + b_perf[ pu.phase_pos[BlackoilPhases::Aqua] + perf * numComp] = + FluidSystem::waterPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); + } + + if (pu.phase_used[BlackoilPhases::Vapour]) { + const int gaspos = pu.phase_pos[BlackoilPhases::Vapour] + perf * numComp; + const int gaspos_well = pu.phase_pos[BlackoilPhases::Vapour] + w * pu.num_phases; + + if (pu.phase_used[BlackoilPhases::Liquid]) { + const int oilpos_well = pu.phase_pos[BlackoilPhases::Liquid] + w * pu.num_phases; + const double oilrate = std::abs(xw.wellRates()[oilpos_well]); //in order to handle negative rates in producers + rvmax_perf[perf] = FluidSystem::gasPvt().saturatedOilVaporizationFactor(fs.pvtRegionIndex(), temperature, p_avg); + if (oilrate > 0) { + const double gasrate = std::abs(xw.wellRates()[gaspos_well]) - xw.solventWellRate(w); + double rv = 0.0; + if (gasrate > 0) { + rv = oilrate / gasrate; + } + rv = std::min(rv, rvmax_perf[perf]); + + b_perf[gaspos] = FluidSystem::gasPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg, rv); + } + else { + b_perf[gaspos] = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); + } + + } else { + b_perf[gaspos] = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); + } + } + + if (pu.phase_used[BlackoilPhases::Liquid]) { + const int oilpos = pu.phase_pos[BlackoilPhases::Liquid] + perf * numComp; + const int oilpos_well = pu.phase_pos[BlackoilPhases::Liquid] + w * pu.num_phases; + if (pu.phase_used[BlackoilPhases::Vapour]) { + rsmax_perf[perf] = FluidSystem::oilPvt().saturatedGasDissolutionFactor(fs.pvtRegionIndex(), temperature, p_avg); + const int gaspos_well = pu.phase_pos[BlackoilPhases::Vapour] + w * pu.num_phases; + const double gasrate = std::abs(xw.wellRates()[gaspos_well]) - xw.solventWellRate(w); + if (gasrate > 0) { + const double oilrate = std::abs(xw.wellRates()[oilpos_well]); + double rs = 0.0; + if (oilrate > 0) { + rs = gasrate / oilrate; + } + rs = std::min(rs, rsmax_perf[perf]); + b_perf[oilpos] = FluidSystem::oilPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg, rs); + } else { + b_perf[oilpos] = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); + } + } else { + b_perf[oilpos] = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); + } + } + + // Surface density. + for (int p = 0; p < pu.num_phases; ++p) { + surf_dens_perf[numComp*perf + p] = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( p ), fs.pvtRegionIndex()); + } + + // We use cell values for solvent injector + if (has_solvent) { + b_perf[numComp*perf + solventCompIdx] = intQuants.solventInverseFormationVolumeFactor().value(); + surf_dens_perf[numComp*perf + solventCompIdx] = intQuants.solventRefDensity(); + } + } + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 214d836da..0891b984f 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -169,6 +169,11 @@ namespace Opm // number of the perforations for this well int number_of_perforations_; + // record the index of the first perforation + // TODO: it might not be needed if we refactor WellState to be a vector + // of states of individual well. + int first_perf_; + // well index for each perforation std::vector well_index_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 52188fa0a..a3a5e0b52 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -65,6 +65,7 @@ namespace Opm const int perf_index_begin = wells->well_connpos[index_well]; const int perf_index_end = wells->well_connpos[index_well + 1]; number_of_perforations_ = perf_index_end - perf_index_begin; + first_perf_ = perf_index_begin; well_cell_.resize(number_of_perforations_); std::copy(wells->well_cells + perf_index_begin, From 9514e44d139c655ae2e40ece52fa4ec53654360e Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 15:54:05 +0200 Subject: [PATCH 016/104] adding function computeConnectionDensities to StandardWell which is copied from WellDensitySegmented, while avoid using of the Wells struct. TODO: find a better place to put these long functions. --- opm/autodiff/StandardWell.hpp | 8 +++ opm/autodiff/StandardWell_impl.hpp | 98 ++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 2c0f44258..61be4d166 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -219,6 +219,14 @@ namespace Opm std::vector& rsmax_perf, std::vector& rvmax_perf, std::vector& surf_dens_perf) const; + + // TODO: not total sure whether it is a good idea to put here + // the major reason to put here is to avoid the usage of Wells struct + void computeConnectionDensities(const std::vector& perfComponentRates, + const std::vector& b_perf, + const std::vector& rsmax_perf, + const std::vector& rvmax_perf, + const std::vector& surf_dens_perf); }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 6a2bfb8a5..2f6159b03 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1524,4 +1524,102 @@ namespace Opm } } + + + + + template + void + StandardWell:: + computeConnectionDensities(const std::vector& perfComponentRates, + const std::vector& b_perf, + const std::vector& rsmax_perf, + const std::vector& rvmax_perf, + const std::vector& surf_dens_perf) + { + // Verify that we have consistent input. + const int np = numberOfPhases(); + const int nperf = numberOfPerforations(); + const int num_comp = numComponents(); + const PhaseUsage* phase_usage = phase_usage_; + + // 1. Compute the flow (in surface volume units for each + // component) exiting up the wellbore from each perforation, + // taking into account flow from lower in the well, and + // in/out-flow at each perforation. + std::vector q_out_perf(nperf*num_comp); + + // TODO: investigate whether we should use the following techniques to calcuate the composition of flows in the wellbore + // Iterate over well perforations from bottom to top. + for (int perf = nperf - 1; perf >= 0; --perf) { + for (int component = 0; component < num_comp; ++component) { + if (perf == nperf - 1) { + // This is the bottom perforation. No flow from below. + q_out_perf[perf*num_comp+ component] = 0.0; + } else { + // Set equal to flow from below. + q_out_perf[perf*num_comp + component] = q_out_perf[(perf+1)*num_comp + component]; + } + // Subtract outflow through perforation. + q_out_perf[perf*num_comp + component] -= perfComponentRates[perf*num_comp + component]; + } + } + + // 2. Compute the component mix at each perforation as the + // absolute values of the surface rates divided by their sum. + // Then compute volume ratios (formation factors) for each perforation. + // Finally compute densities for the segments associated with each perforation. + const int gaspos = phase_usage->phase_pos[BlackoilPhases::Vapour]; + const int oilpos = phase_usage->phase_pos[BlackoilPhases::Liquid]; + std::vector mix(num_comp,0.0); + std::vector x(num_comp); + std::vector surf_dens(num_comp); + std::vector dens(nperf); + + for (int perf = 0; perf < nperf; ++perf) { + // Find component mix. + const double tot_surf_rate = std::accumulate(q_out_perf.begin() + num_comp*perf, + q_out_perf.begin() + num_comp*(perf+1), 0.0); + if (tot_surf_rate != 0.0) { + for (int component = 0; component < num_comp; ++component) { + mix[component] = std::fabs(q_out_perf[perf*num_comp + component]/tot_surf_rate); + } + } else { + // No flow => use well specified fractions for mix. + for (int phase = 0; phase < np; ++phase) { + mix[phase] = compFrac()[phase]; + } + // intialize 0.0 for comIdx >= np; + } + // Compute volume ratio. + x = mix; + double rs = 0.0; + double rv = 0.0; + if (!rsmax_perf.empty() && mix[oilpos] > 0.0) { + rs = std::min(mix[gaspos]/mix[oilpos], rsmax_perf[perf]); + } + if (!rvmax_perf.empty() && mix[gaspos] > 0.0) { + rv = std::min(mix[oilpos]/mix[gaspos], rvmax_perf[perf]); + } + if (rs != 0.0) { + // Subtract gas in oil from gas mixture + x[gaspos] = (mix[gaspos] - mix[oilpos]*rs)/(1.0 - rs*rv); + } + if (rv != 0.0) { + // Subtract oil in gas from oil mixture + x[oilpos] = (mix[oilpos] - mix[gaspos]*rv)/(1.0 - rs*rv);; + } + double volrat = 0.0; + for (int component = 0; component < num_comp; ++component) { + volrat += x[component] / b_perf[perf*num_comp+ component]; + } + for (int component = 0; component < num_comp; ++component) { + surf_dens[component] = surf_dens_perf[perf*num_comp+ component]; + } + + // Compute segment density. + perf_densities_[perf] = std::inner_product(surf_dens.begin(), surf_dens.end(), mix.begin(), 0.0) / volrat; + } + } + } From 0f997a537a0d5c5b738f3a1fa23d76266a9c2d8a Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 16:32:43 +0200 Subject: [PATCH 017/104] adding computeConnectionPressureDelta to StandardWell it is also copied from WellDensitySegmented while only handle one Standard Well. --- opm/autodiff/StandardWell.hpp | 4 +++ opm/autodiff/StandardWell_impl.hpp | 40 +++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 3 +++ opm/autodiff/WellInterface_impl.hpp | 2 ++ 4 files changed, 49 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 61be4d166..de9581aea 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -179,6 +179,8 @@ namespace Opm using WellInterface::active_; using WellInterface::phase_usage_; using WellInterface::first_perf_; + using WellInterface::ref_depth_; + using WellInterface::perf_depth_; // densities of the fluid in each perforation std::vector perf_densities_; @@ -227,6 +229,8 @@ namespace Opm const std::vector& rsmax_perf, const std::vector& rvmax_perf, const std::vector& surf_dens_perf); + + void computeConnectionPressureDelta(); }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 2f6159b03..dd320fbe5 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1622,4 +1622,44 @@ namespace Opm } } + + + + template + void + StandardWell:: + computeConnectionPressureDelta() + { + // Algorithm: + + // We'll assume the perforations are given in order from top to + // bottom for each well. By top and bottom we do not necessarily + // mean in a geometric sense (depth), but in a topological sense: + // the 'top' perforation is nearest to the surface topologically. + // Our goal is to compute a pressure delta for each perforation. + + // 1. Compute pressure differences between perforations. + // dp_perf will contain the pressure difference between a + // perforation and the one above it, except for the first + // perforation for each well, for which it will be the + // difference to the reference (bhp) depth. + + const int nperf = numberOfPerforations(); + perf_pressure_diffs_.resize(nperf, 0.0); + + for (int perf = 0; perf < nperf; ++perf) { + const double z_above = perf == 0 ? ref_depth_ : perf_depth_[perf - 1]; + const double dz = perf_depth_[perf] - z_above; + perf_pressure_diffs_[perf] = dz * perf_densities_[perf] * gravity_; + } + + // 2. Compute pressure differences to the reference point (bhp) by + // accumulating the already computed adjacent pressure + // differences, storing the result in dp_perf. + // This accumulation must be done per well. + const auto beg = perf_pressure_diffs_.begin(); + const auto end = perf_pressure_diffs_.end(); + std::partial_sum(beg, end, beg); + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 0891b984f..3078304ce 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -180,6 +180,9 @@ namespace Opm // depth for each perforation std::vector perf_depth_; + // reference depth for the BHP + int ref_depth_; + double well_efficiency_factor_; // cell index for each well perforation diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index a3a5e0b52..a403bdda8 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -60,6 +60,8 @@ namespace Opm well_controls_ = wells->ctrls[index_well]; + ref_depth_ = wells->depth_ref[index_well]; + // perforations related { const int perf_index_begin = wells->well_connpos[index_well]; From e01e8c352aac351eb7f3091fc26fa4a739f41f1a Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 23 Jun 2017 16:45:14 +0200 Subject: [PATCH 018/104] adding function computeWellConnectionDensitesPressures to StandardWell --- opm/autodiff/StandardWell.hpp | 6 +++++ opm/autodiff/StandardWell_impl.hpp | 36 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index de9581aea..0f04569c6 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -231,6 +231,12 @@ namespace Opm const std::vector& surf_dens_perf); void computeConnectionPressureDelta(); + + void computeWellConnectionDensitesPressures(const WellState& xw, + const std::vector& b_perf, + const std::vector& rsmax_perf, + const std::vector& rvmax_perf, + const std::vector& surf_dens_perf); }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index dd320fbe5..5aefbf6c3 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1662,4 +1662,40 @@ namespace Opm std::partial_sum(beg, end, beg); } + + + + + + + template + void + StandardWell:: + computeWellConnectionDensitesPressures(const WellState& xw, + const std::vector& b_perf, + const std::vector& rsmax_perf, + const std::vector& rvmax_perf, + const std::vector& surf_dens_perf) + { + // Compute densities + const int nperf = numberOfPerforations(); + const int numComponent = numComponents(); + const int np = numberOfPhases(); + std::vector perfRates(b_perf.size(),0.0); + + for (int perf = 0; perf < nperf; ++perf) { + for (int phase = 0; phase < np; ++phase) { + perfRates[perf*numComponent + phase] = xw.perfPhaseRates()[(first_perf_ + perf) * np + phase]; + } + if(has_solvent) { + perfRates[perf*numComponent + solventCompIdx] = xw.perfRateSolvent()[perf + first_perf_]; + } + } + + computeConnectionDensities(perfRates, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf); + + computeConnectionPressureDelta(); + + } + } From d99fe876ddbee74caa98764ea82678a0d16a26b1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 26 Jun 2017 14:35:43 +0200 Subject: [PATCH 019/104] adding getWellConvergence for StandardWell --- opm/autodiff/StandardWell.hpp | 5 ++ opm/autodiff/StandardWell_impl.hpp | 76 ++++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 7 ++- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 0f04569c6..0ccb471b6 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -47,6 +47,7 @@ namespace Opm using IntensiveQuantities = typename WellInterface::IntensiveQuantities; using FluidSystem = typename WellInterface::FluidSystem; using MaterialLaw = typename WellInterface::MaterialLaw; + using ModelParameters = typename WellInterface::ModelParameters; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -145,6 +146,10 @@ namespace Opm // will need touch different types of well_state, we will see. void updateWellControl(WellState& xw) const; + virtual bool getWellConvergence(Simulator& ebosSimulator, + std::vector& B_avg, + const ModelParameters& param) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 5aefbf6c3..86c20fca4 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1666,6 +1666,82 @@ namespace Opm + template + bool + StandardWell:: + getWellConvergence(Simulator& ebosSimulator, + std::vector& B_avg, + const ModelParameters& param) const + { + typedef double Scalar; + typedef std::vector< Scalar > Vector; + + const int np = numberOfPhases(); + const int numComp = numComponents(); + + assert(int(B_avg.size()) == numComp); + + const double tol_wells = param.tolerance_wells_; + const double maxResidualAllowed = param.max_residual_allowed_; + + std::vector res(numComp); + for (int comp = 0; comp < numWellEq; ++comp) { + res[comp] = resWell_[0][comp]; + } + + Vector well_flux_residual(numComp); + bool converged_Well = true; + + // Finish computation + for ( int compIdx = 0; compIdx < numComp; ++compIdx ) + { + well_flux_residual[compIdx] = B_avg[compIdx] * res[compIdx]; + converged_Well = converged_Well && (well_flux_residual[compIdx] < tol_wells); + } + + // if one of the residuals is NaN, throw exception, so that the solver can be restarted + // TODO: not understand why phase here while component in other places. + for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { + const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); + + if (std::isnan(well_flux_residual[phaseIdx])) { + OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name()); + } + + if (well_flux_residual[phaseIdx] > maxResidualAllowed) { + OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name()); + } + } + + /* if ( terminal_output_ ) + { + // Only rank 0 does print to std::cout + if (iteration == 0) { + std::string msg; + msg = "Iter"; + for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { + const std::string& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); + msg += " W-FLUX(" + phaseName + ")"; + } + OpmLog::note(msg); + } + + std::ostringstream ss; + const std::streamsize oprec = ss.precision(3); + const std::ios::fmtflags oflags = ss.setf(std::ios::scientific); + ss << std::setw(4) << iteration; + for (int compIdx = 0; compIdx < numComp; ++compIdx) { + ss << std::setw(11) << well_flux_residual[compIdx]; + } + ss.precision(oprec); + ss.flags(oflags); + OpmLog::note(ss.str()); + } */ + return converged_Well; + } + + + template diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 3078304ce..b108e9d37 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -46,8 +46,6 @@ namespace Opm { - class SimulatorFullyImplicitBlackoilEbos; - template class WellInterface { @@ -55,6 +53,7 @@ namespace Opm using WellState = WellStateFullyImplicitBlackoilDense; + typedef BlackoilModelParameters ModelParameters; typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; @@ -140,6 +139,10 @@ namespace Opm const double wsolvent() const; + virtual bool getWellConvergence(Simulator& ebosSimulator, + std::vector& B_avg, + const ModelParameters& param) const = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From 9dace225de026ceac8a5ecfbe707e156ef2150ca Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 26 Jun 2017 16:01:45 +0200 Subject: [PATCH 020/104] adding wellEqIteration() to StandardWell the involvement of the group control in updateWellControls() makes the solution of well equations for each well individually more troublesome. As a result, we will still makes the solveWellEq in all the wells level. --- opm/autodiff/StandardWell.hpp | 7 +++-- opm/autodiff/StandardWell_impl.hpp | 47 +++++++++++++++++++++++------- opm/autodiff/WellInterface.hpp | 6 +++- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 0ccb471b6..6e9252a6f 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -147,7 +147,7 @@ namespace Opm void updateWellControl(WellState& xw) const; virtual bool getWellConvergence(Simulator& ebosSimulator, - std::vector& B_avg, + const std::vector& B_avg, const ModelParameters& param) const; using WellInterface::phaseUsage; @@ -181,7 +181,6 @@ namespace Opm using WellInterface::vfp_properties_; using WellInterface::gravity_; using WellInterface::well_efficiency_factor_; - using WellInterface::active_; using WellInterface::phase_usage_; using WellInterface::first_perf_; using WellInterface::ref_depth_; @@ -242,6 +241,10 @@ namespace Opm const std::vector& rsmax_perf, const std::vector& rvmax_perf, const std::vector& surf_dens_perf); + + virtual void wellEqIteration(Simulator& ebosSimulator, + const ModelParameters& param, + WellState& well_state); }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 86c20fca4..c7b492c2f 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1136,15 +1136,15 @@ namespace Opm double liquid = 0.0; double vapour = 0.0; - const Opm::PhaseUsage& pu = phase_usage_; + const Opm::PhaseUsage& pu = *phase_usage_; - if (active_[ Water ]) { + if (active()[ Water ]) { aqua = xw.wellRates()[well_index*np + pu.phase_pos[ Water ] ]; } - if (active_[ Oil ]) { + if (active()[ Oil ]) { liquid = xw.wellRates()[well_index*np + pu.phase_pos[ Oil ] ]; } - if (active_[ Gas ]) { + if (active()[ Gas ]) { vapour = xw.wellRates()[well_index*np + pu.phase_pos[ Gas ] ]; } @@ -1277,10 +1277,10 @@ namespace Opm tot_well_rate += g[p] * xw.wellRates()[np*well_index + p]; } if(std::abs(tot_well_rate) > 0) { - if (active_[ Water ]) { + if (active()[ Water ]) { xw.wellSolutions()[WFrac*nw + well_index] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; } - if (active_[ Gas ]) { + if (active()[ Gas ]) { xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (1.0 - wsolvent()) * xw.wellRates()[np*well_index + Gas] / tot_well_rate ; } if (has_solvent) { @@ -1289,7 +1289,7 @@ namespace Opm } else { // tot_well_rate == 0 if (wellType() == INJECTOR) { // only single phase injection handled - if (active_[Water]) { + if (active()[Water]) { if (distr[Water] > 0.0) { xw.wellSolutions()[WFrac * nw + well_index] = 1.0; } else { @@ -1297,7 +1297,7 @@ namespace Opm } } - if (active_[Gas]) { + if (active()[Gas]) { if (distr[Gas] > 0.0) { xw.wellSolutions()[GFrac * nw + well_index] = 1.0 - wsolvent(); if (has_solvent) { @@ -1313,10 +1313,10 @@ namespace Opm // this will happen. } else if (wellType() == PRODUCER) { // producers // TODO: the following are not addressed for the solvent case yet - if (active_[Water]) { + if (active()[Water]) { xw.wellSolutions()[WFrac * nw + well_index] = 1.0 / np; } - if (active_[Gas]) { + if (active()[Gas]) { xw.wellSolutions()[GFrac * nw + well_index] = 1.0 / np; } } else { @@ -1670,7 +1670,7 @@ namespace Opm bool StandardWell:: getWellConvergence(Simulator& ebosSimulator, - std::vector& B_avg, + const std::vector& B_avg, const ModelParameters& param) const { typedef double Scalar; @@ -1774,4 +1774,29 @@ namespace Opm } + + + + + template + void + StandardWell:: + wellEqIteration(Simulator& ebosSimulator, + const ModelParameters& param, + WellState& well_state) + { + // We assemble the well equations, then we check the convergence, + // which is why we do not put the assembleWellEq here. + BVector dx_well(1); + invDuneD_.mv(resWell_, dx_well); + + updateWellState(dx_well, param, well_state); + + // updateWellControls uses communication + // Therefore the following is executed if there + // are active wells anywhere in the global domain. + updateWellControl(well_state); + setWellVariables(well_state); + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index b108e9d37..360d8f46f 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -140,9 +140,13 @@ namespace Opm const double wsolvent() const; virtual bool getWellConvergence(Simulator& ebosSimulator, - std::vector& B_avg, + const std::vector& B_avg, const ModelParameters& param) const = 0; + virtual void wellEqIteration(Simulator& ebosSimulator, + const ModelParameters& param, + WellState& well_state) = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From 07d24b9d8d40a8765bf186824032b4383ac755b9 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 27 Jun 2017 12:17:36 +0200 Subject: [PATCH 021/104] solveWellEq in StandardWellsDense calls solveWellEq from each individual well. --- opm/autodiff/StandardWell.hpp | 8 +- opm/autodiff/StandardWellsDense.hpp | 4 + opm/autodiff/StandardWellsDense_impl.hpp | 113 +---------------------- opm/autodiff/WellInterface.hpp | 6 ++ opm/autodiff/WellInterface_impl.hpp | 12 +++ 5 files changed, 30 insertions(+), 113 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 6e9252a6f..3b8766845 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -115,10 +115,10 @@ namespace Opm const double Tw, const EvalWell& bhp, const double& cdp, const bool& allow_cf, std::vector& cq_s) const; - void assembleWellEq(Simulator& ebosSimulator, - const double dt, - WellState& well_state, - bool only_wells); + virtual void assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells); bool allow_cross_flow(const Simulator& ebosSimulator) const; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 1e56b0adc..6196b4092 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -298,6 +298,10 @@ enum WellVariablePositions { const Wells* wells_; const std::vector< const Well* > wells_ecl_; + // the number of wells in this process + // trying to not use things from Wells struct + const int number_of_wells_; + // a vector of all the wells. // eventually, the wells_ above should be gone. // the name is just temporary diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index fe911ab29..13ea4fb18 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -16,6 +16,7 @@ namespace Opm { : wells_active_(wells_arg!=nullptr) , wells_(wells_arg) , wells_ecl_(wells_ecl) + , number_of_wells_(wells_arg ? (wells_arg->number_of_wells) : 0) , well_container_(createWellContainer(wells_ecl, wells_arg, current_timeIdx) ) , well_collection_(well_collection) , param_(param) @@ -260,116 +261,10 @@ namespace Opm { WellState& well_state, bool only_wells) { - const int nw = wells().number_of_wells; - const int numComp = numComponents(); - const int np = numPhases(); - - // clear all entries - duneB_ = 0.0; - duneC_ = 0.0; - invDuneD_ = 0.0; - resWell_ = 0.0; - - auto& ebosJac = ebosSimulator.model().linearizer().matrix(); - auto& ebosResid = ebosSimulator.model().linearizer().residual(); - - const double volume = 0.002831684659200; // 0.1 cu ft; - for (int w = 0; w < nw; ++w) { - bool allow_cf = allow_cross_flow(w, ebosSimulator); - const EvalWell& bhp = getBhp(w); - for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) { - - const int cell_idx = wells().well_cells[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); - std::vector cq_s(numComp,0.0); - std::vector mob(numComp, 0.0); - getMobility(ebosSimulator, w, perf, cell_idx, mob); - computeWellFlux(w, wells().WI[perf], intQuants, mob, bhp, wellPerforationPressureDiffs()[perf], allow_cf, cq_s); - - for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - - // the cq_s entering mass balance equations need to consider the efficiency factors. - const EvalWell cq_s_effective = cq_s[componentIdx] * well_perforation_efficiency_factors_[perf]; - - if (!only_wells) { - // subtract sum of component fluxes in the reservoir equation. - // need to consider the efficiency factor - ebosResid[cell_idx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.value(); - } - - // subtract sum of phase fluxes in the well equations. - resWell_[w][componentIdx] -= cq_s[componentIdx].value(); - - // assemble the jacobians - for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { - if (!only_wells) { - // also need to consider the efficiency factor when manipulating the jacobians. - duneB_[w][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix - } - invDuneD_[w][w][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); - } - - for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { - if (!only_wells) { - // also need to consider the efficiency factor when manipulating the jacobians. - ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); - duneC_[w][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); - } - } - - // add a trivial equation for the dummy phase for 2p cases (Only support water + oil) - if ( numComp < numWellEq ) { - assert(!active_[ Gas ]); - invDuneD_[w][w][Gas][Gas] = 1.0; - } - - // Store the perforation phase flux for later usage. - if (has_solvent_ && componentIdx == solventSaturationIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) - well_state.perfRateSolvent()[perf] = cq_s[componentIdx].value(); - } else { - well_state.perfPhaseRates()[perf*np + componentIdx] = cq_s[componentIdx].value(); - } - } - - if (has_polymer_) { - EvalWell cq_s_poly = cq_s[Water]; - if (wells().type[w] == INJECTOR) { - cq_s_poly *= wpolymer(w); - } else { - cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); - } - if (!only_wells) { - for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { - ebosJac[cell_idx][cell_idx][contiPolymerEqIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_poly.derivative(pvIdx); - } - ebosResid[cell_idx][contiPolymerEqIdx] -= cq_s_poly.value(); - } - } - - // Store the perforation pressure for later usage. - well_state.perfPress()[perf] = well_state.bhp()[w] + wellPerforationPressureDiffs()[perf]; - } - - // add vol * dF/dt + Q to the well equations; - for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - EvalWell resWell_loc = (wellSurfaceVolumeFraction(w, componentIdx) - F0_[w + nw*componentIdx]) * volume / dt; - resWell_loc += getQs(w, componentIdx); - for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { - invDuneD_[w][w][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); - } - resWell_[w][componentIdx] += resWell_loc.value(); - } - - // add trivial equation for polymer - if (has_polymer_) { - invDuneD_[w][w][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; // - } + // TODO: incoporate the new change to StandardWell + for (int w = 0; w < number_of_wells_; ++w) { + well_container_[w]->assembleWellEq(ebosSimulator, dt, well_state, only_wells); } - - - - // do the local inversion of D. - localInvert( invDuneD_ ); } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 360d8f46f..582e04d39 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -64,6 +64,7 @@ namespace Opm static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); /// Constructor + // TODO: Wel can be reference. WellInterface(const Well* well, const int time_step, const Wells* wells); /// Well name. @@ -147,6 +148,11 @@ namespace Opm const ModelParameters& param, WellState& well_state) = 0; + virtual void assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells) = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index a403bdda8..264fe9eec 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -267,6 +267,18 @@ namespace Opm + template + bool + WellInterface:: + allowCrossFlow() const + { + return allow_cf_; + } + + + + + template const PhaseUsage& WellInterface:: From e3399ca2030a0f886066db9dcda657c382b66d8d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 27 Jun 2017 15:16:22 +0200 Subject: [PATCH 022/104] fixing some compilation error after rebase. while a lot of new stuff have not entered, since many of the essential parts are in different files now. Rebasing will not incoporate the new stuff automatically. --- opm/autodiff/StandardWell.hpp | 27 ++++++------------------ opm/autodiff/StandardWell_impl.hpp | 22 +++++++++---------- opm/autodiff/StandardWellsDense_impl.hpp | 5 ++--- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 3b8766845..6dc581efb 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -48,6 +48,7 @@ namespace Opm using FluidSystem = typename WellInterface::FluidSystem; using MaterialLaw = typename WellInterface::MaterialLaw; using ModelParameters = typename WellInterface::ModelParameters; + using BlackoilIndices = typename WellInterface::BlackoilIndices; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -61,9 +62,11 @@ namespace Opm typedef double Scalar; // static const int numEq = BlackoilIndices::numEq; - static const int numEq = 3; - static const int numWellEq = numEq; //number of wellEq is the same as numEq in the model - static const int solventCompIdx = 3; //TODO get this from ebos + static const int numEq = BlackoilIndices::numEq; + static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? 3:numEq; // //numEq; //number of wellEq is only for 3 for polymer + static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; + static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; + typedef Dune::FieldVector VectorBlockType; typedef Dune::FieldMatrix MatrixBlockType; typedef Dune::BCRSMatrix Mat; @@ -71,24 +74,6 @@ namespace Opm typedef DenseAd::Evaluation EvalWell; typedef DenseAd::Evaluation Eval; - // for now, using the matrix and block version in StandardWellsDense. - // TODO: for bettern generality, it should contain blocksize_field and blocksize_well. - // They are allowed to be different and it will create four types of matrix blocks and two types of - // vector blocks. - - /* const static int blocksize = 3; - typedef double Scalar; - typedef Dune::FieldVector VectorBlockType; - typedef Dune::FieldMatrix MatrixBlockType; - typedef Dune::BCRSMatrix Mat; - typedef Dune::BlockVector BVector; - typedef DenseAd::Evaluation EvalWell; */ - /* using WellInterface::EvalWell; - using WellInterface::BVector; - using WellInterface::Mat; - using WellInterface::MatrixBlockType; - using WellInterface::VectorBlockType; */ - StandardWell(const Well* well, const int time_step, const Wells* wells); /// the densities of the fluid in each perforation diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index c7b492c2f..21a4df6a0 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -239,7 +239,7 @@ namespace Opm /* if (has_solvent_ ) { // TODO: investigate whether the use of the comp_frac is justified. double comp_frac = 0.0; - if (compIdx == solventCompIdx) { // solvent + if (compIdx == contiSolventEqIdx) { // solvent comp_frac = wells().comp_frac[np*wellIdx + pu.phase_pos[ Gas ]] * wsolvent(wellIdx); } else if (compIdx == pu.phase_pos[ Gas ]) { comp_frac = wells().comp_frac[np*wellIdx + compIdx] * (1.0 - wsolvent(wellIdx)); @@ -306,7 +306,7 @@ namespace Opm // TODO: handling solvent related later /* if (has_solvent_ && phase_under_control == Gas) { // for GRAT controlled wells solvent is included in the target - wellVolumeFractionScaledPhaseUnderControl += wellVolumeFractionScaled(solventCompIdx); + wellVolumeFractionScaledPhaseUnderControl += wellVolumeFractionScaled(contiSolventEqIdx); } */ if (phase == phase_under_control) { @@ -368,7 +368,7 @@ namespace Opm const WellControls* wc = wellControls(); if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { - if (has_solvent && compIdx == solventCompIdx) { + if (has_solvent && compIdx == contiSolventEqIdx) { return wellVolumeFraction(compIdx); } const double* distr = well_controls_get_current_distr(wc); @@ -403,7 +403,7 @@ namespace Opm return well_variables_[GFrac]; } - if (compIdx == solventCompIdx) { + if (compIdx == contiSolventEqIdx) { return well_variables_[SFrac]; } @@ -489,7 +489,7 @@ namespace Opm b_perfcells_dense[phase] = extendEval(fs.invB(ebosPhaseIdx)); } if (has_solvent) { - b_perfcells_dense[solventCompIdx] = extendEval(intQuants.solventInverseFormationVolumeFactor()); + b_perfcells_dense[contiSolventEqIdx] = extendEval(intQuants.solventInverseFormationVolumeFactor()); } // Pressure drawdown (also used to determine direction of flow) @@ -541,7 +541,7 @@ namespace Opm } if (has_solvent) { - volumeRatio += cmix_s[solventCompIdx] / b_perfcells_dense[solventCompIdx]; + volumeRatio += cmix_s[contiSolventEqIdx] / b_perfcells_dense[contiSolventEqIdx]; } if (active()[Oil] && active()[Gas]) { @@ -657,7 +657,7 @@ namespace Opm } // Store the perforation phase flux for later usage. - if (componentIdx == solventCompIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) + if (componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) well_state.perfRateSolvent()[perf] = cq_s[componentIdx].value(); } else { well_state.perfPhaseRates()[perf*np + componentIdx] = cq_s[componentIdx].value(); @@ -750,7 +750,7 @@ namespace Opm mob[phase] = extendEval(intQuants.mobility(ebosPhaseIdx)); } if (has_solvent) { - mob[solventCompIdx] = extendEval(intQuants.solventMobility()); + mob[contiSolventEqIdx] = extendEval(intQuants.solventMobility()); } } else { @@ -1518,8 +1518,8 @@ namespace Opm // We use cell values for solvent injector if (has_solvent) { - b_perf[numComp*perf + solventCompIdx] = intQuants.solventInverseFormationVolumeFactor().value(); - surf_dens_perf[numComp*perf + solventCompIdx] = intQuants.solventRefDensity(); + b_perf[numComp*perf + contiSolventEqIdx] = intQuants.solventInverseFormationVolumeFactor().value(); + surf_dens_perf[numComp*perf + contiSolventEqIdx] = intQuants.solventRefDensity(); } } } @@ -1764,7 +1764,7 @@ namespace Opm perfRates[perf*numComponent + phase] = xw.perfPhaseRates()[(first_perf_ + perf) * np + phase]; } if(has_solvent) { - perfRates[perf*numComponent + solventCompIdx] = xw.perfRateSolvent()[perf + first_perf_]; + perfRates[perf*numComponent + contiSolventEqIdx] = xw.perfRateSolvent()[perf + first_perf_]; } } diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 13ea4fb18..b7397b2db 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1044,7 +1044,7 @@ namespace Opm { std::vector< Scalar > maxNormWell(numComp, Scalar() ); - auto& grid = ebosSimulator.gridManager().grid(); + const auto& grid = ebosSimulator.gridManager().grid(); const auto& gridView = grid.leafGridView(); ElementContext elemCtx(ebosSimulator); const auto& elemEndIt = gridView.template end(); @@ -1088,7 +1088,6 @@ namespace Opm { } } - const auto& grid = ebosSimulator.gridManager().grid(); grid.comm().max(maxNormWell.data(), maxNormWell.size()); Vector well_flux_residual(numComp); @@ -3122,7 +3121,7 @@ namespace Opm { B += 1 / fs.invB(ebosPhaseIdx).value(); } if (has_solvent_) { - auto& B = B_avg[ solventCompIdx ]; + auto& B = B_avg[solventSaturationIdx]; B += 1 / intQuants.solventInverseFormationVolumeFactor().value(); } } From e5b5e250fe508dae381d66cf3acfa461c041bfcf Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 27 Jun 2017 16:04:04 +0200 Subject: [PATCH 023/104] incoporating more content from PR 1220 while the parts related to polymer are not incoporated fully yet, since it has been considered in the new well model refactoring. --- opm/autodiff/StandardWell.hpp | 8 ++-- opm/autodiff/StandardWell_impl.hpp | 47 ++++++++++++++++++++---- opm/autodiff/StandardWellsDense.hpp | 1 + opm/autodiff/StandardWellsDense_impl.hpp | 1 - opm/autodiff/WellInterface.hpp | 2 + opm/autodiff/WellInterface_impl.hpp | 19 ++++++---- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 6dc581efb..27fb9eaa7 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -40,8 +40,9 @@ namespace Opm { public: - // using WellInterface::Simulator; - // using WellInterface::WellState; + // TODO: several functions related to polymer and PLYSHLOG are not incorprated yet, + // like the function wpolymer, setupCompressedToCartesian, computeRepRadiusPerfLength, + // They are introduced though PR 1220 and will be included later. using Simulator = typename WellInterface::Simulator; using WellState = typename WellInterface::WellState; using IntensiveQuantities = typename WellInterface::IntensiveQuantities; @@ -56,7 +57,8 @@ namespace Opm enum WellVariablePositions { XvarWell = 0, WFrac = 1, - GFrac = 2 + GFrac = 2, + SFrac = 3 }; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 21a4df6a0..931e5b88f 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -144,6 +144,7 @@ namespace Opm setWellVariables(const WellState& well_state) { const int nw = well_state.bhp().size(); + // for two-phase numComp < numWellEq const int numComp = numComponents(); for (int eqIdx = 0; eqIdx < numComp; ++eqIdx) { const unsigned int idx = nw * eqIdx + indexOfWell(); @@ -239,7 +240,7 @@ namespace Opm /* if (has_solvent_ ) { // TODO: investigate whether the use of the comp_frac is justified. double comp_frac = 0.0; - if (compIdx == contiSolventEqIdx) { // solvent + if (has_solvent && compIdx == contiSolventEqIdx) { // solvent comp_frac = wells().comp_frac[np*wellIdx + pu.phase_pos[ Gas ]] * wsolvent(wellIdx); } else if (compIdx == pu.phase_pos[ Gas ]) { comp_frac = wells().comp_frac[np*wellIdx + compIdx] * (1.0 - wsolvent(wellIdx)); @@ -403,7 +404,7 @@ namespace Opm return well_variables_[GFrac]; } - if (compIdx == contiSolventEqIdx) { + if (has_solvent && compIdx == contiSolventEqIdx) { return well_variables_[SFrac]; } @@ -643,13 +644,19 @@ namespace Opm for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { if (!only_wells) { // also need to consider the efficiency factor when manipulating the jacobians. - ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); duneB_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix - duneC_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); } invDuneD_[0][0][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); } + for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { + if (!only_wells) { + // also need to consider the efficiency factor when manipulating the jacobians. + ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + duneC_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + } + } + // add trivial equation for 2p cases (Only support water + oil) if (numComp == 2) { assert(!active()[ Gas ]); @@ -657,13 +664,30 @@ namespace Opm } // Store the perforation phase flux for later usage. - if (componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) + if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) well_state.perfRateSolvent()[perf] = cq_s[componentIdx].value(); } else { well_state.perfPhaseRates()[perf*np + componentIdx] = cq_s[componentIdx].value(); } } + // TODO: will incoporate the following related to polymer later + // which was introduced in PR 1220 + /* if (has_polymer_) { + EvalWell cq_s_poly = cq_s[Water]; + if (wellType() == INJECTOR) { + cq_s_poly *= wpolymer(w); + } else { + cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); + } + if (!only_wells) { + for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { + ebosJac[cell_idx][cell_idx][contiPolymerEqIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_poly.derivative(pvIdx); + } + ebosResid[cell_idx][contiPolymerEqIdx] -= cq_s_poly.value(); + } + } */ + // Store the perforation pressure for later usage. well_state.perfPress()[perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; } @@ -677,6 +701,13 @@ namespace Opm invDuneD_[0][0][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); } resWell_[0][componentIdx] += resWell_loc.value(); + + + // TODO: to incoporate the following polymer related later, which was introduced in PR 1220 + /* // add trivial equation for polymer + if (has_polymer_) { + invDuneD_[w][w][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; // + } */ } // do the local inversion of D. @@ -733,6 +764,8 @@ namespace Opm const int perf, std::vector& mob) const { + // TODO: not incoporating the PLYSHLOG related for now. + // which is incoporate from PR 1220 and should be included later. const int np = numberOfPhases(); const int cell_idx = wellCells()[perf]; assert (int(mob.size()) == numComponents()); @@ -1281,10 +1314,10 @@ namespace Opm xw.wellSolutions()[WFrac*nw + well_index] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; } if (active()[ Gas ]) { - xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (1.0 - wsolvent()) * xw.wellRates()[np*well_index + Gas] / tot_well_rate ; + xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (xw.wellRates()[np*well_index + Gas] - xw.solventWellRate(well_index)) / tot_well_rate ; } if (has_solvent) { - xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * wsolvent() * xw.wellRates()[np*well_index + Gas] / tot_well_rate ; + xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; } } else { // tot_well_rate == 0 if (wellType() == INJECTOR) { diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 6196b4092..21058f058 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index b7397b2db..05b251efd 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1,5 +1,4 @@ -#include namespace Opm { diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 582e04d39..2ddb24f52 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -37,6 +37,8 @@ #include #include +#include + #include #include #include diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 264fe9eec..f04f5f2ec 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -298,7 +298,9 @@ namespace Opm WellInterface:: flowPhaseToEbosCompIdx( const int phaseIdx ) const { - const int phaseToComp[ 4 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx, solventCompIdx }; + const int phaseToComp[ 3 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx}; + if (phaseIdx > 2 ) + return phaseIdx; return phaseToComp[ phaseIdx ]; } @@ -311,12 +313,15 @@ namespace Opm WellInterface:: flowToEbosPvIdx( const int flowPv ) const { - const int flowToEbos[ 4 ] = { - BlackoilIndices::pressureSwitchIdx, - BlackoilIndices::waterSaturationIdx, - BlackoilIndices::compositionSwitchIdx, - BlackoilIndices::solventSaturationIdx - }; + const int flowToEbos[ 3 ] = { + BlackoilIndices::pressureSwitchIdx, + BlackoilIndices::waterSaturationIdx, + BlackoilIndices::compositionSwitchIdx + }; + + if (flowPv > 2 ) + return flowPv; + return flowToEbos[ flowPv ]; } From 48dee6bd7a4d23f67a4f6b7b820df9127ffe584e Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 27 Jun 2017 16:59:52 +0200 Subject: [PATCH 024/104] renaming function allCrossFlow() in StandardWell to avoid confusion. and the detail of the function needs to be justified. --- opm/autodiff/StandardWell.hpp | 2 +- opm/autodiff/StandardWell_impl.hpp | 4 ++-- opm/autodiff/StandardWellsDense_impl.hpp | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 27fb9eaa7..2535ec05d 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -107,7 +107,7 @@ namespace Opm WellState& well_state, bool only_wells); - bool allow_cross_flow(const Simulator& ebosSimulator) const; + bool crossFlowAllowed(const Simulator& ebosSimulator) const; void getMobility(const Simulator& ebosSimulator, const int perf, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 931e5b88f..4c9199f91 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -614,7 +614,7 @@ namespace Opm // TODO: it probably can be static member for StandardWell const double volume = 0.002831684659200; // 0.1 cu ft; - const bool allow_cf = allow_cross_flow(ebosSimulator); + const bool allow_cf = crossFlowAllowed(ebosSimulator); const EvalWell& bhp = getBhp(); @@ -721,7 +721,7 @@ namespace Opm template bool StandardWell:: - allow_cross_flow(const Simulator& ebosSimulator) const + crossFlowAllowed(const Simulator& ebosSimulator) const { if (allowCrossFlow()) { return true; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 05b251efd..95f982fa7 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -260,13 +260,15 @@ namespace Opm { WellState& well_state, bool only_wells) { - // TODO: incoporate the new change to StandardWell for (int w = 0; w < number_of_wells_; ++w) { well_container_[w]->assembleWellEq(ebosSimulator, dt, well_state, only_wells); } } + + + template void StandardWellsDense:: From fb5fa836c06a2fa4b152242d5e359a5bf303e0a1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 27 Jun 2017 17:16:42 +0200 Subject: [PATCH 025/104] removing some repeated contents in getWellConvergence in StandardWellsDense which results from the rebasing process. --- opm/autodiff/StandardWellsDense_impl.hpp | 35 +----------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 95f982fa7..32f4efc19 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1045,40 +1045,6 @@ namespace Opm { std::vector< Scalar > maxNormWell(numComp, Scalar() ); - const auto& grid = ebosSimulator.gridManager().grid(); - const auto& gridView = grid.leafGridView(); - ElementContext elemCtx(ebosSimulator); - const auto& elemEndIt = gridView.template end(); - - for (auto elemIt = gridView.template begin(); - elemIt != elemEndIt; ++elemIt) - { - elemCtx.updatePrimaryStencil(*elemIt); - elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0); - - const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0); - const auto& fs = intQuants.fluidState(); - - for ( int phaseIdx = 0; phaseIdx < np; ++phaseIdx ) - { - auto& B = B_avg[ phaseIdx ]; - const int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phaseIdx); - - B += 1 / fs.invB(ebosPhaseIdx).value(); - } - if (has_solvent_) { - auto& B = B_avg[ solventSaturationIdx ]; - B += 1 / intQuants.solventInverseFormationVolumeFactor().value(); - } - } - - // compute global average - grid.comm().sum(B_avg.data(), B_avg.size()); - for(auto& bval: B_avg) - { - bval/=global_nc_; - } - auto res = residual(); const int nw = res.size() / numComp; @@ -1089,6 +1055,7 @@ namespace Opm { } } + const auto& grid = ebosSimulator.gridManager().grid(); grid.comm().max(maxNormWell.data(), maxNormWell.size()); Vector well_flux_residual(numComp); From d535157b1aa233c73453cab7109d1e6739c1d64f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 28 Jun 2017 11:15:04 +0200 Subject: [PATCH 026/104] cleaning up a few fucntions in StandardWellsDense to remove the implementation implemented in StandardWell already. --- opm/autodiff/StandardWell.hpp | 6 +- opm/autodiff/StandardWell_impl.hpp | 1 + opm/autodiff/StandardWellsDense.hpp | 3 +- opm/autodiff/StandardWellsDense_impl.hpp | 450 ++--------------------- opm/autodiff/WellInterface.hpp | 5 + 5 files changed, 39 insertions(+), 426 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 2535ec05d..981344c0b 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -126,12 +126,12 @@ namespace Opm WellState& well_state) const; // TODO: later will check wheter we need current - void updateWellStateWithTarget(const int current, - WellState& xw) const; + virtual void updateWellStateWithTarget(const int current, + WellState& xw) const; // TODO: this should go to the WellInterface, while updateWellStateWithTarget // will need touch different types of well_state, we will see. - void updateWellControl(WellState& xw) const; + virtual void updateWellControl(WellState& xw) const; virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 4c9199f91..900c8a556 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -818,6 +818,7 @@ namespace Opm const BlackoilModelParameters& param, WellState& well_state) const { + // TODO: to check whether all the things from PR 1220 were incoporated. const int np = numberOfPhases(); const int nw = well_state.bhp().size(); const double dFLimit = param.dbhp_max_rel_; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 21058f058..69cc069fa 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -300,7 +300,8 @@ enum WellVariablePositions { const std::vector< const Well* > wells_ecl_; // the number of wells in this process - // trying to not use things from Wells struct + // trying not to use things from Wells struct + // TODO: maybe a better name to emphasize it is local? const int number_of_wells_; // a vector of all the wells. diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 32f4efc19..363392e77 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1034,54 +1034,24 @@ namespace Opm { typedef double Scalar; typedef std::vector< Scalar > Vector; - const int np = numPhases(); const int numComp = numComponents(); std::vector< Scalar > B_avg( numComp, Scalar() ); computeAverageFormationFactor(ebosSimulator, B_avg); - const double tol_wells = param_.tolerance_wells_; - const double maxResidualAllowed = param_.max_residual_allowed_; + bool converged_well = true; - std::vector< Scalar > maxNormWell(numComp, Scalar() ); - - auto res = residual(); - const int nw = res.size() / numComp; - - for ( int compIdx = 0; compIdx < numComp; ++compIdx ) - { - for ( int w = 0; w < nw; ++w ) { - maxNormWell[compIdx] = std::max(maxNormWell[compIdx], std::abs(res[nw*compIdx + w])); + // TODO: to check the strategy here + // currently, if there is any well not converged, we consider the well eqautions do not get converged + for (const auto& well : well_container_) { + if ( !well->getWellConvergence(ebosSimulator, B_avg, param_) ) { + converged_well = false; } + break; } - const auto& grid = ebosSimulator.gridManager().grid(); - grid.comm().max(maxNormWell.data(), maxNormWell.size()); - - Vector well_flux_residual(numComp); - bool converged_Well = true; - - // Finish computation - for ( int compIdx = 0; compIdx < numComp; ++compIdx ) - { - well_flux_residual[compIdx] = B_avg[compIdx] * maxNormWell[compIdx]; - converged_Well = converged_Well && (well_flux_residual[compIdx] < tol_wells); - } - - // if one of the residuals is NaN, throw exception, so that the solver can be restarted - for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { - const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); - - if (std::isnan(well_flux_residual[phaseIdx])) { - OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName); - } - - if (well_flux_residual[phaseIdx] > maxResidualAllowed) { - OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName); - } - } - - if ( terminal_output_ ) + // TODO: to think about the output here. + /* if ( terminal_output_ ) { // Only rank 0 does print to std::cout if (iteration == 0) { @@ -1104,8 +1074,8 @@ namespace Opm { ss.precision(oprec); ss.flags(oflags); OpmLog::note(ss.str()); - } - return converged_Well; + } */ + return converged_well; } @@ -1249,306 +1219,13 @@ namespace Opm { updateWellState(const BVector& dwells, WellState& well_state) const { + // TODO: the interface of the function should change. + // the current plan is to make different wells have different matrix + // and residual system. if( !localWellsActive() ) return; - - const int np = wells().number_of_phases; - const int nw = wells().number_of_wells; - - double dFLimit = dWellFractionMax(); - double dBHPLimit = dbhpMaxRel(); - std::vector xvar_well_old = well_state.wellSolutions(); - - for (int w = 0; w < nw; ++w) { - - // update the second and third well variable (The flux fractions) - std::vector F(np,0.0); - if (active_[ Water ]) { - const int sign2 = dwells[w][WFrac] > 0 ? 1: -1; - const double dx2_limited = sign2 * std::min(std::abs(dwells[w][WFrac]),dFLimit); - well_state.wellSolutions()[WFrac*nw + w] = xvar_well_old[WFrac*nw + w] - dx2_limited; - } - - if (active_[ Gas ]) { - const int sign3 = dwells[w][GFrac] > 0 ? 1: -1; - const double dx3_limited = sign3 * std::min(std::abs(dwells[w][GFrac]),dFLimit); - well_state.wellSolutions()[GFrac*nw + w] = xvar_well_old[GFrac*nw + w] - dx3_limited; - } - - if (has_solvent_) { - const int sign4 = dwells[w][SFrac] > 0 ? 1: -1; - const double dx4_limited = sign4 * std::min(std::abs(dwells[w][SFrac]),dFLimit); - well_state.wellSolutions()[SFrac*nw + w] = xvar_well_old[SFrac*nw + w] - dx4_limited; - } - - assert(active_[ Oil ]); - F[Oil] = 1.0; - if (active_[ Water ]) { - F[Water] = well_state.wellSolutions()[WFrac*nw + w]; - F[Oil] -= F[Water]; - } - - if (active_[ Gas ]) { - F[Gas] = well_state.wellSolutions()[GFrac*nw + w]; - F[Oil] -= F[Gas]; - } - - double F_solvent = 0.0; - if (has_solvent_) { - F_solvent = well_state.wellSolutions()[SFrac*nw + w]; - F[Oil] -= F_solvent; - } - - if (active_[ Water ]) { - if (F[Water] < 0.0) { - if (active_[ Gas ]) { - F[Gas] /= (1.0 - F[Water]); - } - if (has_solvent_) { - F_solvent /= (1.0 - F[Water]); - } - F[Oil] /= (1.0 - F[Water]); - F[Water] = 0.0; - } - } - if (active_[ Gas ]) { - if (F[Gas] < 0.0) { - if (active_[ Water ]) { - F[Water] /= (1.0 - F[Gas]); - } - if (has_solvent_) { - F_solvent /= (1.0 - F[Gas]); - } - F[Oil] /= (1.0 - F[Gas]); - F[Gas] = 0.0; - } - } - if (F[Oil] < 0.0) { - if (active_[ Water ]) { - F[Water] /= (1.0 - F[Oil]); - } - if (active_[ Gas ]) { - F[Gas] /= (1.0 - F[Oil]); - } - if (has_solvent_) { - F_solvent /= (1.0 - F[Oil]); - } - F[Oil] = 0.0; - } - - if (active_[ Water ]) { - well_state.wellSolutions()[WFrac*nw + w] = F[Water]; - } - if (active_[ Gas ]) { - well_state.wellSolutions()[GFrac*nw + w] = F[Gas]; - } - if(has_solvent_) { - well_state.wellSolutions()[SFrac*nw + w] = F_solvent; - } - - // F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent. - // More testing is needed to make sure this is correct for well groups and THP. - if (has_solvent_){ - F[Gas] += F_solvent; - } - - // The interpretation of the first well variable depends on the well control - 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 = well_state.currentControls()[w]; - const double target_rate = well_controls_iget_target(wc, current); - - std::vector g = {1,1,0.01}; - if (well_controls_iget_type(wc, current) == RESERVOIR_RATE) { - const double* distr = well_controls_iget_distr(wc, current); - for (int p = 0; p < np; ++p) { - if (distr[p] > 0.) { // For injection wells, there only one non-zero distr value - F[p] /= distr[p]; - } else { - F[p] = 0.; - } - } - } else { - for (int p = 0; p < np; ++p) { - F[p] /= g[p]; - } - } - - switch (well_controls_iget_type(wc, current)) { - case THP: // The BHP and THP both uses the total rate as first well variable. - case BHP: - { - well_state.wellSolutions()[nw*XvarWell + w] = xvar_well_old[nw*XvarWell + w] - dwells[w][XvarWell]; - - switch (wells().type[w]) { - case INJECTOR: - for (int p = 0; p < np; ++p) { - const double comp_frac = wells().comp_frac[np*w + p]; - well_state.wellRates()[w*np + p] = comp_frac * well_state.wellSolutions()[nw*XvarWell + w]; - } - break; - case PRODUCER: - for (int p = 0; p < np; ++p) { - well_state.wellRates()[w*np + p] = well_state.wellSolutions()[nw*XvarWell + w] * F[p]; - } - break; - } - - if (well_controls_iget_type(wc, current) == THP) { - - // Calculate bhp from thp control and well rates - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - - const Opm::PhaseUsage& pu = phase_usage_; - - if (active_[ Water ]) { - aqua = well_state.wellRates()[w*np + pu.phase_pos[ Water ] ]; - } - if (active_[ Oil ]) { - liquid = well_state.wellRates()[w*np + pu.phase_pos[ Oil ] ]; - } - if (active_[ Gas ]) { - vapour = well_state.wellRates()[w*np + pu.phase_pos[ Gas ] ]; - } - - const int vfp = well_controls_iget_vfp(wc, current); - const double& thp = well_controls_iget_target(wc, current); - const double& alq = well_controls_iget_alq(wc, current); - - //Set *BHP* target by calculating bhp from THP - const WellType& well_type = wells().type[w]; - // pick the density in the top layer - const int perf = wells().well_connpos[w]; - const double rho = well_perforation_densities_[perf]; - - if (well_type == INJECTOR) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), w, vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(), - rho, gravity_); - - well_state.bhp()[w] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; - } - else if (well_type == PRODUCER) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), w, vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(), - rho, gravity_); - - well_state.bhp()[w] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; - } - else { - OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); - } - } - } - break; - case SURFACE_RATE: // Both rate controls use bhp as first well variable - case RESERVOIR_RATE: - { - const int sign1 = dwells[w][XvarWell] > 0 ? 1: -1; - const double dx1_limited = sign1 * std::min(std::abs(dwells[w][XvarWell]),std::abs(xvar_well_old[nw*XvarWell + w])*dBHPLimit); - well_state.wellSolutions()[nw*XvarWell + w] = std::max(xvar_well_old[nw*XvarWell + w] - dx1_limited,1e5); - well_state.bhp()[w] = well_state.wellSolutions()[nw*XvarWell + w]; - - if (well_controls_iget_type(wc, current) == SURFACE_RATE) { - if (wells().type[w]==PRODUCER) { - - const double* distr = well_controls_iget_distr(wc, current); - - double F_target = 0.0; - for (int p = 0; p < np; ++p) { - F_target += distr[p] * F[p]; - } - for (int p = 0; p < np; ++p) { - well_state.wellRates()[np*w + p] = F[p] * target_rate / F_target; - } - } else { - - for (int p = 0; p < np; ++p) { - well_state.wellRates()[w*np + p] = wells().comp_frac[np*w + p] * target_rate; - } - } - } else { // RESERVOIR_RATE - for (int p = 0; p < np; ++p) { - well_state.wellRates()[np*w + p] = F[p] * target_rate; - } - } - } - break; - } // end of switch (well_controls_iget_type(wc, current)) - } // end of for (int w = 0; w < nw; ++w) - - - // for the wells having a THP constaint, we should update their thp value - // If it is under THP control, it will be set to be the target value. Otherwise, - // the thp value will be calculated based on the bhp value, assuming the bhp value is correctly calculated. - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - const int nwc = well_controls_get_num(wc); - // Looping over all controls until we find a THP constraint - int ctrl_index = 0; - for ( ; ctrl_index < nwc; ++ctrl_index) { - if (well_controls_iget_type(wc, ctrl_index) == THP) { - // the current control - const int current = well_state.currentControls()[w]; - // If under THP control at the moment - if (current == ctrl_index) { - const double thp_target = well_controls_iget_target(wc, current); - well_state.thp()[w] = thp_target; - } else { // otherwise we calculate the thp from the bhp value - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - - const Opm::PhaseUsage& pu = phase_usage_; - - if (active_[ Water ]) { - aqua = well_state.wellRates()[w*np + pu.phase_pos[ Water ] ]; - } - if (active_[ Oil ]) { - liquid = well_state.wellRates()[w*np + pu.phase_pos[ Oil ] ]; - } - if (active_[ Gas ]) { - vapour = well_state.wellRates()[w*np + pu.phase_pos[ Gas ] ]; - } - - const double alq = well_controls_iget_alq(wc, ctrl_index); - const int table_id = well_controls_iget_vfp(wc, ctrl_index); - - const WellType& well_type = wells().type[w]; - const int perf = wells().well_connpos[w]; //first perforation. - if (well_type == INJECTOR) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), w, vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(), - wellPerforationDensities()[perf], gravity_); - - const double bhp = well_state.bhp()[w]; - well_state.thp()[w] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp + dp); - } else if (well_type == PRODUCER) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), w, vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(), - wellPerforationDensities()[perf], gravity_); - - const double bhp = well_state.bhp()[w]; - well_state.thp()[w] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp + dp, alq); - } else { - OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); - } - } - - // the THP control is found, we leave the loop now - break; - } - } // end of for loop for seaching THP constraints - - // no THP constraint found - if (ctrl_index == nwc) { // not finding a THP contstraints - well_state.thp()[w] = 0.0; - } - } // end of for (int w = 0; w < nw; ++w) + /* for (auto& well : well_container_) { + well->updateWellState(dwells, param_, well_state); + } */ } @@ -1566,95 +1243,24 @@ namespace Opm { // we simply return. if( !wellsActive() ) return ; - const int np = wells().number_of_phases; - const int nw = wells().number_of_wells; - - // keeping a copy of the current controls, to see whether control changes later. - std::vector old_control_index(nw, 0); - for (int w = 0; w < nw; ++w) { - old_control_index[w] = xw.currentControls()[w]; - } - - // Find, for each well, if any constraints are broken. If so, - // switch control to first broken constraint. -#pragma omp parallel for schedule(dynamic) - for (int w = 0; w < nw; ++w) { - 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 (wellhelpers::constraintBroken( - xw.bhp(), xw.thp(), 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. - xw.currentControls()[w] = ctrl_index; - current = xw.currentControls()[w]; - well_controls_set_current( wc, current); - } - - // update whether well is under group control - if (wellCollection()->groupControlActive()) { - // get well node in the well collection - WellNode& well_node = well_collection_->findWellNode(std::string(wells().name[w])); - - // update whehter the well is under group control or individual control - if (well_node.groupControlIndex() >= 0 && current == well_node.groupControlIndex()) { - // under group control - well_node.setIndividualControl(false); - } else { - // individual control - well_node.setIndividualControl(true); - } - } - } - - // the new well control indices after all the related updates, - std::vector updated_control_index(nw, 0); - for (int w = 0; w < nw; ++w) { - updated_control_index[w] = xw.currentControls()[w]; - } - - // checking whether control changed - wellhelpers::WellSwitchingLogger logger; - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - if (updated_control_index[w] != old_control_index[w]) { - logger.wellSwitched(wells().name[w], - well_controls_iget_type(wc, old_control_index[w]), - well_controls_iget_type(wc, updated_control_index[w])); - } - - if (updated_control_index[w] != old_control_index[w] || well_collection_->groupControlActive()) { - updateWellStateWithTarget(wc, updated_control_index[w], w, xw); - } + for (const auto& well : well_container_) { + well->updateWellControl(xw); } + // TODO: group control has to be applied in the level of the all wells // upate the well targets following group controls + // The following probably can go to a function // it will not change the control mode, only update the targets if (wellCollection()->groupControlActive()) { applyVREPGroupControl(xw); wellCollection()->updateWellTargets(xw.wellRates()); - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - updateWellStateWithTarget(wc, updated_control_index[w], w, xw); + for (int w = 0; w < number_of_wells_; ++w) { + // TODO: check whether we need current argument in updateWellStateWithTarget + // maybe there is some circumstances that the current is different from the one + // in the WellState. + // while probalby, the current argument can be removed + const int current = xw.currentControls()[w]; + well_container_[w]->updateWellStateWithTarget(current, xw); } } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 2ddb24f52..29b141348 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -155,6 +155,11 @@ namespace Opm WellState& well_state, bool only_wells) = 0; + virtual void updateWellStateWithTarget(const int current, + WellState& xw) const = 0; + + virtual void updateWellControl(WellState& xw) const = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From 229244142fbccf30ef196e9e1314646d39346963 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 28 Jun 2017 13:46:01 +0200 Subject: [PATCH 027/104] adding updateGroupControls() to StandardWellsDense which separates the group control related operations. The group control has to be handled in the level of all wells, which makes more functions of the well class to be visible outside. --- opm/autodiff/StandardWell_impl.hpp | 6 --- opm/autodiff/StandardWellsDense.hpp | 2 + opm/autodiff/StandardWellsDense_impl.hpp | 69 +++++++++++------------- 3 files changed, 34 insertions(+), 43 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 900c8a556..ed453fe27 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1825,12 +1825,6 @@ namespace Opm invDuneD_.mv(resWell_, dx_well); updateWellState(dx_well, param, well_state); - - // updateWellControls uses communication - // Therefore the following is executed if there - // are active wells anywhere in the global domain. - updateWellControl(well_state); - setWellVariables(well_state); } } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 69cc069fa..e42d0ff2c 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -294,6 +294,8 @@ enum WellVariablePositions { void applyVREPGroupControl(WellState& well_state) const; + void updateGroupControls(WellState& well_state) const; + protected: bool wells_active_; const Wells* wells_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 363392e77..913ca8452 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -230,6 +230,7 @@ namespace Opm { } updateWellControls(well_state); + updateGroupControls(well_state); // Set the primary variables for the wells setWellVariables(well_state); @@ -739,22 +740,6 @@ namespace Opm { StandardWellsDense:: setWellVariables(const WellState& xw) { - const int nw = wells().number_of_wells; - // for two-phase numComp < numWellEq - const int numComp = numComponents(); - for (int eqIdx = 0; eqIdx < numComp; ++eqIdx) { - for (int w = 0; w < nw; ++w) { - const unsigned int idx = nw * eqIdx + w; - assert( idx < wellVariables_.size() ); - assert( idx < xw.wellSolutions().size() ); - EvalWell& eval = wellVariables_[ idx ]; - - eval = 0.0; - eval.setValue( xw.wellSolutions()[ idx ] ); - eval.setDerivative(numEq + eqIdx, 1.0); - } - } - for (auto& well_interface : well_container_) { well_interface->setWellVariables(xw); } @@ -950,10 +935,9 @@ namespace Opm { ++it; if( localWellsActive() ) { - BVector dx_well (nw); - invDuneD_.mv(resWell_, dx_well); - - updateWellState(dx_well, well_state); + for (auto& well : well_container_) { + well->wellEqIteration(ebosSimulator, param_, well_state); + } } // updateWellControls uses communication // Therefore the following is executed if there @@ -961,6 +945,7 @@ namespace Opm { if( wellsActive() ) { updateWellControls(well_state); + updateGroupControls(well_state); setWellVariables(well_state); } } while (it < 15); @@ -1246,23 +1231,6 @@ namespace Opm { for (const auto& well : well_container_) { well->updateWellControl(xw); } - - // TODO: group control has to be applied in the level of the all wells - // upate the well targets following group controls - // The following probably can go to a function - // it will not change the control mode, only update the targets - if (wellCollection()->groupControlActive()) { - applyVREPGroupControl(xw); - wellCollection()->updateWellTargets(xw.wellRates()); - for (int w = 0; w < number_of_wells_; ++w) { - // TODO: check whether we need current argument in updateWellStateWithTarget - // maybe there is some circumstances that the current is different from the one - // in the WellState. - // while probalby, the current argument can be removed - const int current = xw.currentControls()[w]; - well_container_[w]->updateWellStateWithTarget(current, xw); - } - } } @@ -1703,6 +1671,33 @@ namespace Opm { + template + void + StandardWellsDense:: + updateGroupControls(WellState& well_state) const + { + if (wellCollection()->groupControlActive()) { + applyVREPGroupControl(well_state); + wellCollection()->updateWellTargets(well_state.wellRates()); + + // TODO: group control has to be applied in the level of the all wells + // upate the well targets following group controls + // it will not change the control mode, only update the targets + for (int w = 0; w < number_of_wells_; ++w) { + // TODO: check whether we need current argument in updateWellStateWithTarget + // maybe there is some circumstances that the current is different from the one + // in the WellState. + // while probalby, the current argument can be removed + const int current = well_state.currentControls()[w]; + well_container_[w]->updateWellStateWithTarget(current, well_state); + } + } + } + + + + + template typename StandardWellsDense::EvalWell StandardWellsDense:: From 9c215faade4d5aa64b4dc78fa8b393f385880cf5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 28 Jun 2017 14:22:16 +0200 Subject: [PATCH 028/104] removing function allow_cross_flow() from StandardWellsDense --- opm/autodiff/StandardWell.hpp | 4 +-- opm/autodiff/StandardWell_impl.hpp | 2 +- opm/autodiff/StandardWellsDense.hpp | 2 -- opm/autodiff/StandardWellsDense_impl.hpp | 40 ++---------------------- opm/autodiff/WellInterface.hpp | 4 +++ 5 files changed, 9 insertions(+), 43 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 981344c0b..1aeb36344 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -107,7 +107,7 @@ namespace Opm WellState& well_state, bool only_wells); - bool crossFlowAllowed(const Simulator& ebosSimulator) const; + virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const; void getMobility(const Simulator& ebosSimulator, const int perf, @@ -145,7 +145,6 @@ namespace Opm using WellInterface::indexOfWell; using WellInterface::name; using WellInterface::wellType; - using WellInterface::allowCrossFlow; using WellInterface::wellControls; using WellInterface::compFrac; using WellInterface::numberOfPhases; @@ -172,6 +171,7 @@ namespace Opm using WellInterface::first_perf_; using WellInterface::ref_depth_; using WellInterface::perf_depth_; + using WellInterface::allow_cf_; // densities of the fluid in each perforation std::vector perf_densities_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index ed453fe27..9ed6fce5a 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -723,7 +723,7 @@ namespace Opm StandardWell:: crossFlowAllowed(const Simulator& ebosSimulator) const { - if (allowCrossFlow()) { + if (allow_cf_) { return true; } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index e42d0ff2c..f55805fb1 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -162,8 +162,6 @@ enum WellVariablePositions { const int cell_idx, std::vector& mob) const; - bool allow_cross_flow(const int w, const Simulator& ebosSimulator) const; - void localInvert(Mat& istlA) const; void print(Mat& istlA) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 913ca8452..fae2427ab 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -328,7 +328,7 @@ namespace Opm { if (PolymerModule::hasPlyshlog()) { // compute the well water velocity with out shear effects. const int numComp = numComponents(); - bool allow_cf = allow_cross_flow(w, ebosSimulator); + const bool allow_cf = well_container_[w]->crossFlowAllowed(ebosSimulator); const EvalWell& bhp = getBhp(w); std::vector cq_s(numComp,0.0); computeWellFlux(w, wells().WI[perf], intQuants, mob, bhp, wellPerforationPressureDiffs()[perf], allow_cf, cq_s); @@ -364,42 +364,6 @@ namespace Opm { - template - bool - StandardWellsDense:: - allow_cross_flow(const int w, const Simulator& ebosSimulator) const - { - if (wells().allow_cf[w]) { - return true; - } - - // check for special case where all perforations have cross flow - // then the wells must allow for cross flow - for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) { - const int cell_idx = wells().well_cells[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); - const auto& fs = intQuants.fluidState(); - EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); - EvalWell bhp = getBhp(w); - - // Pressure drawdown (also used to determine direction of flow) - EvalWell well_pressure = bhp + wellPerforationPressureDiffs()[perf]; - EvalWell drawdown = pressure - well_pressure; - - if (drawdown.value() < 0 && wells().type[w] == INJECTOR) { - return false; - } - - if (drawdown.value() > 0 && wells().type[w] == PRODUCER) { - return false; - } - } - return true; - } - - - - template void @@ -2254,7 +2218,7 @@ namespace Opm { const int numComp = numComponents(); well_flux.resize(np, 0.0); - const bool allow_cf = allow_cross_flow(well_index, ebosSimulator); + const bool allow_cf = well_container_[well_index]->crossFlowAllowed(ebosSimulator); for (int perf = wells().well_connpos[well_index]; perf < wells().well_connpos[well_index + 1]; ++perf) { const int cell_index = wells().well_cells[perf]; const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_index, /*timeIdx=*/ 0)); diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 29b141348..6feb3d959 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -135,8 +135,12 @@ namespace Opm int numComponents() const; + // simply returning allow_cf_ + // TODO: to check whether needed, it causes name problem with the crossFlowAllowed bool allowCrossFlow() const; + virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const = 0; + // TODO: for this kind of function, maybe can make a function with parameter perf const std::vector& saturationTableNumber() const; From 4e4e38310769d2b3e6440bc054ea762618aba112 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 28 Jun 2017 15:02:34 +0200 Subject: [PATCH 029/104] slightly changing createWellContainer() in StandardWellsDense no functional change. --- opm/autodiff/StandardWellsDense.hpp | 5 +--- opm/autodiff/StandardWellsDense_impl.hpp | 29 ++++++++++-------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index f55805fb1..bd499b690 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -312,10 +312,7 @@ enum WellVariablePositions { std::vector > > well_container_; // TODO: forgot why returning a vector here - std::vector > > - createWellContainer(const std::vector& wells_ecl, - const Wells* wells_arg, - const int time_step); + void createWellContainer(const Wells* wells_arg); // Well collection is used to enforce the group control WellCollection* well_collection_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index fae2427ab..f80a19c30 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -16,7 +16,6 @@ namespace Opm { , wells_(wells_arg) , wells_ecl_(wells_ecl) , number_of_wells_(wells_arg ? (wells_arg->number_of_wells) : 0) - , well_container_(createWellContainer(wells_ecl, wells_arg, current_timeIdx) ) , well_collection_(well_collection) , param_(param) , terminal_output_(terminal_output) @@ -30,6 +29,7 @@ namespace Opm { , wellVariables_( wells_ ? (wells_arg->number_of_wells * numWellEq) : 0) , F0_(wells_ ? (wells_arg->number_of_wells * numWellEq) : 0 ) { + createWellContainer(wells_arg); if( wells_ ) { invDuneD_.setBuildMode( Mat::row_wise ); @@ -156,19 +156,16 @@ namespace Opm { template - std::vector > > + void StandardWellsDense:: - createWellContainer(const std::vector& wells_ecl, - const Wells* wells_arg, - const int time_step) + createWellContainer(const Wells* wells_arg) { - std::vector > > wells_container; - + well_container_.clear(); // There might be no wells in the process if (localWellsActive()) { - const int nw = wells_arg->number_of_wells; + const int nw = number_of_wells_; - wells_container.reserve(nw); + well_container_.reserve(nw); // With the following way, it will have the same order with wells struct // Hopefully, it can generate the same residual history with master branch @@ -176,10 +173,10 @@ namespace Opm { const std::string well_name = std::string(wells_arg->name[w]); // finding the location of the well in wells_ecl - const int nw_wells_ecl = wells_ecl.size(); + const int nw_wells_ecl = wells_ecl_.size(); int index_well = 0; for (; index_well < nw_wells_ecl; ++index_well) { - if (well_name == wells_ecl[index_well]->name()) { + if (well_name == wells_ecl_[index_well]->name()) { break; } } @@ -189,22 +186,20 @@ namespace Opm { OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl "); } - const Well* well_ecl = wells_ecl[index_well]; - if (well_ecl->getStatus(time_step) == WellCommon::SHUT) { + const Well* well_ecl = wells_ecl_[index_well]; + if (well_ecl->getStatus(current_timeIdx_) == WellCommon::SHUT) { continue; } - if (well_ecl->isMultiSegment(time_step)) { + if (well_ecl->isMultiSegment(current_timeIdx_)) { OPM_THROW(Opm::NumericalProblem, "Not handling Multisegment Wells for now"); } // Basically, we are handling all the wells as StandardWell for the moment // TODO: to be changed when we begin introducing MultisegmentWell - wells_container.push_back(std::make_shared >(well_ecl, time_step, wells_arg) ); + well_container_.push_back(std::make_shared >(well_ecl, current_timeIdx_, wells_arg) ); } } - - return wells_container; } From 033fe706202309b6eff78fb5ba3b34bb392c3aff Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 28 Jun 2017 15:20:25 +0200 Subject: [PATCH 030/104] making init() function for WellInterface virtual. --- opm/autodiff/StandardWell.hpp | 10 +++++----- opm/autodiff/StandardWell_impl.hpp | 5 +++-- opm/autodiff/WellInterface.hpp | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 1aeb36344..456311ff7 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -114,11 +114,11 @@ namespace Opm std::vector& mob) const; // TODO: the parameters need to be optimized/adjusted - void init(const PhaseUsage* phase_usage_arg, - const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, - const double gravity_arg, - const int num_cells); + virtual void init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg, + const int num_cells); // Update the well_state based on solution void updateWellState(const BVector& dwells, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 9ed6fce5a..a2bf07240 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -47,8 +47,9 @@ namespace Opm const double gravity_arg, const int num_cells) { - WellInterface(phase_usage_arg, active_arg, - vfp_properties_arg, gravity_arg, num_cells); + WellInterface::init(phase_usage_arg, active_arg, + vfp_properties_arg, gravity_arg, num_cells); + // setup sparsity pattern for the matrices // TODO: C and B are opposite compared with the notations used in the paper. diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 6feb3d959..bfa829791 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -112,11 +112,11 @@ namespace Opm virtual std::vector& perfPressureDiffs() = 0; // TODO: the parameters need to be optimized/adjusted - void init(const PhaseUsage* phase_usage_arg, - const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, - const double gravity_arg, - const int num_cells); + virtual void init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const double gravity_arg, + const int num_cells); // TODO: temporary virtual void setWellVariables(const WellState& well_state) = 0; From 3ceea766166f72e92b8d735eae9c4f608e1d94e9 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 29 Jun 2017 13:52:31 +0200 Subject: [PATCH 031/104] adding function computeAccumWell and computeWellConnectionPressures to StandardWell and removing a few not needed function from StandardWellsDense --- opm/autodiff/StandardWell.hpp | 8 ++ opm/autodiff/StandardWell_impl.hpp | 38 ++++- opm/autodiff/StandardWellsDense.hpp | 16 --- opm/autodiff/StandardWellsDense_impl.hpp | 168 +---------------------- opm/autodiff/WellInterface.hpp | 7 + 5 files changed, 59 insertions(+), 178 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 456311ff7..c6233be01 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -40,6 +40,9 @@ namespace Opm { public: + // TODO: some functions working with AD variables handles only with values (double) without + // dealing with derivatives. It can be beneficial to make functions can work with either AD or scalar value. + // And also, it can also be beneficial to make these functions hanle different types of AD variables. // TODO: several functions related to polymer and PLYSHLOG are not incorprated yet, // like the function wpolymer, setupCompressedToCartesian, computeRepRadiusPerfLength, // They are introduced though PR 1220 and will be included later. @@ -137,6 +140,11 @@ namespace Opm const std::vector& B_avg, const ModelParameters& param) const; + virtual void computeAccumWell(); + + virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw); + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a2bf07240..0f5b711ef 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -28,6 +28,7 @@ namespace Opm , perf_densities_(numberOfPerforations()) , perf_pressure_diffs_(numberOfPerforations()) , well_variables_(numWellEq) // the number of the primary variables + , F0_(numWellEq) { duneB_.setBuildMode( Mat::row_wise ); duneC_.setBuildMode( Mat::row_wise ); @@ -1468,7 +1469,7 @@ namespace Opm // TODO: can make this a member? const int nw = xw.bhp().size(); const int numComp = numComponents(); - const PhaseUsage& pu = phase_usage_; + const PhaseUsage& pu = *phase_usage_; b_perf.resize(nperf*numComp); surf_dens_perf.resize(nperf*numComp); const int w = indexOfWell(); @@ -1813,6 +1814,27 @@ namespace Opm + template + void + StandardWell:: + computeWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& well_state) + { + // 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. + std::vector b_perf; + std::vector rsmax_perf; + std::vector rvmax_perf; + std::vector surf_dens_perf; + computePropertiesForWellConnectionPressures(ebosSimulator, well_state, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf); + computeWellConnectionDensitesPressures(well_state, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf); + } + + + + + template void StandardWell:: @@ -1828,4 +1850,18 @@ namespace Opm updateWellState(dx_well, param, well_state); } + + + + + template + void + StandardWell:: + computeAccumWell() + { + for (int eq_idx = 0; eq_idx < numWellEq; ++eq_idx) { + F0_[eq_idx] = wellSurfaceVolumeFraction(eq_idx).value(); + } + } + } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index bd499b690..94555f1e9 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -238,13 +238,6 @@ enum WellVariablePositions { void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw); - void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, - const WellState& xw, - std::vector& b_perf, - std::vector& rsmax_perf, - std::vector& rvmax_perf, - std::vector& surf_dens_perf) const; - void updateWellState(const BVector& dwells, WellState& well_state) const; @@ -259,15 +252,6 @@ enum WellVariablePositions { const WellState& well_state, DynamicListEconLimited& list_econ_limited) const; - void computeWellConnectionDensitesPressures(const WellState& xw, - const std::vector& b_perf, - const std::vector& rsmax_perf, - const std::vector& rvmax_perf, - const std::vector& surf_dens_perf, - const std::vector& depth_perf, - const double grav); - - // Calculating well potentials for each well // TODO: getBhp() will be refactored to reduce the duplication of the code calculating the bhp from THP. void computeWellPotentials(const Simulator& ebosSimulator, diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index f80a19c30..1eb7d4d7e 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -237,6 +237,8 @@ namespace Opm { if (param_.solve_welleq_initially_ && iterationIdx == 0) { // solve the well equations as a pre-processing step report = solveWellEq(ebosSimulator, dt, well_state); + std::cout << " solvWellEq is done ! " << std::endl; + std::cin.ignore(); } assembleWellEq(ebosSimulator, dt, well_state, false); @@ -728,11 +730,8 @@ namespace Opm { StandardWellsDense:: computeAccumWells() { - const int nw = wells().number_of_wells; - for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { - for (int w = 0; w < nw; ++w) { - F0_[w + nw * eqIdx] = wellSurfaceVolumeFraction(w, eqIdx).value(); - } + for (auto& well : well_container_) { + well->computeAccumWell(); } } @@ -1034,123 +1033,9 @@ namespace Opm { { if( ! localWellsActive() ) return ; - // 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. - std::vector b_perf; - std::vector rsmax_perf; - std::vector rvmax_perf; - std::vector surf_dens_perf; - computePropertiesForWellConnectionPressures(ebosSimulator, xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf); - computeWellConnectionDensitesPressures(xw, b_perf, rsmax_perf, rvmax_perf, surf_dens_perf, cell_depths_, gravity_); - } - - - - - - template - void - StandardWellsDense:: - computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, - const WellState& xw, - std::vector& b_perf, - std::vector& rsmax_perf, - std::vector& rvmax_perf, - std::vector& surf_dens_perf) const - { - const int nperf = wells().well_connpos[wells().number_of_wells]; - const int nw = wells().number_of_wells; - const int numComp = numComponents(); - const PhaseUsage& pu = phase_usage_; - b_perf.resize(nperf*numComp); - surf_dens_perf.resize(nperf*numComp); - - //rs and rv are only used if both oil and gas is present - if (pu.phase_used[BlackoilPhases::Vapour] && pu.phase_pos[BlackoilPhases::Liquid]) { - rsmax_perf.resize(nperf); - rvmax_perf.resize(nperf); - } - - // Compute the average pressure in each well block - for (int w = 0; w < nw; ++w) { - for (int perf = wells().well_connpos[w]; perf < wells().well_connpos[w+1]; ++perf) { - - const int cell_idx = wells().well_cells[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); - const auto& fs = intQuants.fluidState(); - - const double p_above = perf == wells().well_connpos[w] ? xw.bhp()[w] : xw.perfPress()[perf - 1]; - const double p_avg = (xw.perfPress()[perf] + p_above)/2; - const double temperature = fs.temperature(FluidSystem::oilPhaseIdx).value(); - - if (pu.phase_used[BlackoilPhases::Aqua]) { - b_perf[ pu.phase_pos[BlackoilPhases::Aqua] + perf * numComp] = - FluidSystem::waterPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); - } - - if (pu.phase_used[BlackoilPhases::Vapour]) { - const int gaspos = pu.phase_pos[BlackoilPhases::Vapour] + perf * numComp; - const int gaspos_well = pu.phase_pos[BlackoilPhases::Vapour] + w * pu.num_phases; - - if (pu.phase_used[BlackoilPhases::Liquid]) { - const int oilpos_well = pu.phase_pos[BlackoilPhases::Liquid] + w * pu.num_phases; - const double oilrate = std::abs(xw.wellRates()[oilpos_well]); //in order to handle negative rates in producers - rvmax_perf[perf] = FluidSystem::gasPvt().saturatedOilVaporizationFactor(fs.pvtRegionIndex(), temperature, p_avg); - if (oilrate > 0) { - const double gasrate = std::abs(xw.wellRates()[gaspos_well]) - xw.solventWellRate(w); - double rv = 0.0; - if (gasrate > 0) { - rv = oilrate / gasrate; - } - rv = std::min(rv, rvmax_perf[perf]); - - b_perf[gaspos] = FluidSystem::gasPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg, rv); - } - else { - b_perf[gaspos] = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); - } - - } else { - b_perf[gaspos] = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); - } - } - - if (pu.phase_used[BlackoilPhases::Liquid]) { - const int oilpos = pu.phase_pos[BlackoilPhases::Liquid] + perf * numComp; - const int oilpos_well = pu.phase_pos[BlackoilPhases::Liquid] + w * pu.num_phases; - if (pu.phase_used[BlackoilPhases::Vapour]) { - rsmax_perf[perf] = FluidSystem::oilPvt().saturatedGasDissolutionFactor(fs.pvtRegionIndex(), temperature, p_avg); - const int gaspos_well = pu.phase_pos[BlackoilPhases::Vapour] + w * pu.num_phases; - const double gasrate = std::abs(xw.wellRates()[gaspos_well]) - xw.solventWellRate(w); - if (gasrate > 0) { - const double oilrate = std::abs(xw.wellRates()[oilpos_well]); - double rs = 0.0; - if (oilrate > 0) { - rs = gasrate / oilrate; - } - rs = std::min(rs, rsmax_perf[perf]); - b_perf[oilpos] = FluidSystem::oilPvt().inverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg, rs); - } else { - b_perf[oilpos] = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); - } - } else { - b_perf[oilpos] = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(fs.pvtRegionIndex(), temperature, p_avg); - } - } - - // Surface density. - for (int p = 0; p < pu.num_phases; ++p) { - surf_dens_perf[numComp*perf + p] = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( p ), fs.pvtRegionIndex()); - } - - // We use cell values for solvent injector - if (has_solvent_) { - b_perf[numComp*perf + solventSaturationIdx] = intQuants.solventInverseFormationVolumeFactor().value(); - surf_dens_perf[numComp*perf + solventSaturationIdx] = intQuants.solventRefDensity(); - } - } - } + for (auto& well : well_container_) { + well->computeWellConnectionPressures(ebosSimulator, xw); + } } @@ -1304,45 +1189,6 @@ namespace Opm { - template - void - StandardWellsDense:: - computeWellConnectionDensitesPressures(const WellState& xw, - const std::vector& b_perf, - const std::vector& rsmax_perf, - const std::vector& rvmax_perf, - const std::vector& surf_dens_perf, - const std::vector& depth_perf, - const double grav) - { - // Compute densities - const int nperf = depth_perf.size(); - const int numComponent = b_perf.size() / nperf; - const int np = wells().number_of_phases; - std::vector perfRates(b_perf.size(),0.0); - for (int perf = 0; perf < nperf; ++perf) { - for (int phase = 0; phase < np; ++phase) { - perfRates[perf*numComponent + phase] = xw.perfPhaseRates()[perf*np + phase]; - } - if(has_solvent_) { - perfRates[perf*numComponent + solventSaturationIdx] = xw.perfRateSolvent()[perf]; - } - } - well_perforation_densities_ = - WellDensitySegmented::computeConnectionDensities( - wells(), phase_usage_, perfRates, - b_perf, rsmax_perf, rvmax_perf, surf_dens_perf); - - // Compute pressure deltas - well_perforation_pressure_diffs_ = - WellDensitySegmented::computeConnectionPressureDelta( - wells(), depth_perf, well_perforation_densities_, grav); - } - - - - - template void StandardWellsDense:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index bfa829791..75ede5b52 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -164,6 +164,13 @@ namespace Opm virtual void updateWellControl(WellState& xw) const = 0; + virtual void computeAccumWell() = 0; + + // TODO: it should come with a different name + // for MS well, the definition is different and should not use this name anymore + virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw) = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From eca28a8ade00252547c7e09bd78735c0d2be88ca Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 29 Jun 2017 14:31:17 +0200 Subject: [PATCH 032/104] adding a function outputWellState for StandardWellsDense to output the well state for debugging purpose. --- opm/autodiff/StandardWellsDense.hpp | 2 ++ opm/autodiff/StandardWellsDense_impl.hpp | 27 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 94555f1e9..b5f0f010d 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -425,6 +425,8 @@ enum WellVariablePositions { void computeAverageFormationFactor(Simulator& ebosSimulator, std::vector& B_avg) const; + + void outputWellState(const WellState& well_state) const; }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 1eb7d4d7e..7279a5617 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -2511,4 +2511,31 @@ namespace Opm { + template + void + StandardWellsDense:: + outputWellState(const WellState& well_state) const + { + std::cout << " output the bhp " << std::endl; + for (const double bhp : well_state.bhp()) { + std::cout << bhp << " "; + } + std::cout << std::endl; + + std::cout << " output the well rates " << std::endl; + for (const double rate : well_state.wellRates()) { + std::cout << rate << " "; + } + std::cout << std::endl; + + std::cout << " output the wellSolutions " << std::endl; + for (const double solution : well_state.wellSolutions()) { + std::cout << solution << " "; + } + std::cout << std::endl; + } + + + + } // namespace Opm From 4624ecc63da545bf4076a405d987eecc91955f12 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 18 Jul 2017 17:21:20 +0200 Subject: [PATCH 033/104] correcting the dFLimit and dBHPLimit in StandardWell_impl --- opm/autodiff/StandardWell_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 0f5b711ef..b47bb9641 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -823,8 +823,8 @@ namespace Opm // TODO: to check whether all the things from PR 1220 were incoporated. const int np = numberOfPhases(); const int nw = well_state.bhp().size(); - const double dFLimit = param.dbhp_max_rel_; - const double dBHPLimit = param.dwell_fraction_max_; + const double dBHPLimit = param.dbhp_max_rel_; + const double dFLimit = param.dwell_fraction_max_; std::vector xvar_well_old(numWellEq); // TODO: better way to handle this? From 1c2353fab8e637b6d638cb893cf7cde0e9a2dc8b Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 19 Jul 2017 11:51:19 +0200 Subject: [PATCH 034/104] making the ref_depth_ to be double in WellInterface which is a mistake. --- opm/autodiff/WellInterface.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 75ede5b52..c3aa65f8a 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -212,7 +212,7 @@ namespace Opm std::vector perf_depth_; // reference depth for the BHP - int ref_depth_; + double ref_depth_; double well_efficiency_factor_; From ddb21e1f2c2c9afea04d42ae450b683a69584236 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 19 Jul 2017 15:07:44 +0200 Subject: [PATCH 035/104] correcting the indexing of xvar_well_old in updateWellState in StandardWell_impl. --- opm/autodiff/StandardWell_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index b47bb9641..78a880a4b 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1023,8 +1023,8 @@ namespace Opm case RESERVOIR_RATE: { const int sign1 = dwells[0][XvarWell] > 0 ? 1: -1; - const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[nw*XvarWell + indexOfWell()])*dBHPLimit); - well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = std::max(xvar_well_old[nw*XvarWell + indexOfWell()] - dx1_limited,1e5); + const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[XvarWell])*dBHPLimit); + well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); well_state.bhp()[indexOfWell()] = well_state.wellSolutions()[nw*XvarWell + indexOfWell()]; if (well_controls_iget_type(wc, current) == SURFACE_RATE) { From 168205a9935f67f9a3d7a79c9d2b9737d384157d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 19 Jul 2017 16:10:50 +0200 Subject: [PATCH 036/104] checking magnitude of the residual for convergence in StandardWell_impl. --- opm/autodiff/StandardWell_impl.hpp | 3 ++- opm/autodiff/StandardWellsDense_impl.hpp | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 78a880a4b..9ac63f58f 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1722,7 +1722,8 @@ namespace Opm std::vector res(numComp); for (int comp = 0; comp < numWellEq; ++comp) { - res[comp] = resWell_[0][comp]; + // magnitude of the residual matters + res[comp] = std::abs(resWell_[0][comp]); } Vector well_flux_residual(numComp); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 7279a5617..025b72569 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -237,8 +237,6 @@ namespace Opm { if (param_.solve_welleq_initially_ && iterationIdx == 0) { // solve the well equations as a pre-processing step report = solveWellEq(ebosSimulator, dt, well_state); - std::cout << " solvWellEq is done ! " << std::endl; - std::cin.ignore(); } assembleWellEq(ebosSimulator, dt, well_state, false); @@ -989,8 +987,8 @@ namespace Opm { for (const auto& well : well_container_) { if ( !well->getWellConvergence(ebosSimulator, B_avg, param_) ) { converged_well = false; + // break; // TODO: no need to check other wells? } - break; } // TODO: to think about the output here. From b080874b89bf52cf658db7becdaa49a568234438 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 20 Jul 2017 11:09:34 +0200 Subject: [PATCH 037/104] adding the offset of perf for each well when accessing perforation rate and perforation pressure from the well_state. --- opm/autodiff/StandardWell_impl.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 9ac63f58f..b3b5624ac 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -667,9 +667,9 @@ namespace Opm // Store the perforation phase flux for later usage. if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) - well_state.perfRateSolvent()[perf] = cq_s[componentIdx].value(); + well_state.perfRateSolvent()[first_perf_ + perf] = cq_s[componentIdx].value(); } else { - well_state.perfPhaseRates()[perf*np + componentIdx] = cq_s[componentIdx].value(); + well_state.perfPhaseRates()[(first_perf_ + perf) * np + componentIdx] = cq_s[componentIdx].value(); } } @@ -691,7 +691,7 @@ namespace Opm } */ // Store the perforation pressure for later usage. - well_state.perfPress()[perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; + well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; } // add vol * dF/dt + Q to the well equations; @@ -1489,7 +1489,7 @@ namespace Opm // TODO: this is another place to show why WellState need to be a vector of WellState. // TODO: to check why should be perf - 1 const double p_above = perf == 0 ? xw.bhp()[w] : xw.perfPress()[first_perf_ + perf - 1]; - const double p_avg = (xw.perfPress()[perf] + p_above)/2; + const double p_avg = (xw.perfPress()[first_perf_ + perf] + p_above)/2; const double temperature = fs.temperature(FluidSystem::oilPhaseIdx).value(); if (pu.phase_used[BlackoilPhases::Aqua]) { @@ -1801,7 +1801,7 @@ namespace Opm perfRates[perf*numComponent + phase] = xw.perfPhaseRates()[(first_perf_ + perf) * np + phase]; } if(has_solvent) { - perfRates[perf*numComponent + contiSolventEqIdx] = xw.perfRateSolvent()[perf + first_perf_]; + perfRates[perf*numComponent + contiSolventEqIdx] = xw.perfRateSolvent()[first_perf_ + perf]; } } From 51226af445f49ecfbab3b23ee8c3d03d1d2465e1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 20 Jul 2017 14:23:29 +0200 Subject: [PATCH 038/104] refactoring solveJacobianSystem() not to handle xw. which is one step to avoid to access the type of xw, which is related to the implementation of the well model. --- opm/autodiff/BlackoilModelEbos.hpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 83e80cdc6..20879065a 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -284,10 +284,9 @@ namespace Opm { const int nc = AutoDiffGrid::numCells(grid_); const int nw = numWells(); BVector x(nc); - BVector xw(nw); try { - solveJacobianSystem(x, xw); + solveJacobianSystem(x); report.linear_solve_time += perfTimer.stop(); report.total_linear_iterations += linearIterationsLastSolve(); } @@ -488,7 +487,7 @@ namespace Opm { /// Solve the Jacobian system Jx = r where J is the Jacobian and /// r is the residual. - void solveJacobianSystem(BVector& x, BVector& xw) const + void solveJacobianSystem(BVector& x) const { const auto& ebosJac = ebosSimulator_.model().linearizer().matrix(); auto& ebosResid = ebosSimulator_.model().linearizer().residual(); @@ -510,13 +509,6 @@ namespace Opm { Operator opA(ebosJac, well_model_); istlSolver().solve( opA, x, ebosResid ); } - - if( xw.size() > 0 ) - { - // recover wells. - xw = 0.0; - wellModel().recoverVariable(x, xw); - } } //===================================================================== From e7a2e527635ad082908d2dcb27d57be6e351ab2f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 11:09:28 +0200 Subject: [PATCH 039/104] making matrix C and B same with the reference paper. Really no good point to make C and B different from the paper formulation except introducing more confusion. --- opm/autodiff/StandardWellsDense.hpp | 2 +- opm/autodiff/StandardWellsDense_impl.hpp | 36 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index b5f0f010d..9c62c10d7 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -343,7 +343,7 @@ enum WellVariablePositions { long int global_nc_; - mutable BVector Cx_; + mutable BVector Bx_; mutable BVector invDrw_; mutable BVector scaleAddRes_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 025b72569..bfe08ce89 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -33,8 +33,8 @@ namespace Opm { if( wells_ ) { invDuneD_.setBuildMode( Mat::row_wise ); - duneC_.setBuildMode( Mat::row_wise ); duneB_.setBuildMode( Mat::row_wise ); + duneC_.setBuildMode( Mat::row_wise ); } } @@ -69,8 +69,8 @@ namespace Opm { calculateEfficiencyFactors(); // setup sparsity pattern for the matrices - //[A B^T [x = [ res - // C D] x_well] res_well] + //[A C^T [x = [ res + // B D] x_well] res_well] const int nw = wells().number_of_wells; const int nperf = wells().well_connpos[nw]; @@ -88,18 +88,18 @@ namespace Opm { // set invDuneD invDuneD_.setSize( nw, nw, nw ); - // set duneC - duneC_.setSize( nw, nc, nperf ); - // set duneB duneB_.setSize( nw, nc, nperf ); + // set duneC + duneC_.setSize( nw, nc, nperf ); + for (auto row=invDuneD_.createbegin(), end = invDuneD_.createend(); row!=end; ++row) { // Add nonzeros for diagonal row.insert(row.index()); } - for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { + for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { // Add nonzeros for diagonal for (int perf = wells().well_connpos[row.index()] ; perf < wells().well_connpos[row.index()+1]; ++perf) { const int cell_idx = wells().well_cells[perf]; @@ -107,8 +107,8 @@ namespace Opm { } } - // make the B^T matrix - for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { + // make the C^T matrix + for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { for (int perf = wells().well_connpos[row.index()] ; perf < wells().well_connpos[row.index()+1]; ++perf) { const int cell_idx = wells().well_cells[perf]; row.insert(cell_idx); @@ -118,7 +118,7 @@ namespace Opm { resWell_.resize( nw ); // resize temporary class variables - Cx_.resize( duneC_.N() ); + Bx_.resize( duneB_.N() ); invDrw_.resize( invDuneD_.N() ); if (has_polymer_) @@ -405,7 +405,7 @@ namespace Opm { assert( invDrw_.size() == invDuneD_.N() ); invDuneD_.mv(resWell_,invDrw_); - duneB_.mmtv(invDrw_, r); + duneC_.mmtv(invDrw_, r); } @@ -421,14 +421,14 @@ namespace Opm { return; } - assert( Cx_.size() == duneC_.N() ); + assert( Bx_.size() == duneB_.N() ); - BVector& invDCx = invDrw_; - assert( invDCx.size() == invDuneD_.N()); + BVector& invDBx = invDrw_; + assert( invDBx.size() == invDuneD_.N()); - duneC_.mv(x, Cx_); - invDuneD_.mv(Cx_, invDCx); - duneB_.mmtv(invDCx,Ax); + duneB_.mv(x, Bx_); + invDuneD_.mv(Bx_, invDBx); + duneC_.mmtv(invDBx,Ax); } @@ -466,7 +466,7 @@ namespace Opm { return; } BVector resWell = resWell_; - duneC_.mmv(x, resWell); + duneB_.mmv(x, resWell); invDuneD_.mv(resWell, xw); } From f1123acdf02b19db372d8587196948c937bca950 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 11:59:28 +0200 Subject: [PATCH 040/104] also switching B and C in StandardWell_impl --- opm/autodiff/StandardWell.hpp | 2 +- opm/autodiff/StandardWell_impl.hpp | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index c6233be01..d17bee477 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -195,7 +195,7 @@ namespace Opm Mat invDuneD_; // several vector used in the matrix calculation - mutable BVector Cx_; + mutable BVector Bx_; mutable BVector invDrw_; mutable BVector scaleAddRes_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index b3b5624ac..ebe4f8607 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -53,20 +53,19 @@ namespace Opm // setup sparsity pattern for the matrices - // TODO: C and B are opposite compared with the notations used in the paper. - //[A B^T [x = [ res - // C D] x_well] res_well] + //[A C^T [x = [ res + // B D] x_well] res_well] // set the size of the matrices invDuneD_.setSize(1, 1, 1); - duneC_.setSize(1, num_cells, numberOfPerforations()); duneB_.setSize(1, num_cells, numberOfPerforations()); + duneC_.setSize(1, num_cells, numberOfPerforations()); for (auto row=invDuneD_.createbegin(), end = invDuneD_.createend(); row!=end; ++row) { // Add nonzeros for diagonal row.insert(row.index()); } - for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { + for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { // Add nonzeros for diagonal for (int perf = 0 ; perf < numberOfPerforations(); ++perf) { const int cell_idx = wellCells()[perf]; @@ -74,8 +73,8 @@ namespace Opm } } - // make the B^T matrix - for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { + // make the C^T matrix + for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { for (int perf = 0; perf < numberOfPerforations(); ++perf) { const int cell_idx = wellCells()[perf]; row.insert(cell_idx); @@ -85,7 +84,7 @@ namespace Opm resWell_.resize(1); // resize temporary class variables - Cx_.resize( duneC_.N() ); + Bx_.resize( duneB_.N() ); invDrw_.resize( invDuneD_.N() ); } @@ -646,7 +645,7 @@ namespace Opm for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { if (!only_wells) { // also need to consider the efficiency factor when manipulating the jacobians. - duneB_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix + duneC_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix } invDuneD_[0][0][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); } @@ -655,7 +654,7 @@ namespace Opm if (!only_wells) { // also need to consider the efficiency factor when manipulating the jacobians. ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); - duneC_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + duneB_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); } } From 07f563a1e1d48aeb3fa7bdb210474684d3347a4d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 14:21:17 +0200 Subject: [PATCH 041/104] implementing two apply() function in StandardWell_impl The two functions will be essentially the same even for different types of wells. Maybe later we should try to put them in WellInterface. --- opm/autodiff/StandardWell.hpp | 26 +++++++-------- opm/autodiff/StandardWell_impl.hpp | 40 +++++++++++++++++++++++ opm/autodiff/StandardWellsDense_impl.hpp | 41 ++++++++++++++++++++++-- opm/autodiff/WellInterface.hpp | 23 ++++++++++++- 4 files changed, 111 insertions(+), 19 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index d17bee477..5de51cd2c 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -25,13 +25,6 @@ #include -#include -#include -#include - -#include -#include - namespace Opm { @@ -64,20 +57,18 @@ namespace Opm SFrac = 3 }; + using WellInterface::numEq; + using typename WellInterface::VectorBlockType; + using typename WellInterface::MatrixBlockType; + using typename WellInterface::Mat; + using typename WellInterface::BVector; + using typename WellInterface::Eval; - typedef double Scalar; - // static const int numEq = BlackoilIndices::numEq; - static const int numEq = BlackoilIndices::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? 3:numEq; // //numEq; //number of wellEq is only for 3 for polymer static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; - typedef Dune::FieldVector VectorBlockType; - typedef Dune::FieldMatrix MatrixBlockType; - typedef Dune::BCRSMatrix Mat; - typedef Dune::BlockVector BVector; typedef DenseAd::Evaluation EvalWell; - typedef DenseAd::Evaluation Eval; StandardWell(const Well* well, const int time_step, const Wells* wells); @@ -145,6 +136,11 @@ namespace Opm virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw); + // Ax = Ax - C D^-1 B x + virtual void apply(const BVector& x, BVector& Ax) const; + // r = r - C D^-1 Rw + virtual void apply(BVector& r) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index ebe4f8607..274eaa1f3 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1864,4 +1864,44 @@ namespace Opm } } + + + + + template + void + StandardWell:: + apply(const BVector& x, BVector& Ax) const + { + assert( Bx_.size() == duneB_.N() ); + assert( invDrw_.size() == invDuneD_.N() ); + + // Bx_ = duneB_ * x + duneB_.mv(x, Bx_); + // invDBx = invDuneD_ * Bx_ + // TODO: with this, we modified the content of the invDrw_. + // Is it necessary to do this to save some memory? + BVector& invDBx = invDrw_; + invDuneD_.mv(Bx_, invDBx); + + // Ax = Ax - duneC_^T * invDBx + duneC_.mmtv(invDBx,Ax); + } + + + + + template + void + StandardWell:: + apply(BVector& r) const + { + assert( invDrw_.size() == invDuneD_.N() ); + + // invDrw_ = invDuneD_ * resWell_ + invDuneD_.mv(resWell_, invDrw_); + // r = r - duneC_^T * invDrw_ + duneC_.mmtv(invDrw_, r); + } + } diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index bfe08ce89..1577e6e08 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -377,6 +377,13 @@ namespace Opm { + // applying the well residual to reservoir residuals + // r = r - duneC_^T * invDuneD_ * resWell_ + // TODO: for this, we should calcuate the duneC_^T * invDuneD_ * resWell_ for each + // well, then sum them up and apply to r finally + // In a more general case, the number of the equations for reservoir and wells can be different, + // we need to think about the possible data types can be faced. + // we do not want to expose the some well related data type even inside the Well Model template void StandardWellsDense:: @@ -402,16 +409,23 @@ namespace Opm { return; } - assert( invDrw_.size() == invDuneD_.N() ); + for (auto& well : well_container_) { + well->apply(r); + } + /* assert( invDrw_.size() == invDuneD_.N() ); + + // invDrw_ = invDuneD_ * resWell_ invDuneD_.mv(resWell_,invDrw_); - duneC_.mmtv(invDrw_, r); + // r = r - duneC_^T * invDrw_ + duneC_.mmtv(invDrw_, r); */ } + // Ax = A x - C D^-1 B x template void StandardWellsDense:: @@ -421,20 +435,33 @@ namespace Opm { return; } - assert( Bx_.size() == duneB_.N() ); + for (auto& well : well_container_) { + well->apply(x, Ax); + } + + /* assert( Bx_.size() == duneB_.N() ); BVector& invDBx = invDrw_; assert( invDBx.size() == invDuneD_.N()); + // Bx_ = duneB_ * x duneB_.mv(x, Bx_); + // invDBx = invDuneD_ * Bx_ invDuneD_.mv(Bx_, invDBx); + // Ax = Ax - duneC_^T * invDBx duneC_.mmtv(invDBx,Ax); + */ } + // Ax = Ax - alpha * C D^-1 B x + // TODO: for the new Well Model, we will calcuate + // C D^-1 B for each well and sum it up + // while it can be implemented in the function apply() + // then this function does not need to change template void StandardWellsDense:: @@ -449,7 +476,9 @@ namespace Opm { } scaleAddRes_ = 0.0; + // scaleAddRes_ = - C D^-1 B x apply( x, scaleAddRes_ ); + // Ax = Ax + alpha * scaleAddRes_ Ax.axpy( alpha, scaleAddRes_ ); } @@ -457,6 +486,10 @@ namespace Opm { + // xw = D^-1(resWell - B * x) + // TODO: this function should be moved to StandardWell + // xw should not appear in StandardWellsDense to avoid + // the data type to hold different types of xw template void StandardWellsDense:: @@ -466,7 +499,9 @@ namespace Opm { return; } BVector resWell = resWell_; + // resWell = resWell - B * x duneB_.mmv(x, resWell); + // xw = D^-1 * resWell invDuneD_.mv(resWell, xw); } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index c3aa65f8a..6423ef519 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -39,6 +39,13 @@ #include +#include +#include +#include + +#include +#include + #include #include #include @@ -62,7 +69,15 @@ namespace Opm typedef typename GET_PROP_TYPE(TypeTag, IntensiveQuantities) IntensiveQuantities; typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; - static const int solventCompIdx = 3; //TODO get this from ebos + static const int numEq = BlackoilIndices::numEq; + typedef double Scalar; + + typedef Dune::FieldVector VectorBlockType; + typedef Dune::FieldMatrix MatrixBlockType; + typedef Dune::BCRSMatrix Mat; + typedef Dune::BlockVector BVector; + typedef DenseAd::Evaluation Eval; + static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); /// Constructor @@ -171,6 +186,12 @@ namespace Opm virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw) = 0; + // Ax = Ax - C D^-1 B x + virtual void apply(const BVector& x, BVector& Ax) const = 0; + + // r = r - C D^-1 Rw + virtual void apply(BVector& r) const = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From ab676351345a16cd447b0bad4860889321b395ab Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 15:30:34 +0200 Subject: [PATCH 042/104] adding applySolutionWellState to apply solution from reservoir to update well state. With this way, the BlackoilModelEbos does not need to know the data type assocated with different well type. It is not well tested yet. --- opm/autodiff/BlackoilModelEbos.hpp | 6 ++++- opm/autodiff/StandardWell.hpp | 8 ++++++ opm/autodiff/StandardWell_impl.hpp | 32 ++++++++++++++++++++++++ opm/autodiff/StandardWellsDense.hpp | 7 ++++-- opm/autodiff/StandardWellsDense_impl.hpp | 17 ++++--------- opm/autodiff/WellInterface.hpp | 5 ++++ 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 20879065a..4304635d3 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -321,7 +321,11 @@ namespace Opm { // Apply the update, with considering model-dependent limitations and // chopping of the update. updateState(x,iteration); - wellModel().updateWellState(xw, well_state); + + if( nw > 0 ) + { + wellModel().applySolutionWellState(x, well_state); + } report.update_time += perfTimer.stop(); } diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 5de51cd2c..356599af0 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -141,6 +141,11 @@ namespace Opm // r = r - C D^-1 Rw virtual void apply(BVector& r) const; + // using the solution x to recover the solution xw for wells and applying + // xw to update Well State + virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, + WellState& well_state) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -167,6 +172,9 @@ namespace Opm // TODO: maybe this function can go to some helper file. void localInvert(Mat& istlA) const; + // xw = inv(D)*(rw - C*x) + void recoverSolutionWell(const BVector& x, BVector& xw) const; + // TODO: decide wether to use member function to refer to private member later using WellInterface::vfp_properties_; using WellInterface::gravity_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 274eaa1f3..a9973877e 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1904,4 +1904,36 @@ namespace Opm duneC_.mmtv(invDrw_, r); } + + + + + template + void + StandardWell:: + recoverSolutionWell(const BVector& x, BVector& xw) const + { + BVector resWell = resWell_; + // resWell = resWell - B * x + duneB_.mmv(x, resWell); + // xw = D^-1 * resWell + invDuneD_.mv(resWell, xw); + } + + + + + + template + void + StandardWell:: + applySolutionWellState(const BVector& x, + const ModelParameters& param, + WellState& well_state) const + { + BVector xw(1); + recoverSolutionWell(x, xw); + updateWellState(xw, param, well_state); + } + } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 9c62c10d7..c81835c39 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -98,6 +98,8 @@ enum WellVariablePositions { static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; static const int polymerConcentrationIdx = BlackoilIndices::polymerConcentrationIdx; + // 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::FieldMatrix MatrixBlockType; typedef Dune::BCRSMatrix Mat; @@ -175,8 +177,9 @@ enum WellVariablePositions { // apply well model with scaling of alpha void applyScaleAdd(const Scalar alpha, const BVector& x, BVector& Ax) const; - // xw = inv(D)*(rw - C*x) - void recoverVariable(const BVector& x, BVector& xw) const; + // using the solution x to recover the solution xw for wells and applying + // xw to update Well State + void applySolutionWellState(const BVector& x, WellState& well_state) const; int flowPhaseToEbosCompIdx( const int phaseIdx ) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 1577e6e08..64e81ae0e 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -431,6 +431,7 @@ namespace Opm { StandardWellsDense:: apply(const BVector& x, BVector& Ax) const { + // TODO: do we still need localWellsActive()? if ( ! localWellsActive() ) { return; } @@ -486,28 +487,20 @@ namespace Opm { - // xw = D^-1(resWell - B * x) - // TODO: this function should be moved to StandardWell - // xw should not appear in StandardWellsDense to avoid - // the data type to hold different types of xw template void StandardWellsDense:: - recoverVariable(const BVector& x, BVector& xw) const + applySolutionWellState(const BVector& x, WellState& well_state) const { - if ( ! localWellsActive() ) { - return; + for (auto& well : well_container_) { + well->applySolutionWellState(x, param_, well_state); } - BVector resWell = resWell_; - // resWell = resWell - B * x - duneB_.mmv(x, resWell); - // xw = D^-1 * resWell - invDuneD_.mv(resWell, xw); } + template int StandardWellsDense:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 6423ef519..c9fb75e99 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -192,6 +192,11 @@ namespace Opm // r = r - C D^-1 Rw virtual void apply(BVector& r) const = 0; + // using the solution x to recover the solution xw for wells and applying + // xw to update Well State + virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, + WellState& well_state) const = 0; + protected: // TODO: some variables shared by all the wells should be made static // well name From f1677015fe567706842de646b0097acb73b1f068 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 16:01:32 +0200 Subject: [PATCH 043/104] using getWellConvergence in BlackoilModelEbos Tested with SPE1. TODO: with the current way, we are not outputting any well residual information. We need to address what kind of residual information we want to output with the new well model. --- opm/autodiff/BlackoilModelEbos.hpp | 22 ++++++++++------------ opm/autodiff/StandardWellsDense.hpp | 2 +- opm/autodiff/StandardWellsDense_impl.hpp | 17 +++++++---------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 4304635d3..691d4845d 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -909,7 +909,6 @@ namespace Opm { Vector CNV(numComp); Vector mass_balance_residual(numComp); - Vector well_flux_residual(numComp); bool converged_MB = true; bool converged_CNV = true; @@ -923,8 +922,7 @@ namespace Opm { converged_CNV = converged_CNV && (CNV[compIdx] < tol_cnv); // Well flux convergence is only for fluid phases, not other materials // in our current implementation. - well_flux_residual[compIdx] = B_avg[compIdx] * maxNormWell[compIdx]; - converged_Well = converged_Well && (well_flux_residual[compIdx] < tol_wells); + converged_Well = wellModel().getWellConvergence(ebosSimulator_, B_avg); residual_norms.push_back(CNV[compIdx]); } @@ -961,9 +959,9 @@ namespace Opm { for (int compIdx = 0; compIdx < numComp; ++compIdx) { msg += " CNV(" + key[ compIdx ] + ") "; } - for (int compIdx = 0; compIdx < numComp; ++compIdx) { + /* for (int compIdx = 0; compIdx < numComp; ++compIdx) { msg += " W-FLUX(" + key[ compIdx ] + ")"; - } + } */ OpmLog::note(msg); } std::ostringstream ss; @@ -976,9 +974,9 @@ namespace Opm { for (int compIdx = 0; compIdx < numComp; ++compIdx) { ss << std::setw(11) << CNV[compIdx]; } - for (int compIdx = 0; compIdx < numComp; ++compIdx) { - ss << std::setw(11) << well_flux_residual[compIdx]; - } + // for (int compIdx = 0; compIdx < numComp; ++compIdx) { + // ss << std::setw(11) << well_flux_residual[compIdx]; + // } ss.precision(oprec); ss.flags(oflags); OpmLog::note(ss.str()); @@ -988,13 +986,13 @@ namespace Opm { const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); if (std::isnan(mass_balance_residual[phaseIdx]) - || std::isnan(CNV[phaseIdx]) - || (phaseIdx < numPhases() && std::isnan(well_flux_residual[phaseIdx]))) { + || std::isnan(CNV[phaseIdx])) { + // || (phaseIdx < numPhases() && std::isnan(well_flux_residual[phaseIdx]))) { OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName); } if (mass_balance_residual[phaseIdx] > maxResidualAllowed() - || CNV[phaseIdx] > maxResidualAllowed() - || (phaseIdx < numPhases() && well_flux_residual[phaseIdx] > maxResidualAllowed())) { + || CNV[phaseIdx] > maxResidualAllowed()) { + // || (phaseIdx < numPhases() && well_flux_residual[phaseIdx] > maxResidualAllowed())) { OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName); } } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index c81835c39..25688ee94 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -236,7 +236,7 @@ enum WellVariablePositions { std::vector residual() const; bool getWellConvergence(Simulator& ebosSimulator, - const int iteration) const; + const std::vector& B_avg) const; void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 64e81ae0e..afd013307 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -901,11 +901,16 @@ namespace Opm { const int nw = wells().number_of_wells; WellState well_state0 = well_state; + const int numComp = numComponents(); + std::vector< Scalar > B_avg( numComp, Scalar() ); + computeAverageFormationFactor(ebosSimulator, B_avg); + int it = 0; bool converged; do { assembleWellEq(ebosSimulator, dt, well_state, true); - converged = getWellConvergence(ebosSimulator, it); + + converged = getWellConvergence(ebosSimulator, B_avg); // checking whether the group targets are converged if (wellCollection()->groupControlActive()) { @@ -998,16 +1003,8 @@ namespace Opm { bool StandardWellsDense:: getWellConvergence(Simulator& ebosSimulator, - const int iteration) const + const std::vector& B_avg) const { - typedef double Scalar; - typedef std::vector< Scalar > Vector; - - const int numComp = numComponents(); - - std::vector< Scalar > B_avg( numComp, Scalar() ); - computeAverageFormationFactor(ebosSimulator, B_avg); - bool converged_well = true; // TODO: to check the strategy here From e695a3c41835c491363942b24ac45e5b4559acdb Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 21 Jul 2017 16:16:20 +0200 Subject: [PATCH 044/104] cleaning up some unused stuff from StandardWellsDense --- opm/autodiff/StandardWellsDense.hpp | 9 ----- opm/autodiff/StandardWellsDense_impl.hpp | 44 ------------------------ 2 files changed, 53 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 25688ee94..a0880205a 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -338,21 +338,12 @@ enum WellVariablePositions { std::vector wellVariables_; std::vector F0_; - Mat duneB_; - Mat duneC_; - Mat invDuneD_; - BVector resWell_; long int global_nc_; - mutable BVector Bx_; - mutable BVector invDrw_; mutable BVector scaleAddRes_; - double dbhpMaxRel() const {return param_.dbhp_max_rel_; } - double dWellFractionMax() const {return param_.dwell_fraction_max_; } - // protected methods EvalWell getBhp(const int wellIdx) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index afd013307..4cafc84e7 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -30,12 +30,6 @@ namespace Opm { , F0_(wells_ ? (wells_arg->number_of_wells * numWellEq) : 0 ) { createWellContainer(wells_arg); - if( wells_ ) - { - invDuneD_.setBuildMode( Mat::row_wise ); - duneB_.setBuildMode( Mat::row_wise ); - duneC_.setBuildMode( Mat::row_wise ); - } } @@ -68,10 +62,6 @@ namespace Opm { calculateEfficiencyFactors(); - // setup sparsity pattern for the matrices - //[A C^T [x = [ res - // B D] x_well] res_well] - const int nw = wells().number_of_wells; const int nperf = wells().well_connpos[nw]; const int nc = numCells(); @@ -85,42 +75,8 @@ namespace Opm { assert (np == 3 || (np == 2 && !pu.phase_used[Gas]) ); #endif - // set invDuneD - invDuneD_.setSize( nw, nw, nw ); - - // set duneB - duneB_.setSize( nw, nc, nperf ); - - // set duneC - duneC_.setSize( nw, nc, nperf ); - - for (auto row=invDuneD_.createbegin(), end = invDuneD_.createend(); row!=end; ++row) { - // Add nonzeros for diagonal - row.insert(row.index()); - } - - for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { - // Add nonzeros for diagonal - for (int perf = wells().well_connpos[row.index()] ; perf < wells().well_connpos[row.index()+1]; ++perf) { - const int cell_idx = wells().well_cells[perf]; - row.insert(cell_idx); - } - } - - // make the C^T matrix - for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { - for (int perf = wells().well_connpos[row.index()] ; perf < wells().well_connpos[row.index()+1]; ++perf) { - const int cell_idx = wells().well_cells[perf]; - row.insert(cell_idx); - } - } - resWell_.resize( nw ); - // resize temporary class variables - Bx_.resize( duneB_.N() ); - invDrw_.resize( invDuneD_.N() ); - if (has_polymer_) { if (PolymerModule::hasPlyshlog()) { From dfc532a71308537e565cff998ce6304c19420bd5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 24 Jul 2017 10:46:12 +0200 Subject: [PATCH 045/104] cleaning up some more stuff unused in StandardWellsDense --- opm/autodiff/StandardWellsDense.hpp | 8 -- opm/autodiff/StandardWellsDense_impl.hpp | 129 ----------------------- 2 files changed, 137 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index a0880205a..84e0f0436 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -208,8 +208,6 @@ enum WellVariablePositions { /// return true if wells are available on this process bool localWellsActive() const; - int numWellVars() const; - /// Density of each well perforation const std::vector& wellPerforationDensities() const; @@ -241,11 +239,6 @@ enum WellVariablePositions { void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw); - void updateWellState(const BVector& dwells, - WellState& well_state) const; - - - void updateWellControls(WellState& xw) const; /// upate the dynamic lists related to economic limits @@ -336,7 +329,6 @@ enum WellVariablePositions { std::vector wells_bore_diameter_; std::vector wellVariables_; - std::vector F0_; BVector resWell_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 4cafc84e7..4961d9345 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -27,7 +27,6 @@ namespace Opm { , well_perforation_densities_( wells_ ? wells_arg->well_connpos[wells_arg->number_of_wells] : 0) , well_perforation_pressure_diffs_( wells_ ? wells_arg->well_connpos[wells_arg->number_of_wells] : 0) , wellVariables_( wells_ ? (wells_arg->number_of_wells * numWellEq) : 0) - , F0_(wells_ ? (wells_arg->number_of_wells * numWellEq) : 0 ) { createWellContainer(wells_arg); } @@ -84,8 +83,6 @@ namespace Opm { } } - // do the initialization work - // do the initialization for all the wells // TODO: to see whether we can postpone of the intialization of the well containers to // optimize the usage of the following several member variables @@ -226,90 +223,6 @@ namespace Opm { StandardWellsDense:: getMobility(const Simulator& ebosSimulator, const int w, const int perf, const int cell_idx, std::vector& mob) const { - - const int np = wells().number_of_phases; - assert (int(mob.size()) == numComponents()); - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); - const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); - - // either use mobility of the perforation cell or calcualte its own - // based on passing the saturation table index - const int satid = wells().sat_table_id[perf] - 1; - const int satid_elem = materialLawManager->satnumRegionIdx(cell_idx); - if( satid == satid_elem ) { // the same saturation number is used. i.e. just use the mobilty from the cell - - for (int phase = 0; phase < np; ++phase) { - int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); - mob[phase] = extendEval(intQuants.mobility(ebosPhaseIdx)); - } - if (has_solvent_) { - mob[solventSaturationIdx] = extendEval(intQuants.solventMobility()); - } - } else { - - const auto& paramsCell = materialLawManager->connectionMaterialLawParams(satid, cell_idx); - Eval relativePerms[3] = { 0.0, 0.0, 0.0 }; - MaterialLaw::relativePermeabilities(relativePerms, paramsCell, intQuants.fluidState()); - - // reset the satnumvalue back to original - materialLawManager->connectionMaterialLawParams(satid_elem, cell_idx); - - // compute the mobility - for (int phase = 0; phase < np; ++phase) { - int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); - mob[phase] = extendEval(relativePerms[ebosPhaseIdx] / intQuants.fluidState().viscosity(ebosPhaseIdx)); - } - - // this may not work if viscosity and relperms has been modified? - if (has_solvent_) { - OPM_THROW(std::runtime_error, "individual mobility for wells does not work in combination with solvent"); - } - } - - // modify the water mobility if polymer is present - if (has_polymer_) { - // assume fully mixture for wells. - EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); - - if (wells().type[w] == INJECTOR) { - const auto& viscosityMultiplier = PolymerModule::plyviscViscosityMultiplierTable(intQuants.pvtRegionIndex()); - mob[ Water ] /= (extendEval(intQuants.waterViscosityCorrection()) * viscosityMultiplier.eval(polymerConcentration, /*extrapolate=*/true) ); - } - - if (PolymerModule::hasPlyshlog()) { - // compute the well water velocity with out shear effects. - const int numComp = numComponents(); - const bool allow_cf = well_container_[w]->crossFlowAllowed(ebosSimulator); - const EvalWell& bhp = getBhp(w); - std::vector cq_s(numComp,0.0); - computeWellFlux(w, wells().WI[perf], intQuants, mob, bhp, wellPerforationPressureDiffs()[perf], allow_cf, cq_s); - double area = 2 * M_PI * wells_rep_radius_[perf] * wells_perf_length_[perf]; - const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); - const auto& scaledDrainageInfo = - materialLawManager->oilWaterScaledEpsInfoDrainage(cell_idx); - const Scalar& Swcr = scaledDrainageInfo.Swcr; - const EvalWell poro = extendEval(intQuants.porosity()); - const EvalWell Sw = extendEval(intQuants.fluidState().saturation(flowPhaseToEbosPhaseIdx(Water))); - // guard against zero porosity and no water - const EvalWell denom = Opm::max( (area * poro * (Sw - Swcr)), 1e-12); - EvalWell waterVelocity = cq_s[ Water ] / denom * extendEval(intQuants.fluidState().invB(flowPhaseToEbosPhaseIdx(Water))); - - if (PolymerModule::hasShrate()) { - // TODO Use the same conversion as for the reservoar equations. - // Need the "permeability" of the well? - // For now use the same formula as in legacy. - waterVelocity *= PolymerModule::shrate( intQuants.pvtRegionIndex() ) / wells_bore_diameter_[perf]; - } - EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); - EvalWell shearFactor = PolymerModule::computeShearFactor(polymerConcentration, - intQuants.pvtRegionIndex(), - waterVelocity); - - // modify the mobility with the shear factor and recompute the well fluxes. - mob[ Water ] /= shearFactor; - } - } - } @@ -321,12 +234,6 @@ namespace Opm { StandardWellsDense:: localInvert(Mat& istlA) const { - for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) { - for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) { - //std::cout << (*col) << std::endl; - (*col).invert(); - } - } } @@ -621,23 +528,6 @@ namespace Opm { - template - int - StandardWellsDense:: - numWellVars() const - { - if ( !localWellsActive() ) { - return 0; - } - - const int nw = wells().number_of_wells; - return numWellEq * nw; - } - - - - - template const std::vector& StandardWellsDense:: @@ -1021,25 +911,6 @@ namespace Opm { - template - void - StandardWellsDense:: - updateWellState(const BVector& dwells, - WellState& well_state) const - { - // TODO: the interface of the function should change. - // the current plan is to make different wells have different matrix - // and residual system. - if( !localWellsActive() ) return; - /* for (auto& well : well_container_) { - well->updateWellState(dwells, param_, well_state); - } */ - } - - - - - template void StandardWellsDense:: From c3cc4021fa40e4b0d361a42aee4d648f062cd47f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 24 Jul 2017 11:31:57 +0200 Subject: [PATCH 046/104] adding computeWellRatesWithBhp() to StandardWell --- opm/autodiff/StandardWell.hpp | 6 ++++++ opm/autodiff/StandardWell_impl.hpp | 32 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 356599af0..93c39b117 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -244,6 +244,12 @@ namespace Opm virtual void wellEqIteration(Simulator& ebosSimulator, const ModelParameters& param, WellState& well_state); + + // TODO: maybe we should provide a light version of computeWellFlux, which does not include the + // calculation of the derivatives + void computeWellRatesWithBhp(const Simulator& ebosSimulator, + const EvalWell& bhp, + std::vector& well_flux) const; }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a9973877e..27198db1e 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1936,4 +1936,36 @@ namespace Opm updateWellState(xw, param, well_state); } + + + + + template + void + StandardWell:: + computeWellRatesWithBhp(const Simulator& ebosSimulator, + const EvalWell& bhp, + std::vector& well_flux) const + { + const int np = numberOfPhases(); + const int numComp = numComponents(); + well_flux.resize(np, 0.0); + + const bool allow_cf = crossFlowAllowed(ebosSimulator); + + for (int perf = 0; perf < numberOfPerforations(); ++perf) { + const int cell_idx = wellCells()[perf]; + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/ 0)); + // flux for each perforation + std::vector cq_s(numComp, 0.0); + std::vector mob(numComp, 0.0); + getMobility(ebosSimulator, perf, mob); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + + for(int p = 0; p < np; ++p) { + well_flux[p] += cq_s[p].value(); + } + } + } + } From 1d34c9dc6e6d434e1fe8091078134db198faa721 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 24 Jul 2017 14:48:57 +0200 Subject: [PATCH 047/104] adding computeWellPotentialWithTHP() to StandardWell it is not clear at the moment how all the well potentials related will work with MS wells. For the moment, keep the well poentials related only in StandardWell. By theory, they should be very similar, while some dependence on certain member variables makes some small refactoring work necessary. --- opm/autodiff/StandardWell.hpp | 7 ++ opm/autodiff/StandardWell_impl.hpp | 125 ++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 10 ++- opm/autodiff/WellInterface_impl.hpp | 117 +++++++++++++++++++++++++- 4 files changed, 257 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 93c39b117..2f3478a7a 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -245,11 +245,18 @@ namespace Opm const ModelParameters& param, WellState& well_state); + using WellInterface::wellHasTHPConstraints; + using WellInterface::mostStrictBhpFromBhpLimits; + // TODO: maybe we should provide a light version of computeWellFlux, which does not include the // calculation of the derivatives void computeWellRatesWithBhp(const Simulator& ebosSimulator, const EvalWell& bhp, std::vector& well_flux) const; + + std::vector computeWellPotentialWithTHP(const Simulator& ebosSimulator, + const double initial_bhp, // bhp from BHP constraints + const std::vector& initial_potential) const; }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 27198db1e..5d6d0a8b7 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1968,4 +1968,129 @@ namespace Opm } } + + + + + template + std::vector + StandardWell:: + computeWellPotentialWithTHP(const Simulator& ebosSimulator, + const double initial_bhp, // bhp from BHP constraints + const std::vector& initial_potential) const + { + // TODO: pay attention to the situation that finally the potential is calculated based on the bhp control + // TODO: should we consider the bhp constraints during the iterative process? + const int np = numberOfPhases(); + + assert( np == int(initial_potential.size()) ); + + std::vector potentials = initial_potential; + std::vector old_potentials = potentials; // keeping track of the old potentials + + double bhp = initial_bhp; + double old_bhp = bhp; + + bool converged = false; + const int max_iteration = 1000; + const double bhp_tolerance = 1000.; // 1000 pascal + + int iteration = 0; + + while ( !converged && iteration < max_iteration ) { + // for each iteration, we calculate the bhp based on the rates/potentials with thp constraints + // with considering the bhp value from the bhp limits. At the beginning of each iteration, + // we initialize the bhp to be the bhp value from the bhp limits. Then based on the bhp values calculated + // from the thp constraints, we decide the effective bhp value for well potential calculation. + bhp = initial_bhp; + + // The number of the well controls/constraints + const int nwc = well_controls_get_num(wellControls()); + + for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { + if (well_controls_iget_type(wellControls(), ctrl_index) == THP) { + double aqua = 0.0; + double liquid = 0.0; + double vapour = 0.0; + + const Opm::PhaseUsage& pu = phase_usage_; + + if (active()[ Water ]) { + aqua = potentials[pu.phase_pos[ Water ] ]; + } + if (active()[ Oil ]) { + liquid = potentials[pu.phase_pos[ Oil ] ]; + } + if (active()[ Gas ]) { + vapour = potentials[pu.phase_pos[ Gas ] ]; + } + + const int vfp = well_controls_iget_vfp(wellControls(), ctrl_index); + const double thp = well_controls_iget_target(wellControls(), ctrl_index); + const double alq = well_controls_iget_alq(wellControls(), ctrl_index); + + // Calculating the BHP value based on THP + // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. + const double rho = 0.; // perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface + const double well_ref_depth = perf_depth_[0]; + if (wellType() == INJECTOR) { + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + const double bhp_calculated = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; + // apply the strictest of the bhp controlls i.e. smallest bhp for injectors + if (bhp_calculated < bhp) { + bhp = bhp_calculated; + } + } + else if (wellType() == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); + const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); + const double bhp_calculated = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; + // apply the strictest of the bhp controlls i.e. largest bhp for producers + if (bhp_calculated > bhp) { + bhp = bhp_calculated; + } + } else { + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + } + } + } + + // there should be always some available bhp/thp constraints there + if (std::isinf(bhp) || std::isnan(bhp)) { + OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name()); + } + + converged = std::abs(old_bhp - bhp) < bhp_tolerance; + + computeWellRatesWithBhp(ebosSimulator, bhp, potentials); + + // checking whether the potentials have valid values + for (const double value : potentials) { + if (std::isinf(value) || std::isnan(value)) { + OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name()); + } + } + + if (!converged) { + old_bhp = bhp; + for (int p = 0; p < np; ++p) { + // TODO: improve the interpolation, will it always be valid with the way below? + // TODO: finding better paramters, better iteration strategy for better convergence rate. + const double potential_update_damping_factor = 0.001; + potentials[p] = potential_update_damping_factor * potentials[p] + (1.0 - potential_update_damping_factor) * old_potentials[p]; + old_potentials[p] = potentials[p]; + } + } + + ++iteration; + } + + if (!converged) { + OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name()); + } + + return potentials; + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index c9fb75e99..b128eef7a 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -159,7 +159,7 @@ namespace Opm // TODO: for this kind of function, maybe can make a function with parameter perf const std::vector& saturationTableNumber() const; - const double wsolvent() const; + double wsolvent() const; virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, @@ -197,6 +197,10 @@ namespace Opm virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, WellState& well_state) const = 0; + void computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const; + protected: // TODO: some variables shared by all the wells should be made static // well name @@ -255,6 +259,10 @@ namespace Opm const VFPProperties* vfp_properties_; double gravity_; + + bool wellHasTHPConstraints() const; + + double mostStrictBhpFromBhpLimits() const; }; } diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index f04f5f2ec..784cf701e 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -376,7 +376,7 @@ namespace Opm template - const double + double WellInterface:: wsolvent() const { @@ -387,4 +387,119 @@ namespace Opm return 0.0; } + + + + + template + void + WellInterface:: + computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const + { + const int np = numberOfPhases(); + + well_potentials.resize(np, 0.0); + + // get the bhp value based on the bhp constraints + const double bhp = mostStrictBhpFromBhpLimits(); + + // does the well have a THP related constraint? + if ( !wellHasTHPConstraints() ) { + assert(std::abs(bhp) != std::numeric_limits::max()); + + computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); + } else { + // the well has a THP related constraint + // checking whether a well is newly added, it only happens at the beginning of the report step + if ( !well_state.isNewWell(index_of_well_) ) { + for (int p = 0; p < np; ++p) { + // This is dangerous for new added well + // since we are not handling the initialization correctly for now + well_potentials[p] = well_state.wellRates()[index_of_well_ * np + p]; + } + } else { + // We need to generate a reasonable rates to start the iteration process + computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); + for (double& value : well_potentials) { + // make the value a little safer in case the BHP limits are default ones + // TODO: a better way should be a better rescaling based on the investigation of the VFP table. + const double rate_safety_scaling_factor = 0.00001; + value *= rate_safety_scaling_factor; + } + } + + // potentials = computeWellPotentialWithTHP(ebosSimulator, w, bhp, potentials); + } + } + + + + + + template + double + WellInterface:: + mostStrictBhpFromBhpLimits() const + { + double bhp; + + // initial bhp value, making the value not usable + switch( well_type_ ) { + case INJECTOR: + bhp = std::numeric_limits::max(); + break; + case PRODUCER: + bhp = -std::numeric_limits::max(); + break; + default: + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name()); + } + + // The number of the well controls/constraints + const int nwc = well_controls_get_num(well_controls_); + + for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { + // finding a BHP constraint + if (well_controls_iget_type(well_controls_, ctrl_index) == BHP) { + // get the bhp constraint value, it should always be postive assummingly + const double bhp_target = well_controls_iget_target(well_controls_, ctrl_index); + + switch(well_type_) { + case INJECTOR: // using the lower bhp contraint from Injectors + if (bhp_target < bhp) { + bhp = bhp_target; + } + break; + case PRODUCER: + if (bhp_target > bhp) { + bhp = bhp_target; + } + break; + default: + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name()); + } // end of switch + } + } + + return bhp; + } + + + + + template + bool + WellInterface:: + wellHasTHPConstraints() const + { + const int nwc = well_controls_get_num(well_controls_); + for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { + if (well_controls_iget_type(well_controls_, ctrl_index) == THP) { + return true; + } + } + return false; + } } From e6d2b8550bf81ff0a743f2156c6c2dce95ba6827 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 24 Jul 2017 15:09:44 +0200 Subject: [PATCH 048/104] adding function computeWellPotentials to StandardWell for some similar reasons, this function stays in StandardWell for now. Eventually, it should go to WellInterface with some refactoring work. --- opm/autodiff/StandardWell.hpp | 4 +++ opm/autodiff/StandardWell_impl.hpp | 49 ++++++++++++++++++++++++++++- opm/autodiff/WellInterface.hpp | 6 ++-- opm/autodiff/WellInterface_impl.hpp | 47 --------------------------- 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 2f3478a7a..4806ac2a1 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -146,6 +146,10 @@ namespace Opm virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, WellState& well_state) const; + virtual void computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 5d6d0a8b7..c15c427d6 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -2013,7 +2013,7 @@ namespace Opm double liquid = 0.0; double vapour = 0.0; - const Opm::PhaseUsage& pu = phase_usage_; + const Opm::PhaseUsage& pu = *phase_usage_; if (active()[ Water ]) { aqua = potentials[pu.phase_pos[ Water ] ]; @@ -2093,4 +2093,51 @@ namespace Opm return potentials; } + + + + + template + void + StandardWell:: + computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const + { + const int np = numberOfPhases(); + + well_potentials.resize(np, 0.0); + + // get the bhp value based on the bhp constraints + const double bhp = mostStrictBhpFromBhpLimits(); + + // does the well have a THP related constraint? + if ( !wellHasTHPConstraints() ) { + assert(std::abs(bhp) != std::numeric_limits::max()); + + computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); + } else { + // the well has a THP related constraint + // checking whether a well is newly added, it only happens at the beginning of the report step + if ( !well_state.isNewWell(indexOfWell()) ) { + for (int p = 0; p < np; ++p) { + // This is dangerous for new added well + // since we are not handling the initialization correctly for now + well_potentials[p] = well_state.wellRates()[indexOfWell() * np + p]; + } + } else { + // We need to generate a reasonable rates to start the iteration process + computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); + for (double& value : well_potentials) { + // make the value a little safer in case the BHP limits are default ones + // TODO: a better way should be a better rescaling based on the investigation of the VFP table. + const double rate_safety_scaling_factor = 0.00001; + value *= rate_safety_scaling_factor; + } + } + + well_potentials = computeWellPotentialWithTHP(ebosSimulator, bhp, well_potentials); + } + } + } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index b128eef7a..d7827c706 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -197,9 +197,9 @@ namespace Opm virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, WellState& well_state) const = 0; - void computeWellPotentials(const Simulator& ebosSimulator, - const WellState& well_state, - std::vector& well_potentials) const; + virtual void computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const = 0; protected: // TODO: some variables shared by all the wells should be made static diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 784cf701e..c2801186d 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -391,53 +391,6 @@ namespace Opm - template - void - WellInterface:: - computeWellPotentials(const Simulator& ebosSimulator, - const WellState& well_state, - std::vector& well_potentials) const - { - const int np = numberOfPhases(); - - well_potentials.resize(np, 0.0); - - // get the bhp value based on the bhp constraints - const double bhp = mostStrictBhpFromBhpLimits(); - - // does the well have a THP related constraint? - if ( !wellHasTHPConstraints() ) { - assert(std::abs(bhp) != std::numeric_limits::max()); - - computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); - } else { - // the well has a THP related constraint - // checking whether a well is newly added, it only happens at the beginning of the report step - if ( !well_state.isNewWell(index_of_well_) ) { - for (int p = 0; p < np; ++p) { - // This is dangerous for new added well - // since we are not handling the initialization correctly for now - well_potentials[p] = well_state.wellRates()[index_of_well_ * np + p]; - } - } else { - // We need to generate a reasonable rates to start the iteration process - computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials); - for (double& value : well_potentials) { - // make the value a little safer in case the BHP limits are default ones - // TODO: a better way should be a better rescaling based on the investigation of the VFP table. - const double rate_safety_scaling_factor = 0.00001; - value *= rate_safety_scaling_factor; - } - } - - // potentials = computeWellPotentialWithTHP(ebosSimulator, w, bhp, potentials); - } - } - - - - - template double WellInterface:: From 8130b3279133bdc659e3a61b2d8198ee5cf8bfbd Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 10:15:27 +0200 Subject: [PATCH 049/104] cleaning up some functions from StandardWellsDense. --- opm/autodiff/StandardWellsDense.hpp | 44 +- opm/autodiff/StandardWellsDense_impl.hpp | 541 +---------------------- 2 files changed, 14 insertions(+), 571 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 84e0f0436..c78a75df9 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -157,13 +157,6 @@ enum WellVariablePositions { WellState& well_state, bool only_wells); - void - getMobility(const Simulator& ebosSimulator, - const int w, - const int perf, - const int cell_idx, - std::vector& mob) const; - void localInvert(Mat& istlA) const; void print(Mat& istlA) const; @@ -222,9 +215,6 @@ enum WellVariablePositions { void computeAccumWells(); - void computeWellFlux(const int& w, const double& Tw, const IntensiveQuantities& intQuants, const std::vector& mob_perfcells_dense, - const EvalWell& bhp, const double& cdp, const bool& allow_cf, std::vector& cq_s) const; - SimulatorReport solveWellEq(Simulator& ebosSimulator, const double dt, WellState& well_state); @@ -284,6 +274,8 @@ enum WellVariablePositions { // TODO: maybe a better name to emphasize it is local? const int number_of_wells_; + const int number_of_phases_; + // a vector of all the wells. // eventually, the wells_ above should be gone. // the name is just temporary @@ -330,24 +322,10 @@ enum WellVariablePositions { std::vector wellVariables_; - BVector resWell_; - long int global_nc_; mutable BVector scaleAddRes_; - // protected methods - EvalWell getBhp(const int wellIdx) const; - - EvalWell getQs(const int wellIdx, const int compIdx) const; - - EvalWell wellVolumeFraction(const int wellIdx, const int compIdx) const; - - EvalWell wellVolumeFractionScaled(const int wellIdx, const int compIdx) const; - - // Q_p / (Q_w + Q_g + Q_o) for three phase cases. - EvalWell wellSurfaceVolumeFraction(const int well_index, const int compIdx) const; - bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, const WellState& well_state, const int well_number) const; @@ -382,24 +360,6 @@ enum WellVariablePositions { const int well_index, WellState& xw) const; - bool wellHasTHPConstraints(const int well_index) const; - - // TODO: maybe we should provide a light version of computeWellFlux, which does not include the - // calculation of the derivatives - void computeWellRatesWithBhp(const Simulator& ebosSimulator, - const EvalWell& bhp, - const int well_index, - std::vector& well_flux) const; - - double mostStrictBhpFromBhpLimits(const int well_index) const; - - // TODO: maybe it should be improved to be calculate general rates for THP control later - std::vector - computeWellPotentialWithTHP(const Simulator& ebosSimulator, - const int well_index, - const double initial_bhp, // bhp from BHP constraints - const std::vector& initial_potential) const; - double wsolvent(const int well_index) const; double wpolymer(const int well_index) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 4961d9345..145e11fe9 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -16,6 +16,7 @@ namespace Opm { , wells_(wells_arg) , wells_ecl_(wells_ecl) , number_of_wells_(wells_arg ? (wells_arg->number_of_wells) : 0) + , number_of_phases_(wells_arg ? (wells_arg->number_of_phases) : 0) // TODO: not sure if it is proper for this way , well_collection_(well_collection) , param_(param) , terminal_output_(terminal_output) @@ -74,8 +75,6 @@ namespace Opm { assert (np == 3 || (np == 2 && !pu.phase_used[Gas]) ); #endif - resWell_.resize( nw ); - if (has_polymer_) { if (PolymerModule::hasPlyshlog()) { @@ -218,17 +217,6 @@ namespace Opm { - template - void - StandardWellsDense:: - getMobility(const Simulator& ebosSimulator, const int w, const int perf, const int cell_idx, std::vector& mob) const - { - } - - - - - template void StandardWellsDense:: @@ -573,8 +561,8 @@ namespace Opm { StandardWellsDense:: setWellVariables(const WellState& xw) { - for (auto& well_interface : well_container_) { - well_interface->setWellVariables(xw); + for (auto& well : well_container_) { + well->setWellVariables(xw); } } @@ -611,132 +599,6 @@ namespace Opm { - template - void - StandardWellsDense:: - computeWellFlux(const int& w, const double& Tw, - const IntensiveQuantities& intQuants, - const std::vector& mob_perfcells_dense, - const EvalWell& bhp, const double& cdp, - const bool& allow_cf, std::vector& cq_s) const - { - const Opm::PhaseUsage& pu = phase_usage_; - const int np = wells().number_of_phases; - const int numComp = numComponents(); - std::vector cmix_s(numComp,0.0); - for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - cmix_s[componentIdx] = wellSurfaceVolumeFraction(w, componentIdx); - } - auto& fs = intQuants.fluidState(); - - EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); - EvalWell rs = extendEval(fs.Rs()); - EvalWell rv = extendEval(fs.Rv()); - std::vector b_perfcells_dense(numComp, 0.0); - for (int phase = 0; phase < np; ++phase) { - int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(phase); - b_perfcells_dense[phase] = extendEval(fs.invB(ebosPhaseIdx)); - } - if (has_solvent_) { - b_perfcells_dense[solventSaturationIdx] = extendEval(intQuants.solventInverseFormationVolumeFactor()); - } - - // Pressure drawdown (also used to determine direction of flow) - EvalWell well_pressure = bhp + cdp; - EvalWell drawdown = pressure - well_pressure; - - // producing perforations - if ( drawdown.value() > 0 ) { - //Do nothing if crossflow is not allowed - if (!allow_cf && wells().type[w] == INJECTOR) { - return; - } - - // compute component volumetric rates at standard conditions - for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - const EvalWell cq_p = - Tw * (mob_perfcells_dense[componentIdx] * drawdown); - cq_s[componentIdx] = b_perfcells_dense[componentIdx] * cq_p; - } - - if (active_[Oil] && active_[Gas]) { - const int oilpos = pu.phase_pos[Oil]; - const int gaspos = pu.phase_pos[Gas]; - const EvalWell cq_sOil = cq_s[oilpos]; - const EvalWell cq_sGas = cq_s[gaspos]; - cq_s[gaspos] += rs * cq_sOil; - cq_s[oilpos] += rv * cq_sGas; - } - - } else { - //Do nothing if crossflow is not allowed - if (!allow_cf && wells().type[w] == PRODUCER) { - return; - } - - // Using total mobilities - EvalWell total_mob_dense = mob_perfcells_dense[0]; - for (int componentIdx = 1; componentIdx < numComp; ++componentIdx) { - total_mob_dense += mob_perfcells_dense[componentIdx]; - } - - // injection perforations total volume rates - const EvalWell cqt_i = - Tw * (total_mob_dense * drawdown); - - // compute volume ratio between connection at standard conditions - EvalWell volumeRatio = 0.0; - if (active_[Water]) { - const int watpos = pu.phase_pos[Water]; - volumeRatio += cmix_s[watpos] / b_perfcells_dense[watpos]; - } - - if (has_solvent_) { - volumeRatio += cmix_s[solventSaturationIdx] / b_perfcells_dense[solventSaturationIdx]; - } - - if (active_[Oil] && active_[Gas]) { - const int oilpos = pu.phase_pos[Oil]; - const int gaspos = pu.phase_pos[Gas]; - - // Incorporate RS/RV factors if both oil and gas active - const EvalWell d = 1.0 - rv * rs; - - if (d.value() == 0.0) { - OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << wells().name[w] << " during flux calcuation" - << " with rs " << rs << " and rv " << rv); - } - - const EvalWell tmp_oil = (cmix_s[oilpos] - rv * cmix_s[gaspos]) / d; - //std::cout << "tmp_oil " < SimulatorReport StandardWellsDense:: @@ -823,7 +685,10 @@ namespace Opm { StandardWellsDense:: residual() const { - if( ! wellsActive() ) + // TODO: to decide later whether to output this + // Even yes, we do not need resWell_. We will use the values + // from each individual well. + /* if( ! wellsActive() ) { return std::vector(); } @@ -838,7 +703,7 @@ namespace Opm { res[idx] = resWell_[ wellIdx ][ compIdx ]; } } - return res; + return res; */ } @@ -1048,48 +913,14 @@ namespace Opm { { // number of wells and phases - const int nw = wells().number_of_wells; - const int np = wells().number_of_phases; + const int nw = number_of_wells_; + const int np = number_of_phases_; well_potentials.resize(nw * np, 0.0); for (int w = 0; w < nw; ++w) { - - // get the bhp value based on the bhp constraints - const double bhp = mostStrictBhpFromBhpLimits(w); - - // does the well have a THP related constraint? - const bool has_thp_control = wellHasTHPConstraints(w); - - std::vector potentials(np); - - if ( !has_thp_control ) { - - assert(std::abs(bhp) != std::numeric_limits::max()); - - computeWellRatesWithBhp(ebosSimulator, bhp, w, potentials); - - } else { // the well has a THP related constraint - // checking whether a well is newly added, it only happens at the beginning of the report step - if ( !well_state.isNewWell(w) ) { - for (int p = 0; p < np; ++p) { - // This is dangerous for new added well - // since we are not handling the initialization correctly for now - potentials[p] = well_state.wellRates()[w * np + p]; - } - } else { - // We need to generate a reasonable rates to start the iteration process - computeWellRatesWithBhp(ebosSimulator, bhp, w, potentials); - for (double& value : potentials) { - // make the value a little safer in case the BHP limits are default ones - // TODO: a better way should be a better rescaling based on the investigation of the VFP table. - const double rate_safety_scaling_factor = 0.00001; - value *= rate_safety_scaling_factor; - } - } - - potentials = computeWellPotentialWithTHP(ebosSimulator, w, bhp, potentials); - } + std::vector potentials; + well_container_[w]->computeWellPotentials(ebosSimulator, well_state, potentials); // putting the sucessfully calculated potentials to the well_potentials for (int p = 0; p < np; ++p) { @@ -1353,118 +1184,6 @@ namespace Opm { - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - getBhp(const int wellIdx) const { - // return well_container_(wellIdx)->getBhp(); - return 0.0; // TODO: for debugging - } - - - - - - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - getQs(const int wellIdx, const int compIdx) const - { - // TODO: incoporate the change from the new PR to the getQs - // in StandardWell - return well_container_(wellIdx)->getQs(compIdx); - } - - - - - - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - wellVolumeFraction(const int wellIdx, const int compIdx) const - { - const int nw = wells().number_of_wells; - if (compIdx == Water) { - return wellVariables_[WFrac * nw + wellIdx]; - } - - if (compIdx == Gas) { - return wellVariables_[GFrac * nw + wellIdx]; - } - - if (has_solvent_ && compIdx == solventSaturationIdx) { - return wellVariables_[SFrac * nw + wellIdx]; - } - - // Oil fraction - EvalWell well_fraction = 1.0; - if (active_[Water]) { - well_fraction -= wellVariables_[WFrac * nw + wellIdx]; - } - - if (active_[Gas]) { - well_fraction -= wellVariables_[GFrac * nw + wellIdx]; - } - if (has_solvent_) { - well_fraction -= wellVariables_[SFrac * nw + wellIdx]; - } - return well_fraction; - } - - - - - - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - wellVolumeFractionScaled(const int wellIdx, const int compIdx) const - { - const WellControls* wc = wells().ctrls[wellIdx]; - if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { - - if (has_solvent_ && compIdx == solventSaturationIdx) { - return wellVolumeFraction(wellIdx, compIdx); - } - const double* distr = well_controls_get_current_distr(wc); - assert(compIdx < 3); - if (distr[compIdx] > 0.) { - return wellVolumeFraction(wellIdx, compIdx) / distr[compIdx]; - } else { - // TODO: not sure why return EvalWell(0.) causing problem here - // Probably due to the wrong Jacobians. - return wellVolumeFraction(wellIdx, compIdx); - } - } - std::vector g = {1,1,0.01,0.01}; - return (wellVolumeFraction(wellIdx, compIdx) / g[compIdx]); - } - - - - - - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - wellSurfaceVolumeFraction(const int well_index, const int compIdx) const - { - EvalWell sum_volume_fraction_scaled = 0.; - const int numComp = numComponents(); - for (int idx = 0; idx < numComp; ++idx) { - sum_volume_fraction_scaled += wellVolumeFractionScaled(well_index, idx); - } - - assert(sum_volume_fraction_scaled.value() != 0.); - - return wellVolumeFractionScaled(well_index, compIdx) / sum_volume_fraction_scaled; - } - - - - - template bool StandardWellsDense:: @@ -1878,242 +1597,6 @@ namespace Opm { - template - bool - StandardWellsDense:: - wellHasTHPConstraints(const int well_index) const - { - const WellControls* well_control = wells().ctrls[well_index]; - const int nwc = well_controls_get_num(well_control); - for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { - if (well_controls_iget_type(well_control, ctrl_index) == THP) { - return true; - } - } - return false; - } - - - - - - template - void - StandardWellsDense:: - computeWellRatesWithBhp(const Simulator& ebosSimulator, - const EvalWell& bhp, - const int well_index, - std::vector& well_flux) const - { - const int np = wells().number_of_phases; - const int numComp = numComponents(); - well_flux.resize(np, 0.0); - - const bool allow_cf = well_container_[well_index]->crossFlowAllowed(ebosSimulator); - for (int perf = wells().well_connpos[well_index]; perf < wells().well_connpos[well_index + 1]; ++perf) { - const int cell_index = wells().well_cells[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_index, /*timeIdx=*/ 0)); - // flux for each perforation - std::vector cq_s(numComp, 0.0); - std::vector mob(numComp, 0.0); - getMobility(ebosSimulator, well_index, perf, cell_index, mob); - computeWellFlux(well_index, wells().WI[perf], intQuants, mob, bhp, - wellPerforationPressureDiffs()[perf], allow_cf, cq_s); - - for(int p = 0; p < np; ++p) { - well_flux[p] += cq_s[p].value(); - } - } - } - - - - - - - template - double - StandardWellsDense:: - mostStrictBhpFromBhpLimits(const int well_index) const - { - double bhp; - - // type of the well, INJECTOR or PRODUCER - const WellType& well_type = wells().type[well_index]; - // initial bhp value, making the value not usable - switch(well_type) { - case INJECTOR: - bhp = std::numeric_limits::max(); - break; - case PRODUCER: - bhp = -std::numeric_limits::max(); - break; - default: - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << wells().name[well_index]); - } - - // the well controls - const WellControls* well_control = wells().ctrls[well_index]; - // The number of the well controls/constraints - const int nwc = well_controls_get_num(well_control); - - for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { - // finding a BHP constraint - if (well_controls_iget_type(well_control, ctrl_index) == BHP) { - // get the bhp constraint value, it should always be postive assummingly - const double bhp_target = well_controls_iget_target(well_control, ctrl_index); - - switch(well_type) { - case INJECTOR: // using the lower bhp contraint from Injectors - if (bhp_target < bhp) { - bhp = bhp_target; - } - break; - case PRODUCER: - if (bhp_target > bhp) { - bhp = bhp_target; - } - break; - default: - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << wells().name[well_index]); - } // end of switch - } - } - - return bhp; - } - - - - - - template - std::vector - StandardWellsDense:: - computeWellPotentialWithTHP(const Simulator& ebosSimulator, - const int well_index, - const double initial_bhp, // bhp from BHP constraints - const std::vector& initial_potential) const - { - // TODO: pay attention to the situation that finally the potential is calculated based on the bhp control - // TODO: should we consider the bhp constraints during the iterative process? - const int np = wells().number_of_phases; - - assert( np == int(initial_potential.size()) ); - - std::vector potentials = initial_potential; - std::vector old_potentials = potentials; // keeping track of the old potentials - - double bhp = initial_bhp; - double old_bhp = bhp; - - bool converged = false; - const int max_iteration = 1000; - const double bhp_tolerance = 1000.; // 1000 pascal - - int iteration = 0; - - while ( !converged && iteration < max_iteration ) { - // for each iteration, we calculate the bhp based on the rates/potentials with thp constraints - // with considering the bhp value from the bhp limits. At the beginning of each iteration, - // we initialize the bhp to be the bhp value from the bhp limits. Then based on the bhp values calculated - // from the thp constraints, we decide the effective bhp value for well potential calculation. - bhp = initial_bhp; - - // the well controls - const WellControls* well_control = wells().ctrls[well_index]; - // The number of the well controls/constraints - const int nwc = well_controls_get_num(well_control); - - for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { - if (well_controls_iget_type(well_control, ctrl_index) == THP) { - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - - const Opm::PhaseUsage& pu = phase_usage_; - - if (active_[ Water ]) { - aqua = potentials[pu.phase_pos[ Water ] ]; - } - if (active_[ Oil ]) { - liquid = potentials[pu.phase_pos[ Oil ] ]; - } - if (active_[ Gas ]) { - vapour = potentials[pu.phase_pos[ Gas ] ]; - } - - const int vfp = well_controls_iget_vfp(well_control, ctrl_index); - const double thp = well_controls_iget_target(well_control, ctrl_index); - const double alq = well_controls_iget_alq(well_control, ctrl_index); - - // Calculating the BHP value based on THP - // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. - const int first_perf = wells().well_connpos[well_index]; //first perforation - - const WellType& well_type = wells().type[well_index]; - if (well_type == INJECTOR) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), well_index, vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(), - wellPerforationDensities()[first_perf], gravity_); - const double bhp_calculated = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; - // apply the strictest of the bhp controlls i.e. smallest bhp for injectors - if (bhp_calculated < bhp) { - bhp = bhp_calculated; - } - } - else if (well_type == PRODUCER) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), well_index, vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(), - wellPerforationDensities()[first_perf], gravity_); - const double bhp_calculated = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; - // apply the strictest of the bhp controlls i.e. largest bhp for producers - if (bhp_calculated > bhp) { - bhp = bhp_calculated; - } - } else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } - } - } - - // there should be always some available bhp/thp constraints there - if (std::isinf(bhp) || std::isnan(bhp)) { - OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << wells().name[well_index]); - } - - converged = std::abs(old_bhp - bhp) < bhp_tolerance; - - computeWellRatesWithBhp(ebosSimulator, bhp, well_index, potentials); - - // checking whether the potentials have valid values - for (const double value : potentials) { - if (std::isinf(value) || std::isnan(value)) { - OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << wells().name[well_index]); - } - } - - if (!converged) { - old_bhp = bhp; - for (int p = 0; p < np; ++p) { - // TODO: improve the interpolation, will it always be valid with the way below? - // TODO: finding better paramters, better iteration strategy for better convergence rate. - const double potential_update_damping_factor = 0.001; - potentials[p] = potential_update_damping_factor * potentials[p] + (1.0 - potential_update_damping_factor) * old_potentials[p]; - old_potentials[p] = potentials[p]; - } - } - - ++iteration; - } - - if (!converged) { - OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << wells().name[well_index]); - } - - return potentials; - } - template double StandardWellsDense:: From a02a0d859968db7ffc34f35580f86ecb0fb0ad65 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 11:22:08 +0200 Subject: [PATCH 050/104] removing updateWellStateWithTarget from StandardWellsDense and also fixing the assertion error related to disabling the residual() function of StandardWellsDense. --- opm/autodiff/BlackoilModelEbos.hpp | 12 +- opm/autodiff/StandardWellsDense.hpp | 5 - opm/autodiff/StandardWellsDense_impl.hpp | 225 +---------------------- 3 files changed, 7 insertions(+), 235 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 691d4845d..2fb37cf81 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -763,7 +763,7 @@ namespace Opm { std::vector< Scalar >& R_sum, std::vector< Scalar >& maxCoeff, std::vector< Scalar >& B_avg, - std::vector< Scalar >& maxNormWell ) + std::vector< Scalar >& maxNormWell) { // Compute total pore volume (use only owned entries) double pvSum = pvSumLocal; @@ -893,15 +893,15 @@ namespace Opm { } // compute maximum of local well residuals - const Vector& wellResidual = wellModel().residual(); - const int nw = wellResidual.size() / numComp; - assert(nw * numComp == int(wellResidual.size())); - for( int compIdx = 0; compIdx < numComp; ++compIdx ) + // const Vector& wellResidual = wellModel().residual(); + // const int nw = wellResidual.size() / numComp; + // assert(nw * numComp == int(wellResidual.size())); + /* for( int compIdx = 0; compIdx < numComp; ++compIdx ) { for ( int w = 0; w < nw; ++w ) { maxNormWell[compIdx] = std::max(maxNormWell[compIdx], std::abs(wellResidual[nw*compIdx + w])); } - } + } */ // compute global sum and max of quantities const double pvSum = convergenceReduction(grid_.comm(), pvSumLocal, diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index c78a75df9..8df079a22 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -355,11 +355,6 @@ enum WellVariablePositions { const WellState& well_state, const WellMapEntryType& map_entry) const; - void updateWellStateWithTarget(const WellControls* wc, - const int current, - const int well_index, - WellState& xw) const; - double wsolvent(const int well_index) const; double wpolymer(const int well_index) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 145e11fe9..201f8c6f4 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1004,7 +1004,7 @@ namespace Opm { WellControls* wc = wells().ctrls[w]; const int control = well_controls_get_current(wc); well_state.currentControls()[w] = control; - updateWellStateWithTarget(wc, control, w, well_state); + well_container_[w]->updateWellStateWithTarget(control, well_state); // The wells are not considered to be newly added // for next time step @@ -1374,229 +1374,6 @@ namespace Opm { - template - void - StandardWellsDense:: - updateWellStateWithTarget(const WellControls* wc, - const int current, - const int well_index, - WellState& xw) const - { - // number of phases - const int np = wells().number_of_phases; - // Updating well state and primary variables. - // Target values are used as initial conditions for BHP, THP, 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()[well_index] = target; - // TODO: similar to the way below to handle THP - // we should not something related to thp here when there is thp constraint - break; - - case THP: { - xw.thp()[well_index] = target; - - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - - const Opm::PhaseUsage& pu = phase_usage_; - - if (active_[ Water ]) { - aqua = xw.wellRates()[well_index*np + pu.phase_pos[ Water ] ]; - } - if (active_[ Oil ]) { - liquid = xw.wellRates()[well_index*np + pu.phase_pos[ Oil ] ]; - } - if (active_[ Gas ]) { - vapour = xw.wellRates()[well_index*np + pu.phase_pos[ Gas ] ]; - } - - const int vfp = well_controls_iget_vfp(wc, current); - const double& thp = well_controls_iget_target(wc, current); - const double& alq = well_controls_iget_alq(wc, current); - - //Set *BHP* target by calculating bhp from THP - const WellType& well_type = wells().type[well_index]; - - // pick the density in the top layer - const int perf = wells().well_connpos[well_index]; - const double rho = well_perforation_densities_[perf]; - - if (well_type == INJECTOR) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), well_index, vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(), - rho, gravity_); - - xw.bhp()[well_index] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; - } - else if (well_type == PRODUCER) { - const double dp = wellhelpers::computeHydrostaticCorrection( - wells(), well_index, vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(), - rho, gravity_); - - xw.bhp()[well_index] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; - } - else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } - break; - } - - case RESERVOIR_RATE: // intentional fall-through - case SURFACE_RATE: - // checking the number of the phases under control - int numPhasesWithTargetsUnderThisControl = 0; - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - numPhasesWithTargetsUnderThisControl += 1; - } - } - - assert(numPhasesWithTargetsUnderThisControl > 0); - - const WellType& well_type = wells().type[well_index]; - if (well_type == INJECTOR) { - // assign target value as initial guess for injectors - // only handles single phase control at the moment - assert(numPhasesWithTargetsUnderThisControl == 1); - - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.) { - xw.wellRates()[np*well_index + phase] = target / distr[phase]; - } else { - xw.wellRates()[np * well_index + phase] = 0.; - } - } - } else if (well_type == PRODUCER) { - // update the rates of phases under control based on the target, - // and also update rates of phases not under control to keep the rate ratio, - // assuming the mobility ratio does not change for the production wells - double original_rates_under_phase_control = 0.0; - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - original_rates_under_phase_control += xw.wellRates()[np * well_index + phase] * distr[phase]; - } - } - - if (original_rates_under_phase_control != 0.0 ) { - double scaling_factor = target / original_rates_under_phase_control; - - for (int phase = 0; phase < np; ++phase) { - xw.wellRates()[np * well_index + phase] *= scaling_factor; - } - } else { // scaling factor is not well defied when original_rates_under_phase_control is zero - // separating targets equally between phases under control - const double target_rate_divided = target / numPhasesWithTargetsUnderThisControl; - for (int phase = 0; phase < np; ++phase) { - if (distr[phase] > 0.0) { - xw.wellRates()[np * well_index + phase] = target_rate_divided / distr[phase]; - } else { - // this only happens for SURFACE_RATE control - xw.wellRates()[np * well_index + phase] = target_rate_divided; - } - } - } - } else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } - - break; - } // end of switch - - - std::vector g = {1.0, 1.0, 0.01}; - if (well_controls_iget_type(wc, current) == RESERVOIR_RATE) { - for (int phase = 0; phase < np; ++phase) { - g[phase] = distr[phase]; - } - } - - // the number of wells - const int nw = wells().number_of_wells; - - switch (well_controls_iget_type(wc, current)) { - case THP: - case BHP: { - const WellType& well_type = wells().type[well_index]; - xw.wellSolutions()[nw*XvarWell + well_index] = 0.0; - if (well_type == INJECTOR) { - for (int p = 0; p < np; ++p) { - xw.wellSolutions()[nw*XvarWell + well_index] += xw.wellRates()[np*well_index + p] * wells().comp_frac[np*well_index + p]; - } - } else { - for (int p = 0; p < np; ++p) { - xw.wellSolutions()[nw*XvarWell + well_index] += g[p] * xw.wellRates()[np*well_index + p]; - } - } - break; - } - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - xw.wellSolutions()[nw*XvarWell + well_index] = xw.bhp()[well_index]; - break; - } // end of switch - - double tot_well_rate = 0.0; - for (int p = 0; p < np; ++p) { - tot_well_rate += g[p] * xw.wellRates()[np*well_index + p]; - } - if(std::abs(tot_well_rate) > 0) { - if (active_[ Water ]) { - xw.wellSolutions()[WFrac*nw + well_index] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; - } - if (active_[ Gas ]) { - xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (xw.wellRates()[np*well_index + Gas] - xw.solventWellRate(well_index)) / tot_well_rate ; - } - if (has_solvent_) { - xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; - } - } else { - const WellType& well_type = wells().type[well_index]; - if (well_type == INJECTOR) { - // only single phase injection handled - if (active_[Water]) { - if (distr[Water] > 0.0) { - xw.wellSolutions()[WFrac * nw + well_index] = 1.0; - } else { - xw.wellSolutions()[WFrac * nw + well_index] = 0.0; - } - } - - if (active_[Gas]) { - if (distr[Gas] > 0.0) { - xw.wellSolutions()[GFrac * nw + well_index] = 1.0 - wsolvent(well_index); - if (has_solvent_) { - xw.wellSolutions()[SFrac * nw + well_index] = wsolvent(well_index); - } - } else { - xw.wellSolutions()[GFrac * nw + well_index] = 0.0; - } - } - - // TODO: it is possible to leave injector as a oil well, - // when F_w and F_g both equals to zero, not sure under what kind of circumstance - // this will happen. - } else if (well_type == PRODUCER) { // producers - if (active_[Water]) { - xw.wellSolutions()[WFrac * nw + well_index] = 1.0 / np; - } - if (active_[Gas]) { - xw.wellSolutions()[GFrac * nw + well_index] = 1.0 / np; - } - } else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } - } - - } - - - - - template double StandardWellsDense:: From 7e9eee4ee44ed4b8f66471e2c0ce298ff3ade5b1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 12:10:13 +0200 Subject: [PATCH 051/104] moving wsolvent and wpolymer to WellInterface to do this, adding well_ecl_ and current_step_ to WelInterface. --- opm/autodiff/StandardWellsDense.hpp | 4 -- opm/autodiff/StandardWellsDense_impl.hpp | 65 ------------------------ opm/autodiff/WellInterface.hpp | 10 +++- opm/autodiff/WellInterface_impl.hpp | 42 +++++++++++++-- 4 files changed, 47 insertions(+), 74 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 8df079a22..760c0fe06 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -355,10 +355,6 @@ enum WellVariablePositions { const WellState& well_state, const WellMapEntryType& map_entry) const; - double wsolvent(const int well_index) const; - - double wpolymer(const int well_index) const; - void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; void computeRepRadiusPerfLength(const Grid& grid); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 201f8c6f4..af7ec6054 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1374,71 +1374,6 @@ namespace Opm { - template - double - StandardWellsDense:: - wsolvent(const int well_index) const { - - if (!has_solvent_) { - return 0.0; - } - - // loop over all wells until we find the well with the matching name - for (const auto& well : wells_ecl_) { - if (well->getStatus( current_timeIdx_ ) == WellCommon::SHUT) { - continue; - } - - WellInjectionProperties injection = well->getInjectionProperties(current_timeIdx_); - if (injection.injectorType == WellInjector::GAS) { - - double solventFraction = well->getSolventFraction(current_timeIdx_); - - // Look until we find the correct well - if (well->name() == wells().name[well_index]) { - return solventFraction; - } - } - } - // we didn't find it return 0; - assert(false); - return 0.0; - } - - - template - double - StandardWellsDense:: - wpolymer(const int well_index) const { - - if (!has_polymer_) { - return 0.0; - } - - // loop over all wells until we find the well with the matching name - for (const auto& well : wells_ecl_) { - if (well->getStatus( current_timeIdx_ ) == WellCommon::SHUT) { - continue; - } - - WellInjectionProperties injection = well->getInjectionProperties(current_timeIdx_); - WellPolymerProperties polymer = well->getPolymerProperties(current_timeIdx_); - if (injection.injectorType == WellInjector::WATER) { - - double polymerFraction = polymer.m_polymerConcentration; - - // Look until we find the correct well - if (well->name() == wells().name[well_index]) { - return polymerFraction; - } - } - } - // we didn't find it return 0; - assert(false); - return 0.0; - } - - template void StandardWellsDense:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index d7827c706..e3ad10f32 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -79,9 +79,10 @@ namespace Opm typedef DenseAd::Evaluation Eval; static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); + static const bool has_polymer = GET_PROP_VALUE(TypeTag, EnablePolymer); /// Constructor - // TODO: Wel can be reference. + // TODO: Well can be reference. WellInterface(const Well* well, const int time_step, const Wells* wells); /// Well name. @@ -161,6 +162,8 @@ namespace Opm double wsolvent() const; + double wpolymer() const; + virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, const ModelParameters& param) const = 0; @@ -202,6 +205,11 @@ namespace Opm std::vector& well_potentials) const = 0; protected: + + const Well* well_ecl_; + + const int current_step_; + // TODO: some variables shared by all the wells should be made static // well name std::string name_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index c2801186d..b3748228a 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -26,6 +26,8 @@ namespace Opm template WellInterface:: WellInterface(const Well* well, const int time_step, const Wells* wells) + : well_ecl_(well) + , current_step_(time_step) { // TODO: trying to use wells struct as little as possible here, be prepared to @@ -380,10 +382,42 @@ namespace Opm WellInterface:: wsolvent() const { - // TODO: not handling it for the moment - // TODO: it needs information from the well_ecl - // TODO: will decide on well_ecl role later. - // It can be just one member variable and no need to deal with well_ecl at all + if (!has_solvent) { + return 0.0; + } + + WellInjectionProperties injection = well_ecl_->getInjectionProperties(current_step_); + if (injection.injectorType == WellInjector::GAS) { + double solvent_fraction = well_ecl_->getSolventFraction(current_step_); + return solvent_fraction; + } + + assert(false); + return 0.0; + } + + + + + + template + double + WellInterface:: + wpolymer() const + { + if (!has_polymer) { + return 0.0; + } + + WellInjectionProperties injection = well_ecl_->getInjectionProperties(current_step_); + WellPolymerProperties polymer = well_ecl_->getPolymerProperties(current_step_); + + if (injection.injectorType == WellInjector::WATER) { + const double polymer_injection_concentration = polymer.m_polymerConcentration; + return polymer_injection_concentration; + } + + assert(false); // TODO: find a more logical way to handle this situation return 0.0; } From 358d4c2a00ec86366d5244bb243025ee5f85ba6f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 14:27:02 +0200 Subject: [PATCH 052/104] cleaning up more things from StandardWellsDense --- CMakeLists_files.cmake | 4 +- opm/autodiff/StandardWellsDense.hpp | 25 ------- opm/autodiff/StandardWellsDense_impl.hpp | 95 ------------------------ 3 files changed, 2 insertions(+), 122 deletions(-) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index cb36ffeca..ccdb2f455 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -239,8 +239,8 @@ list (APPEND PUBLIC_HEADER_FILES opm/autodiff/WellHelpers.hpp opm/autodiff/StandardWells.hpp opm/autodiff/StandardWells_impl.hpp - opm/autodiff/WellInterface.hpp - opm/autodiff/StandardWell.cpp + opm/autodiff/WellInterface.hpp + opm/autodiff/StandardWell.cpp opm/autodiff/StandardWellsDense.hpp opm/autodiff/StandardWellsSolvent.hpp opm/autodiff/StandardWellsSolvent_impl.hpp diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 760c0fe06..09b8d865b 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -105,7 +105,6 @@ enum WellVariablePositions { typedef Dune::BCRSMatrix Mat; typedef Dune::BlockVector BVector; - typedef DenseAd::Evaluation EvalWell; typedef DenseAd::Evaluation Eval; typedef Ewoms::BlackOilPolymerModule PolymerModule; @@ -174,15 +173,10 @@ enum WellVariablePositions { // xw to update Well State void applySolutionWellState(const BVector& x, WellState& well_state) const; - int flowPhaseToEbosCompIdx( const int phaseIdx ) const; - int flowToEbosPvIdx( const int flowPv ) const; int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; - std::vector - extractPerfData(const std::vector& in) const; - int numPhases() const; int numCells() const; @@ -201,18 +195,8 @@ enum WellVariablePositions { /// return true if wells are available on this process bool localWellsActive() const; - /// Density of each well perforation - const std::vector& wellPerforationDensities() const; - - /// Diff to bhp for each well perforation. - const std::vector& wellPerforationPressureDiffs() const; - - EvalWell extendEval(const Eval& in) const; - void setWellVariables(const WellState& xw); - void print(const EvalWell& in) const; - void computeAccumWells(); SimulatorReport solveWellEq(Simulator& ebosSimulator, @@ -307,21 +291,12 @@ enum WellVariablePositions { std::vector well_perforation_efficiency_factors_; // the depth of the all the cell centers // for standard Wells, it the same with the perforation depth - std::vector cell_depths_; std::vector pv_; - std::vector well_perforation_densities_; - std::vector well_perforation_pressure_diffs_; - - std::vector wpolymer_; - std::vector wsolvent_; - std::vector wells_rep_radius_; std::vector wells_perf_length_; std::vector wells_bore_diameter_; - std::vector wellVariables_; - long int global_nc_; mutable BVector scaleAddRes_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index af7ec6054..78cb7aebb 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -57,7 +57,6 @@ namespace Opm { phase_usage_ = phase_usage_arg; active_ = active_arg; gravity_ = gravity_arg; - cell_depths_ = extractPerfData(depth_arg); pv_ = pv_arg; calculateEfficiencyFactors(); @@ -369,17 +368,6 @@ namespace Opm { return flowToEbos[ flowPv ]; } - template - int - StandardWellsDense:: - flowPhaseToEbosCompIdx( const int phaseIdx ) const - { - const int phaseToComp[ 3 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx}; - if (phaseIdx > 2 ) - return phaseIdx; - return phaseToComp[ phaseIdx ]; - } - @@ -394,23 +382,6 @@ namespace Opm { return flowToEbos[ phaseIdx ]; } - template - std::vector - StandardWellsDense:: - extractPerfData(const std::vector& in) const - { - const int nw = wells().number_of_wells; - const int nperf = wells().well_connpos[nw]; - std::vector out(nperf); - for (int w = 0; w < nw; ++w) { - for (int perf = wells().well_connpos[w] ; perf < wells().well_connpos[w+1]; ++perf) { - const int well_idx = wells().well_cells[perf]; - out[perf] = in[well_idx]; - } - } - return out; - } - @@ -516,46 +487,6 @@ namespace Opm { - template - const std::vector& - StandardWellsDense:: - wellPerforationDensities() const - { - return well_perforation_densities_; - } - - - - - - template - const std::vector& - StandardWellsDense:: - wellPerforationPressureDiffs() const - { - return well_perforation_pressure_diffs_; - } - - - - - - template - typename StandardWellsDense::EvalWell - StandardWellsDense:: - extendEval(const Eval& in) const { - EvalWell out = 0.0; - out.setValue(in.value()); - for(int eqIdx = 0; eqIdx < numEq;++eqIdx) { - out.setDerivative(eqIdx, in.derivative(flowToEbosPvIdx(eqIdx))); - } - return out; - } - - - - - template void StandardWellsDense:: @@ -570,21 +501,6 @@ namespace Opm { - template - void - StandardWellsDense:: - print(const EvalWell& in) const - { - std::cout << in.value() << std::endl; - for (int i = 0; i < in.size; ++i) { - std::cout << in.derivative(i) << std::endl; - } - } - - - - - template void StandardWellsDense:: @@ -1029,17 +945,6 @@ namespace Opm { - template - const std::vector& - StandardWellsDense:: - wellPerfEfficiencyFactors() const - { - return well_perforation_efficiency_factors_; - } - - - - template void From 369ccfef5202bc212635a746e86a8362d61a99e0 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 14:48:37 +0200 Subject: [PATCH 053/104] handling well_efficiency_factor_ in WellInterface. --- opm/autodiff/StandardWellsDense.hpp | 6 ------ opm/autodiff/StandardWellsDense_impl.hpp | 7 ++----- opm/autodiff/WellInterface.hpp | 2 ++ opm/autodiff/WellInterface_impl.hpp | 13 ++++++++++++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 09b8d865b..64b496488 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -285,12 +285,6 @@ enum WellVariablePositions { double gravity_; const RateConverterType& rate_converter_; - // The efficiency factor for each connection. It is specified based on wells and groups, - // We calculate the factor for each connection for the computation of contributions to the mass balance equations. - // By default, they should all be one. - std::vector well_perforation_efficiency_factors_; - // the depth of the all the cell centers - // for standard Wells, it the same with the perforation depth std::vector pv_; std::vector wells_rep_radius_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 78cb7aebb..7a3d18cad 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -958,15 +958,12 @@ namespace Opm { const int nw = wells().number_of_wells; for (int w = 0; w < nw; ++w) { - const std::string well_name = wells().name[w]; + const std::string well_name = well_container_[w]->name(); const WellNode& well_node = wellCollection()->findWellNode(well_name); const double well_efficiency_factor = well_node.getAccumulativeEfficiencyFactor(); - // assign the efficiency factor to each perforation related. - for (int perf = wells().well_connpos[w]; perf < wells().well_connpos[w + 1]; ++perf) { - well_perforation_efficiency_factors_[perf] = well_efficiency_factor; - } + well_container_[w]->setWellEfficiencyFactor(well_efficiency_factor); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index e3ad10f32..47892ed3c 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -204,6 +204,8 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const = 0; + void setWellEfficiencyFactor(const double efficiency_factor); + protected: const Well* well_ecl_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index b3748228a..0e387060e 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -98,7 +98,6 @@ namespace Opm } well_efficiency_factor_ = 1.0; - // TODO: need to calculate based on wellCollections, or it should happen in the Well Model side. } @@ -281,6 +280,18 @@ namespace Opm + template + void + WellInterface:: + setWellEfficiencyFactor(const double efficiency_factor) + { + well_efficiency_factor_ = efficiency_factor; + } + + + + + template const PhaseUsage& WellInterface:: From 1a4bd6ffb2eaf5995b2c694cffdf5540c0fc9d4e Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 16:41:25 +0200 Subject: [PATCH 054/104] adding checkMaxWaterCutLimit and checkRateEconLimits to WellInterface --- opm/autodiff/StandardWellsDense.hpp | 8 -- opm/autodiff/StandardWellsDense_impl.hpp | 141 ++--------------------- opm/autodiff/WellInterface.hpp | 17 +++ opm/autodiff/WellInterface_impl.hpp | 131 +++++++++++++++++++++ 4 files changed, 155 insertions(+), 142 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 64b496488..cdab57187 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -295,10 +295,6 @@ enum WellVariablePositions { mutable BVector scaleAddRes_; - bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const int well_number) const; - using WellMapType = typename WellState::WellMapType; using WellMapEntryType = typename WellState::mapentry_t; @@ -320,10 +316,6 @@ enum WellVariablePositions { const WellState& well_state, const WellMapEntryType& map_entry) const; - RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const WellMapEntryType& map_entry) const; - void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; void computeRepRadiusPerfLength(const Grid& grid); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 7a3d18cad..096a14f07 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -137,6 +137,7 @@ namespace Opm { OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl "); } + // TODO: The following should not happen, right? const Well* well_ecl = wells_ecl_[index_well]; if (well_ecl->getStatus(current_timeIdx_) == WellCommon::SHUT) { continue; @@ -620,6 +621,7 @@ namespace Opm { } } return res; */ + return std::vector(1, 0.0); // to disable warning, unusable } @@ -756,7 +758,7 @@ namespace Opm { const int well_number = map_entry[0]; if (econ_production_limits.onAnyRateLimit()) { - rate_limit_violated = checkRateEconLimits(econ_production_limits, well_state, well_number); + rate_limit_violated = well_container_[well_number]->checkRateEconLimits(econ_production_limits, well_state); } if (rate_limit_violated) { @@ -1086,58 +1088,6 @@ namespace Opm { - template - bool - StandardWellsDense:: - checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const int well_number) const - { - - const Opm::PhaseUsage& pu = phase_usage_; - const int np = well_state.numPhases(); - - if (econ_production_limits.onMinOilRate()) { - assert(active_[Oil]); - const double oil_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Oil ] ]; - const double min_oil_rate = econ_production_limits.minOilRate(); - if (std::abs(oil_rate) < min_oil_rate) { - return true; - } - } - - if (econ_production_limits.onMinGasRate() ) { - assert(active_[Gas]); - const double gas_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Gas ] ]; - const double min_gas_rate = econ_production_limits.minGasRate(); - if (std::abs(gas_rate) < min_gas_rate) { - return true; - } - } - - if (econ_production_limits.onMinLiquidRate() ) { - assert(active_[Oil]); - assert(active_[Water]); - const double oil_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Oil ] ]; - const double water_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Water ] ]; - const double liquid_rate = oil_rate + water_rate; - const double min_liquid_rate = econ_production_limits.minLiquidRate(); - if (std::abs(liquid_rate) < min_liquid_rate) { - return true; - } - } - - if (econ_production_limits.onMinReservoirFluidRate()) { - OpmLog::warning("NOT_SUPPORTING_MIN_RESERVOIR_FLUID_RATE", "Minimum reservoir fluid production rate limit is not supported yet"); - } - - return false; - } - - - - - template typename StandardWellsDense::RatioCheckTuple StandardWellsDense:: @@ -1158,8 +1108,11 @@ namespace Opm { int worst_offending_connection = INVALIDCONNECTION; double violation_extent = -1.0; + const int index_of_well = map_entry[0]; + if (econ_production_limits.onMaxWaterCut()) { - const RatioCheckTuple water_cut_return = checkMaxWaterCutLimit(econ_production_limits, well_state, map_entry); + const RatioCheckTuple water_cut_return = + well_container_[index_of_well]->checkMaxWaterCutLimit(econ_production_limits, well_state); bool water_cut_violated = std::get<0>(water_cut_return); if (water_cut_violated) { any_limit_violated = true; @@ -1196,86 +1149,6 @@ namespace Opm { - template - typename StandardWellsDense::RatioCheckTuple - StandardWellsDense:: - checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const WellMapEntryType& map_entry) const - { - bool water_cut_limit_violated = false; - int worst_offending_connection = INVALIDCONNECTION; - bool last_connection = false; - double violation_extent = -1.0; - - const int np = well_state.numPhases(); - const Opm::PhaseUsage& pu = phase_usage_; - const int well_number = map_entry[0]; - - assert(active_[Oil]); - assert(active_[Water]); - - const double oil_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Oil ] ]; - const double water_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Water ] ]; - const double liquid_rate = oil_rate + water_rate; - double water_cut; - if (std::abs(liquid_rate) != 0.) { - water_cut = water_rate / liquid_rate; - } else { - water_cut = 0.0; - } - - const double max_water_cut_limit = econ_production_limits.maxWaterCut(); - if (water_cut > max_water_cut_limit) { - water_cut_limit_violated = true; - } - - if (water_cut_limit_violated) { - // need to handle the worst_offending_connection - const int perf_start = map_entry[1]; - const int perf_number = map_entry[2]; - - std::vector water_cut_perf(perf_number); - for (int perf = 0; perf < perf_number; ++perf) { - const int i_perf = perf_start + perf; - const double oil_perf_rate = well_state.perfPhaseRates()[i_perf * np + pu.phase_pos[ Oil ] ]; - const double water_perf_rate = well_state.perfPhaseRates()[i_perf * np + pu.phase_pos[ Water ] ]; - const double liquid_perf_rate = oil_perf_rate + water_perf_rate; - if (std::abs(liquid_perf_rate) != 0.) { - water_cut_perf[perf] = water_perf_rate / liquid_perf_rate; - } else { - water_cut_perf[perf] = 0.; - } - } - - last_connection = (perf_number == 1); - if (last_connection) { - worst_offending_connection = 0; - violation_extent = water_cut_perf[0] / max_water_cut_limit; - return std::make_tuple(water_cut_limit_violated, last_connection, worst_offending_connection, violation_extent); - } - - double max_water_cut_perf = 0.; - for (int perf = 0; perf < perf_number; ++perf) { - if (water_cut_perf[perf] > max_water_cut_perf) { - worst_offending_connection = perf; - max_water_cut_perf = water_cut_perf[perf]; - } - } - - assert(max_water_cut_perf != 0.); - assert((worst_offending_connection >= 0) && (worst_offending_connection < perf_number)); - - violation_extent = max_water_cut_perf / max_water_cut_limit; - } - - return std::make_tuple(water_cut_limit_violated, last_connection, worst_offending_connection, violation_extent); - } - - - - - template void StandardWellsDense:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 47892ed3c..d7d28ef4c 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -206,8 +206,25 @@ namespace Opm void setWellEfficiencyFactor(const double efficiency_factor); + bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; + + // a tuple type for ratio limit check. + // first value indicates whether ratio limit is violated, when the ratio limit is not violated, the following three + // values should not be used. + // second value indicates whehter there is only one connection left. + // third value indicates the indx of the worst-offending connection. + // the last value indicates the extent of the violation for the worst-offending connection, which is defined by + // the ratio of the actual value to the value of the violated limit. + using RatioCheckTuple = std::tuple; + + RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; protected: + // to indicate a invalid connection + static const int INVALIDCONNECTION = -100000; + const Well* well_ecl_; const int current_step_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 0e387060e..7486c1822 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -500,4 +500,135 @@ namespace Opm } return false; } + + + + + + template + bool + WellInterface:: + checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const + { + const Opm::PhaseUsage& pu = *phase_usage_; + const int np = numberOfPhases(); + + if (econ_production_limits.onMinOilRate()) { + assert(active()[Oil]); + const double oil_rate = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Oil ] ]; + const double min_oil_rate = econ_production_limits.minOilRate(); + if (std::abs(oil_rate) < min_oil_rate) { + return true; + } + } + + if (econ_production_limits.onMinGasRate() ) { + assert(active()[Gas]); + const double gas_rate = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Gas ] ]; + const double min_gas_rate = econ_production_limits.minGasRate(); + if (std::abs(gas_rate) < min_gas_rate) { + return true; + } + } + + if (econ_production_limits.onMinLiquidRate() ) { + assert(active()[Oil]); + assert(active()[Water]); + const double oil_rate = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Oil ] ]; + const double water_rate = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Water ] ]; + const double liquid_rate = oil_rate + water_rate; + const double min_liquid_rate = econ_production_limits.minLiquidRate(); + if (std::abs(liquid_rate) < min_liquid_rate) { + return true; + } + } + + if (econ_production_limits.onMinReservoirFluidRate()) { + OpmLog::warning("NOT_SUPPORTING_MIN_RESERVOIR_FLUID_RATE", "Minimum reservoir fluid production rate limit is not supported yet"); + } + + return false; + } + + + + + + + template + typename WellInterface::RatioCheckTuple + WellInterface:: + checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const + { + bool water_cut_limit_violated = false; + int worst_offending_connection = INVALIDCONNECTION; + bool last_connection = false; + double violation_extent = -1.0; + + const int np = numberOfPhases(); + const Opm::PhaseUsage& pu = *phase_usage_; + const int well_number = index_of_well_; + + assert(active()[Oil]); + assert(active()[Water]); + + const double oil_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Oil ] ]; + const double water_rate = well_state.wellRates()[well_number * np + pu.phase_pos[ Water ] ]; + const double liquid_rate = oil_rate + water_rate; + double water_cut; + if (std::abs(liquid_rate) != 0.) { + water_cut = water_rate / liquid_rate; + } else { + water_cut = 0.0; + } + + const double max_water_cut_limit = econ_production_limits.maxWaterCut(); + if (water_cut > max_water_cut_limit) { + water_cut_limit_violated = true; + } + + if (water_cut_limit_violated) { + // need to handle the worst_offending_connection + const int perf_start = first_perf_; + const int perf_number = number_of_perforations_; + + std::vector water_cut_perf(perf_number); + for (int perf = 0; perf < perf_number; ++perf) { + const int i_perf = perf_start + perf; + const double oil_perf_rate = well_state.perfPhaseRates()[i_perf * np + pu.phase_pos[ Oil ] ]; + const double water_perf_rate = well_state.perfPhaseRates()[i_perf * np + pu.phase_pos[ Water ] ]; + const double liquid_perf_rate = oil_perf_rate + water_perf_rate; + if (std::abs(liquid_perf_rate) != 0.) { + water_cut_perf[perf] = water_perf_rate / liquid_perf_rate; + } else { + water_cut_perf[perf] = 0.; + } + } + + last_connection = (perf_number == 1); + if (last_connection) { + worst_offending_connection = 0; + violation_extent = water_cut_perf[0] / max_water_cut_limit; + return std::make_tuple(water_cut_limit_violated, last_connection, worst_offending_connection, violation_extent); + } + + double max_water_cut_perf = 0.; + for (int perf = 0; perf < perf_number; ++perf) { + if (water_cut_perf[perf] > max_water_cut_perf) { + worst_offending_connection = perf; + max_water_cut_perf = water_cut_perf[perf]; + } + } + + assert(max_water_cut_perf != 0.); + assert((worst_offending_connection >= 0) && (worst_offending_connection < perf_number)); + + violation_extent = max_water_cut_perf / max_water_cut_limit; + } + + return std::make_tuple(water_cut_limit_violated, last_connection, worst_offending_connection, violation_extent); + } + } From 5986295975289100749198f6e480598eda88acc9 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 25 Jul 2017 17:11:36 +0200 Subject: [PATCH 055/104] adding checkRatioEconLimits to WellInterface --- opm/autodiff/StandardWellsDense.hpp | 4 -- opm/autodiff/StandardWellsDense_impl.hpp | 63 +----------------------- opm/autodiff/WellInterface.hpp | 3 ++ opm/autodiff/WellInterface_impl.hpp | 57 +++++++++++++++++++++ 4 files changed, 61 insertions(+), 66 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index cdab57187..b5a082505 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -312,10 +312,6 @@ enum WellVariablePositions { }; - RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const WellMapEntryType& map_entry) const; - void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; void computeRepRadiusPerfLength(const Grid& grid); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 096a14f07..25cef9024 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -790,7 +790,7 @@ namespace Opm { RatioCheckTuple ratio_check_return; if (econ_production_limits.onAnyRatioLimit()) { - ratio_check_return = checkRatioEconLimits(econ_production_limits, well_state, map_entry); + ratio_check_return = well_container_[w]->checkRatioEconLimits(econ_production_limits, well_state); ratio_limits_violated = std::get<0>(ratio_check_return); } @@ -1088,67 +1088,6 @@ namespace Opm { - template - typename StandardWellsDense::RatioCheckTuple - StandardWellsDense:: - checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state, - const WellMapEntryType& map_entry) const - { - // TODO: not sure how to define the worst-offending connection when more than one - // ratio related limit is violated. - // The defintion used here is that we define the violation extent based on the - // ratio between the value and the corresponding limit. - // For each violated limit, we decide the worst-offending connection separately. - // Among the worst-offending connections, we use the one has the biggest violation - // extent. - - bool any_limit_violated = false; - bool last_connection = false; - int worst_offending_connection = INVALIDCONNECTION; - double violation_extent = -1.0; - - const int index_of_well = map_entry[0]; - - if (econ_production_limits.onMaxWaterCut()) { - const RatioCheckTuple water_cut_return = - well_container_[index_of_well]->checkMaxWaterCutLimit(econ_production_limits, well_state); - bool water_cut_violated = std::get<0>(water_cut_return); - if (water_cut_violated) { - any_limit_violated = true; - const double violation_extent_water_cut = std::get<3>(water_cut_return); - if (violation_extent_water_cut > violation_extent) { - violation_extent = violation_extent_water_cut; - worst_offending_connection = std::get<2>(water_cut_return); - last_connection = std::get<1>(water_cut_return); - } - } - } - - if (econ_production_limits.onMaxGasOilRatio()) { - OpmLog::warning("NOT_SUPPORTING_MAX_GOR", "the support for max Gas-Oil ratio is not implemented yet!"); - } - - if (econ_production_limits.onMaxWaterGasRatio()) { - OpmLog::warning("NOT_SUPPORTING_MAX_WGR", "the support for max Water-Gas ratio is not implemented yet!"); - } - - if (econ_production_limits.onMaxGasLiquidRatio()) { - OpmLog::warning("NOT_SUPPORTING_MAX_GLR", "the support for max Gas-Liquid ratio is not implemented yet!"); - } - - if (any_limit_violated) { - assert(worst_offending_connection >=0); - assert(violation_extent > 1.); - } - - return std::make_tuple(any_limit_violated, last_connection, worst_offending_connection, violation_extent); - } - - - - - template void StandardWellsDense:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index d7d28ef4c..961ab4485 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -220,6 +220,9 @@ namespace Opm RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, const WellState& well_state) const; + + RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; protected: // to indicate a invalid connection diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 7486c1822..442638989 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -631,4 +631,61 @@ namespace Opm return std::make_tuple(water_cut_limit_violated, last_connection, worst_offending_connection, violation_extent); } + + + + + template + typename WellInterface::RatioCheckTuple + WellInterface:: + checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const + { + // TODO: not sure how to define the worst-offending connection when more than one + // ratio related limit is violated. + // The defintion used here is that we define the violation extent based on the + // ratio between the value and the corresponding limit. + // For each violated limit, we decide the worst-offending connection separately. + // Among the worst-offending connections, we use the one has the biggest violation + // extent. + + bool any_limit_violated = false; + bool last_connection = false; + int worst_offending_connection = INVALIDCONNECTION; + double violation_extent = -1.0; + + if (econ_production_limits.onMaxWaterCut()) { + const RatioCheckTuple water_cut_return = checkMaxWaterCutLimit(econ_production_limits, well_state); + bool water_cut_violated = std::get<0>(water_cut_return); + if (water_cut_violated) { + any_limit_violated = true; + const double violation_extent_water_cut = std::get<3>(water_cut_return); + if (violation_extent_water_cut > violation_extent) { + violation_extent = violation_extent_water_cut; + worst_offending_connection = std::get<2>(water_cut_return); + last_connection = std::get<1>(water_cut_return); + } + } + } + + if (econ_production_limits.onMaxGasOilRatio()) { + OpmLog::warning("NOT_SUPPORTING_MAX_GOR", "the support for max Gas-Oil ratio is not implemented yet!"); + } + + if (econ_production_limits.onMaxWaterGasRatio()) { + OpmLog::warning("NOT_SUPPORTING_MAX_WGR", "the support for max Water-Gas ratio is not implemented yet!"); + } + + if (econ_production_limits.onMaxGasLiquidRatio()) { + OpmLog::warning("NOT_SUPPORTING_MAX_GLR", "the support for max Gas-Liquid ratio is not implemented yet!"); + } + + if (any_limit_violated) { + assert(worst_offending_connection >=0); + assert(violation_extent > 1.); + } + + return std::make_tuple(any_limit_violated, last_connection, worst_offending_connection, violation_extent); + } + } From 63c418c02f4815721e20d1a5ae92c426b20b4a89 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 26 Jul 2017 11:01:26 +0200 Subject: [PATCH 056/104] adding fucntion updateListEconLimited() to WellInterface --- opm/autodiff/StandardWellsDense_impl.hpp | 96 +----------------------- opm/autodiff/WellInterface.hpp | 3 + opm/autodiff/WellInterface_impl.hpp | 92 +++++++++++++++++++++++ 3 files changed, 98 insertions(+), 93 deletions(-) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 25cef9024..3bce136aa 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -723,99 +723,9 @@ namespace Opm { const WellState& well_state, DynamicListEconLimited& list_econ_limited) const { - // With no wells (on process) wells_struct is a null pointer - const int nw = (wells_struct)? wells_struct->number_of_wells : 0; - - for (int w = 0; w < nw; ++w) { - // flag to check if the mim oil/gas rate limit is violated - bool rate_limit_violated = false; - const std::string& well_name = wells_struct->name[w]; - const Well* well_ecl = schedule.getWell(well_name); - const WellEconProductionLimits& econ_production_limits = well_ecl->getEconProductionLimits(current_step); - - // economic limits only apply for production wells. - if (wells_struct->type[w] != PRODUCER) { - continue; - } - - // if no limit is effective here, then continue to the next well - if ( !econ_production_limits.onAnyEffectiveLimit() ) { - continue; - } - // for the moment, we only handle rate limits, not handling potential limits - // the potential limits should not be difficult to add - const WellEcon::QuantityLimitEnum& quantity_limit = econ_production_limits.quantityLimit(); - if (quantity_limit == WellEcon::POTN) { - const std::string msg = std::string("POTN limit for well ") + well_name + std::string(" is not supported for the moment. \n") - + std::string("All the limits will be evaluated based on RATE. "); - OpmLog::warning("NOT_SUPPORTING_POTN", msg); - } - - const WellMapType& well_map = well_state.wellMap(); - const typename WellMapType::const_iterator i_well = well_map.find(well_name); - assert(i_well != well_map.end()); // should always be found? - const WellMapEntryType& map_entry = i_well->second; - const int well_number = map_entry[0]; - - if (econ_production_limits.onAnyRateLimit()) { - rate_limit_violated = well_container_[well_number]->checkRateEconLimits(econ_production_limits, well_state); - } - - if (rate_limit_violated) { - if (econ_production_limits.endRun()) { - const std::string warning_message = std::string("ending run after well closed due to economic limits is not supported yet \n") - + std::string("the program will keep running after ") + well_name + std::string(" is closed"); - OpmLog::warning("NOT_SUPPORTING_ENDRUN", warning_message); - } - - if (econ_production_limits.validFollowonWell()) { - OpmLog::warning("NOT_SUPPORTING_FOLLOWONWELL", "opening following on well after well closed is not supported yet"); - } - - if (well_ecl->getAutomaticShutIn()) { - list_econ_limited.addShutWell(well_name); - const std::string msg = std::string("well ") + well_name + std::string(" will be shut in due to economic limit"); - OpmLog::info(msg); - } else { - list_econ_limited.addStoppedWell(well_name); - const std::string msg = std::string("well ") + well_name + std::string(" will be stopped due to economic limit"); - OpmLog::info(msg); - } - // the well is closed, not need to check other limits - continue; - } - - // checking for ratio related limits, mostly all kinds of ratio. - bool ratio_limits_violated = false; - RatioCheckTuple ratio_check_return; - - if (econ_production_limits.onAnyRatioLimit()) { - ratio_check_return = well_container_[w]->checkRatioEconLimits(econ_production_limits, well_state); - ratio_limits_violated = std::get<0>(ratio_check_return); - } - - if (ratio_limits_violated) { - const bool last_connection = std::get<1>(ratio_check_return); - const int worst_offending_connection = std::get<2>(ratio_check_return); - - const int perf_start = map_entry[1]; - - assert((worst_offending_connection >= 0) && (worst_offending_connection < map_entry[2])); - - const int cell_worst_offending_connection = wells_struct->well_cells[perf_start + worst_offending_connection]; - list_econ_limited.addClosedConnectionsForWell(well_name, cell_worst_offending_connection); - const std::string msg = std::string("Connection ") + std::to_string(worst_offending_connection) + std::string(" for well ") - + well_name + std::string(" will be closed due to economic limit"); - OpmLog::info(msg); - - if (last_connection) { - list_econ_limited.addShutWell(well_name); - const std::string msg2 = well_name + std::string(" will be shut due to the last connection closed"); - OpmLog::info(msg2); - } - } - - } // for (int w = 0; w < nw; ++w) + for (const auto& well : well_container_) { + well->updateListEconLimited(well_state, list_econ_limited); + } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 961ab4485..07c424f5d 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -223,6 +223,9 @@ namespace Opm RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, const WellState& well_state) const; + + void updateListEconLimited(const WellState& well_state, + DynamicListEconLimited& list_econ_limited) const; protected: // to indicate a invalid connection diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 442638989..961961ce5 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -688,4 +688,96 @@ namespace Opm return std::make_tuple(any_limit_violated, last_connection, worst_offending_connection, violation_extent); } + + + + + template + void + WellInterface:: + updateListEconLimited(const WellState& well_state, + DynamicListEconLimited& list_econ_limited) const + { + // economic limits only apply for production wells. + if (wellType() != PRODUCER) { + return; + } + + // flag to check if the mim oil/gas rate limit is violated + bool rate_limit_violated = false; + const WellEconProductionLimits& econ_production_limits = well_ecl_->getEconProductionLimits(current_step_); + + // if no limit is effective here, then continue to the next well + if ( !econ_production_limits.onAnyEffectiveLimit() ) { + return; + } + + const std::string well_name = name_; + + // for the moment, we only handle rate limits, not handling potential limits + // the potential limits should not be difficult to add + const WellEcon::QuantityLimitEnum& quantity_limit = econ_production_limits.quantityLimit(); + if (quantity_limit == WellEcon::POTN) { + const std::string msg = std::string("POTN limit for well ") + well_name + std::string(" is not supported for the moment. \n") + + std::string("All the limits will be evaluated based on RATE. "); + OpmLog::warning("NOT_SUPPORTING_POTN", msg); + } + + if (econ_production_limits.onAnyRateLimit()) { + rate_limit_violated = checkRateEconLimits(econ_production_limits, well_state); + } + + if (rate_limit_violated) { + if (econ_production_limits.endRun()) { + const std::string warning_message = std::string("ending run after well closed due to economic limits is not supported yet \n") + + std::string("the program will keep running after ") + well_name + std::string(" is closed"); + OpmLog::warning("NOT_SUPPORTING_ENDRUN", warning_message); + } + + if (econ_production_limits.validFollowonWell()) { + OpmLog::warning("NOT_SUPPORTING_FOLLOWONWELL", "opening following on well after well closed is not supported yet"); + } + + if (well_ecl_->getAutomaticShutIn()) { + list_econ_limited.addShutWell(well_name); + const std::string msg = std::string("well ") + well_name + std::string(" will be shut in due to economic limit"); + OpmLog::info(msg); + } else { + list_econ_limited.addStoppedWell(well_name); + const std::string msg = std::string("well ") + well_name + std::string(" will be stopped due to economic limit"); + OpmLog::info(msg); + } + // the well is closed, not need to check other limits + return; + } + + // checking for ratio related limits, mostly all kinds of ratio. + bool ratio_limits_violated = false; + RatioCheckTuple ratio_check_return; + + if (econ_production_limits.onAnyRatioLimit()) { + ratio_check_return = checkRatioEconLimits(econ_production_limits, well_state); + ratio_limits_violated = std::get<0>(ratio_check_return); + } + + if (ratio_limits_violated) { + const bool last_connection = std::get<1>(ratio_check_return); + const int worst_offending_connection = std::get<2>(ratio_check_return); + + assert((worst_offending_connection >= 0) && (worst_offending_connection < number_of_perforations_)); + + const int cell_worst_offending_connection = well_cell_[worst_offending_connection]; + list_econ_limited.addClosedConnectionsForWell(well_name, cell_worst_offending_connection); + const std::string msg = std::string("Connection ") + std::to_string(worst_offending_connection) + std::string(" for well ") + + well_name + std::string(" will be closed due to economic limit"); + OpmLog::info(msg); + + if (last_connection) { + list_econ_limited.addShutWell(well_name); + const std::string msg2 = well_name + std::string(" will be shut due to the last connection closed"); + OpmLog::info(msg2); + } + } + } + } From 8ddd197dcff150559b123111b224fc4090d5c19d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 26 Jul 2017 14:17:16 +0200 Subject: [PATCH 057/104] cleaning up StandardWellsDense removing more unused stuff. --- opm/autodiff/StandardWell.hpp | 4 +- opm/autodiff/StandardWellsDense.hpp | 36 -------------- opm/autodiff/StandardWellsDense_impl.hpp | 61 ------------------------ 3 files changed, 3 insertions(+), 98 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 4806ac2a1..83df3027a 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -64,9 +64,11 @@ namespace Opm using typename WellInterface::BVector; using typename WellInterface::Eval; - static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? 3:numEq; // //numEq; //number of wellEq is only for 3 for polymer + static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; + static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; + static const int polymerConcentrationIdx = BlackoilIndices::polymerConcentrationIdx; typedef DenseAd::Evaluation EvalWell; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index b5a082505..4d26eb4bb 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -56,7 +56,6 @@ #include #include -#include #include @@ -65,14 +64,6 @@ namespace Opm { -enum WellVariablePositions { - XvarWell = 0, - WFrac = 1, - GFrac = 2, - SFrac = 3 -}; - - /// Class for handling the standard well model. template class StandardWellsDense { @@ -93,19 +84,13 @@ enum WellVariablePositions { static const int numEq = BlackoilIndices::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer - static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; - static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; - static const int polymerConcentrationIdx = BlackoilIndices::polymerConcentrationIdx; // 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::FieldMatrix MatrixBlockType; - typedef Dune::BCRSMatrix Mat; typedef Dune::BlockVector BVector; - typedef DenseAd::Evaluation Eval; typedef Ewoms::BlackOilPolymerModule PolymerModule; // For the conversion between the surface volume rate and resrevoir voidage rate @@ -156,10 +141,6 @@ enum WellVariablePositions { WellState& well_state, bool only_wells); - void localInvert(Mat& istlA) const; - - void print(Mat& istlA) const; - // substract Binv(D)rw from r; void apply( BVector& r) const; @@ -295,23 +276,6 @@ enum WellVariablePositions { mutable BVector scaleAddRes_; - using WellMapType = typename WellState::WellMapType; - using WellMapEntryType = typename WellState::mapentry_t; - - // a tuple type for ratio limit check. - // first value indicates whether ratio limit is violated, when the ratio limit is not violated, the following three - // values should not be used. - // second value indicates whehter there is only one connection left. - // third value indicates the indx of the worst-offending connection. - // the last value indicates the extent of the violation for the worst-offending connection, which is defined by - // the ratio of the actual value to the value of the violated limit. - using RatioCheckTuple = std::tuple; - - enum ConnectionIndex { - INVALIDCONNECTION = -10000 - }; - - void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; void computeRepRadiusPerfLength(const Grid& grid); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 3bce136aa..7f670407a 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -137,12 +137,7 @@ namespace Opm { OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl "); } - // TODO: The following should not happen, right? const Well* well_ecl = wells_ecl_[index_well]; - if (well_ecl->getStatus(current_timeIdx_) == WellCommon::SHUT) { - continue; - } - if (well_ecl->isMultiSegment(current_timeIdx_)) { OPM_THROW(Opm::NumericalProblem, "Not handling Multisegment Wells for now"); } @@ -217,40 +212,8 @@ namespace Opm { - template - void - StandardWellsDense:: - localInvert(Mat& istlA) const - { - } - - - - - // applying the well residual to reservoir residuals // r = r - duneC_^T * invDuneD_ * resWell_ - // TODO: for this, we should calcuate the duneC_^T * invDuneD_ * resWell_ for each - // well, then sum them up and apply to r finally - // In a more general case, the number of the equations for reservoir and wells can be different, - // we need to think about the possible data types can be faced. - // we do not want to expose the some well related data type even inside the Well Model - template - void - StandardWellsDense:: - print(Mat& istlA) const - { - for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) { - for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) { - std::cout << row.index() << " " << col.index() << "/n \n"<<(*col) << std::endl; - } - } - } - - - - - template void StandardWellsDense:: @@ -263,13 +226,6 @@ namespace Opm { for (auto& well : well_container_) { well->apply(r); } - - /* assert( invDrw_.size() == invDuneD_.N() ); - - // invDrw_ = invDuneD_ * resWell_ - invDuneD_.mv(resWell_,invDrw_); - // r = r - duneC_^T * invDrw_ - duneC_.mmtv(invDrw_, r); */ } @@ -290,19 +246,6 @@ namespace Opm { for (auto& well : well_container_) { well->apply(x, Ax); } - - /* assert( Bx_.size() == duneB_.N() ); - - BVector& invDBx = invDrw_; - assert( invDBx.size() == invDuneD_.N()); - - // Bx_ = duneB_ * x - duneB_.mv(x, Bx_); - // invDBx = invDuneD_ * Bx_ - invDuneD_.mv(Bx_, invDBx); - // Ax = Ax - duneC_^T * invDBx - duneC_.mmtv(invDBx,Ax); - */ } @@ -310,10 +253,6 @@ namespace Opm { // Ax = Ax - alpha * C D^-1 B x - // TODO: for the new Well Model, we will calcuate - // C D^-1 B for each well and sum it up - // while it can be implemented in the function apply() - // then this function does not need to change template void StandardWellsDense:: From af254a8f4d18923a8719c3ab0c93dbef8e7d84f5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 26 Jul 2017 15:27:39 +0200 Subject: [PATCH 058/104] removing some of the using of Wells in StandardWellsDense --- opm/autodiff/StandardWellsDense.hpp | 1 - opm/autodiff/StandardWellsDense_impl.hpp | 31 ++++++++++++------------ opm/autodiff/WellInterface_impl.hpp | 2 -- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 4d26eb4bb..b0cf3e28c 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -83,7 +83,6 @@ namespace Opm { typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; static const int numEq = BlackoilIndices::numEq; - static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; // TODO: where we should put these types, WellInterface or Well Model? diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 7f670407a..ef70793c9 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -61,12 +61,11 @@ namespace Opm { calculateEfficiencyFactors(); - const int nw = wells().number_of_wells; - const int nperf = wells().well_connpos[nw]; + const int nw = number_of_wells_; const int nc = numCells(); #ifndef NDEBUG - const auto pu = phase_usage_; + const auto& pu = phase_usage_; const int np = pu.num_phases; // assumes the gas fractions are stored after water fractions @@ -331,7 +330,7 @@ namespace Opm { StandardWellsDense:: numPhases() const { - return wells().number_of_phases; + return number_of_phases_; } @@ -420,7 +419,7 @@ namespace Opm { StandardWellsDense:: localWellsActive() const { - return wells_ ? (wells_->number_of_wells > 0 ) : false; + return number_of_wells_ > 0; } @@ -462,7 +461,7 @@ namespace Opm { const double dt, WellState& well_state) { - const int nw = wells().number_of_wells; + const int nw = number_of_wells_; WellState well_state0 = well_state; const int numComp = numComponents(); @@ -507,7 +506,7 @@ namespace Opm { well_state = well_state0; // also recover the old well controls for (int w = 0; w < nw; ++w) { - WellControls* wc = wells().ctrls[w]; + WellControls* wc = well_container_[w]->wellControls(); well_controls_set_current(wc, well_state.currentControls()[w]); } } @@ -549,7 +548,7 @@ namespace Opm { return std::vector(); } - const int nw = wells().number_of_wells; + const int nw = number_of_wells_; const int numComp = numComponents(); std::vector res(numEq*nw, 0.0); for( int compIdx = 0; compIdx < numComp; ++compIdx) { @@ -706,7 +705,7 @@ namespace Opm { prepareTimeStep(const Simulator& ebos_simulator, WellState& well_state) { - const int nw = wells().number_of_wells; + const int nw = number_of_wells_; for (int w = 0; w < nw; ++w) { // after restarting, the well_controls can be modified while // the well_state still uses the old control index @@ -714,8 +713,8 @@ namespace Opm { resetWellControlFromState(well_state); if (wellCollection()->groupControlActive()) { - WellControls* wc = wells().ctrls[w]; - WellNode& well_node = well_collection_->findWellNode(std::string(wells().name[w])); + WellControls* wc = well_container_[w]->wellControls(); + WellNode& well_node = well_collection_->findWellNode(well_container_[w]->name()); // handling the situation that wells do not have a valid control // it happens the well specified with GRUP and restarting due to non-convergencing @@ -768,7 +767,7 @@ namespace Opm { // since the controls are all updated, we should update well_state accordingly for (int w = 0; w < nw; ++w) { - WellControls* wc = wells().ctrls[w]; + WellControls* wc = well_container_[w]->wellControls(); const int control = well_controls_get_current(wc); well_state.currentControls()[w] = control; well_container_[w]->updateWellStateWithTarget(control, well_state); @@ -806,7 +805,7 @@ namespace Opm { return; } - const int nw = wells().number_of_wells; + const int nw = number_of_wells_; for (int w = 0; w < nw; ++w) { const std::string well_name = well_container_[w]->name(); @@ -849,7 +848,7 @@ namespace Opm { std::vector convert_coeff(np, 1.0); for (int w = 0; w < nw; ++w) { - const bool is_producer = wells().type[w] == PRODUCER; + const bool is_producer = well_container_[w]->wellType() == PRODUCER; // not sure necessary to change all the value to be positive if (is_producer) { @@ -899,7 +898,7 @@ namespace Opm { const int well_index = well_node->selfIndex(); well_state.currentControls()[well_index] = well_node->groupControlIndex(); - WellControls* wc = wells().ctrls[well_index]; + WellControls* wc = well_container_[well_index]->wellControls(); well_controls_set_current(wc, well_node->groupControlIndex()); } } @@ -975,7 +974,7 @@ namespace Opm { return; } - const int nw = wells().number_of_wells; + const int nw = number_of_wells_; const int nperf = wells().well_connpos[nw]; const size_t timeStep = current_timeIdx_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 961961ce5..82564377d 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -30,8 +30,6 @@ namespace Opm , current_step_(time_step) { - // TODO: trying to use wells struct as little as possible here, be prepared to - // remove the wells struct in future const std::string& well_name = well->name(); // looking for the location of the well in the wells struct From 0acd0ef387699613c131af03747dc00c5701d161 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 26 Jul 2017 15:31:44 +0200 Subject: [PATCH 059/104] small adjustment of member functions of WellInterface --- opm/autodiff/WellInterface.hpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 07c424f5d..d494363a6 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -209,21 +209,6 @@ namespace Opm bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, const WellState& well_state) const; - // a tuple type for ratio limit check. - // first value indicates whether ratio limit is violated, when the ratio limit is not violated, the following three - // values should not be used. - // second value indicates whehter there is only one connection left. - // third value indicates the indx of the worst-offending connection. - // the last value indicates the extent of the violation for the worst-offending connection, which is defined by - // the ratio of the actual value to the value of the violated limit. - using RatioCheckTuple = std::tuple; - - RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state) const; - - RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state) const; - void updateListEconLimited(const WellState& well_state, DynamicListEconLimited& list_econ_limited) const; protected: @@ -296,6 +281,23 @@ namespace Opm bool wellHasTHPConstraints() const; double mostStrictBhpFromBhpLimits() const; + + // a tuple type for ratio limit check. + // first value indicates whether ratio limit is violated, when the ratio limit is not violated, the following three + // values should not be used. + // second value indicates whehter there is only one connection left. + // third value indicates the indx of the worst-offending connection. + // the last value indicates the extent of the violation for the worst-offending connection, which is defined by + // the ratio of the actual value to the value of the violated limit. + using RatioCheckTuple = std::tuple; + + + RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; + + RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; + }; } From 72ca91d59b60ab6f14cd80405570d37ef755e7cc Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 28 Jul 2017 10:08:57 +0200 Subject: [PATCH 060/104] more cleaning up for StandardWellsDense and StandardWell --- opm/autodiff/StandardWell.hpp | 16 ++++++------- opm/autodiff/StandardWellsDense.hpp | 5 ---- opm/autodiff/StandardWellsDense_impl.hpp | 29 ------------------------ 3 files changed, 8 insertions(+), 42 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 83df3027a..b0d2b7680 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -39,13 +39,13 @@ namespace Opm // TODO: several functions related to polymer and PLYSHLOG are not incorprated yet, // like the function wpolymer, setupCompressedToCartesian, computeRepRadiusPerfLength, // They are introduced though PR 1220 and will be included later. - using Simulator = typename WellInterface::Simulator; - using WellState = typename WellInterface::WellState; - using IntensiveQuantities = typename WellInterface::IntensiveQuantities; - using FluidSystem = typename WellInterface::FluidSystem; - using MaterialLaw = typename WellInterface::MaterialLaw; - using ModelParameters = typename WellInterface::ModelParameters; - using BlackoilIndices = typename WellInterface::BlackoilIndices; + using typename WellInterface::Simulator; + using typename WellInterface::WellState; + using typename WellInterface::IntensiveQuantities; + using typename WellInterface::FluidSystem; + using typename WellInterface::MaterialLaw; + using typename WellInterface::ModelParameters; + using typename WellInterface::BlackoilIndices; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -57,13 +57,13 @@ namespace Opm SFrac = 3 }; - using WellInterface::numEq; using typename WellInterface::VectorBlockType; using typename WellInterface::MatrixBlockType; using typename WellInterface::Mat; using typename WellInterface::BVector; using typename WellInterface::Eval; + using WellInterface::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index b0cf3e28c..025eedc84 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -76,10 +76,7 @@ namespace Opm { typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, ElementContext) ElementContext; typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; - typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables; - typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw; typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator; - typedef typename GET_PROP_TYPE(TypeTag, IntensiveQuantities) IntensiveQuantities; typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar; static const int numEq = BlackoilIndices::numEq; @@ -282,8 +279,6 @@ namespace Opm { void computeAverageFormationFactor(Simulator& ebosSimulator, std::vector& B_avg) const; - - void outputWellState(const WellState& well_state) const; }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index ef70793c9..c3ab836f5 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -1116,33 +1116,4 @@ namespace Opm { } - - - template - void - StandardWellsDense:: - outputWellState(const WellState& well_state) const - { - std::cout << " output the bhp " << std::endl; - for (const double bhp : well_state.bhp()) { - std::cout << bhp << " "; - } - std::cout << std::endl; - - std::cout << " output the well rates " << std::endl; - for (const double rate : well_state.wellRates()) { - std::cout << rate << " "; - } - std::cout << std::endl; - - std::cout << " output the wellSolutions " << std::endl; - for (const double solution : well_state.wellSolutions()) { - std::cout << solution << " "; - } - std::cout << std::endl; - } - - - - } // namespace Opm From b3428a8bf96ec12cbfc54b47c8990ee12c3854b2 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 28 Jul 2017 13:49:26 +0200 Subject: [PATCH 061/104] incorporating the solvent related to StandardWell --- opm/autodiff/StandardWell.hpp | 3 +- opm/autodiff/StandardWell_impl.hpp | 46 ++++++++++++++--------------- opm/autodiff/WellInterface_impl.hpp | 3 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b0d2b7680..b6f864e86 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -65,6 +65,7 @@ namespace Opm using WellInterface::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer + // TODO: should these go to WellInterface? static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; @@ -220,7 +221,7 @@ namespace Opm EvalWell getBhp() const; // TODO: it is also possible to be moved to the base class. - EvalWell getQs(const int phase) const; + EvalWell getQs(const int comp_idx) const; // calculate the properties for the well connections // to calulate the pressure difference between well connections. diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index c15c427d6..a194a4c8d 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -222,7 +222,7 @@ namespace Opm template typename StandardWell::EvalWell StandardWell:: - getQs(const int phase) const + getQs(const int comp_idx) const // TODO: phase or component? { EvalWell qs = 0.0; @@ -230,36 +230,35 @@ namespace Opm const int np = numberOfPhases(); const double target_rate = well_controls_get_current_target(wc); - // TODO: we need to introduce numComponents() for StandardWell - // assert(phase < numComponents()); + assert(comp_idx < numComponents()); const auto pu = phaseUsage(); // TODO: the formulation for the injectors decides it only work with single phase // surface rate injection control. Improvement will be required. if (wellType() == INJECTOR) { - // TODO: adding the handling related to solvent - /* if (has_solvent_ ) { + if (has_solvent) { // TODO: investigate whether the use of the comp_frac is justified. double comp_frac = 0.0; - if (has_solvent && compIdx == contiSolventEqIdx) { // solvent - comp_frac = wells().comp_frac[np*wellIdx + pu.phase_pos[ Gas ]] * wsolvent(wellIdx); - } else if (compIdx == pu.phase_pos[ Gas ]) { - comp_frac = wells().comp_frac[np*wellIdx + compIdx] * (1.0 - wsolvent(wellIdx)); + if (has_solvent && comp_idx == contiSolventEqIdx) { // solvent + comp_frac = compFrac()[pu.phase_pos[ Gas ]] * wsolvent(); + } else if (comp_idx == pu.phase_pos[ Gas ]) { + comp_frac = compFrac()[comp_idx] * (1.0 - wsolvent()); } else { - comp_frac = wells().comp_frac[np*wellIdx + compIdx]; + comp_frac = compFrac()[comp_idx]; } if (comp_frac == 0.0) { return qs; //zero } if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return comp_frac * well_variables_[nw*XvarWell + wellIdx]; + return comp_frac * well_variables_[XvarWell]; } qs.setValue(comp_frac * target_rate); return qs; - } */ - const double comp_frac = compFrac()[phase]; + } + + const double comp_frac = compFrac()[comp_idx]; if (comp_frac == 0.0) { return qs; } @@ -273,7 +272,7 @@ namespace Opm // Producers if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { - return well_variables_[XvarWell] * wellVolumeFractionScaled(phase); + return well_variables_[XvarWell] * wellVolumeFractionScaled(comp_idx); } if (well_controls_get_current_type(wc) == SURFACE_RATE) { @@ -305,17 +304,16 @@ namespace Opm assert(phase_under_control >= 0); EvalWell wellVolumeFractionScaledPhaseUnderControl = wellVolumeFractionScaled(phase_under_control); - // TODO: handling solvent related later - /* if (has_solvent_ && phase_under_control == Gas) { + if (has_solvent && phase_under_control == Gas) { // for GRAT controlled wells solvent is included in the target wellVolumeFractionScaledPhaseUnderControl += wellVolumeFractionScaled(contiSolventEqIdx); - } */ + } - if (phase == phase_under_control) { - /* if (has_solvent_ && phase_under_control == Gas) { + if (comp_idx == phase_under_control) { + if (has_solvent && phase_under_control == Gas) { qs.setValue(target_rate * wellVolumeFractionScaled(Gas).value() / wellVolumeFractionScaledPhaseUnderControl.value() ); return qs; - } */ + } qs.setValue(target_rate); return qs; } @@ -325,7 +323,7 @@ namespace Opm if (wellVolumeFractionScaledPhaseUnderControl < eps) { return qs; } - return (target_rate * wellVolumeFractionScaled(phase) / wellVolumeFractionScaledPhaseUnderControl); + return (target_rate * wellVolumeFractionScaled(comp_idx) / wellVolumeFractionScaledPhaseUnderControl); } // when it is a combined two phase rate limit, such like LRAT @@ -337,16 +335,16 @@ namespace Opm combined_volume_fraction += wellVolumeFractionScaled(p); } } - return (target_rate * wellVolumeFractionScaled(phase) / combined_volume_fraction); + return (target_rate * wellVolumeFractionScaled(comp_idx) / combined_volume_fraction); } // TODO: three phase surface rate control is not tested yet if (num_phases_under_rate_control == 3) { - return target_rate * wellSurfaceVolumeFraction(phase); + return target_rate * wellSurfaceVolumeFraction(comp_idx); } } else if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { // ReservoirRate - return target_rate * wellVolumeFractionScaled(phase); + return target_rate * wellVolumeFractionScaled(comp_idx); } else { OPM_THROW(std::logic_error, "Unknown control type for well " << name()); } diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 82564377d..0b60e2430 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -371,6 +371,7 @@ namespace Opm WellInterface:: numComponents() const { + // TODO: how about two phase polymer if (numPhases() == 2) { return 2; } @@ -378,7 +379,7 @@ namespace Opm int numComp = FluidSystem::numComponents; if (has_solvent) { - numComp ++; + numComp++; } return numComp; } From 5ed9f4d49738100e11851b8340df8a9a62120c97 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 28 Jul 2017 14:41:00 +0200 Subject: [PATCH 062/104] incorporting polymer except the parts related to PLYSHLOG --- opm/autodiff/StandardWell.hpp | 5 ++++- opm/autodiff/StandardWell_impl.hpp | 21 +++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b6f864e86..a8797df79 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -153,6 +153,9 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const; + using WellInterface::has_solvent; + using WellInterface::has_polymer; + using WellInterface::phaseUsage; using WellInterface::active; using WellInterface::numberOfPerforations; @@ -170,9 +173,9 @@ namespace Opm using WellInterface::flowPhaseToEbosCompIdx; using WellInterface::numComponents; using WellInterface::numPhases; - using WellInterface::has_solvent; using WellInterface::wellIndex; using WellInterface::wsolvent; + using WellInterface::wpolymer; protected: diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a194a4c8d..a69ad31d5 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -670,12 +670,10 @@ namespace Opm } } - // TODO: will incoporate the following related to polymer later - // which was introduced in PR 1220 - /* if (has_polymer_) { + if (has_polymer) { EvalWell cq_s_poly = cq_s[Water]; if (wellType() == INJECTOR) { - cq_s_poly *= wpolymer(w); + cq_s_poly *= wpolymer(); } else { cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); } @@ -685,7 +683,7 @@ namespace Opm } ebosResid[cell_idx][contiPolymerEqIdx] -= cq_s_poly.value(); } - } */ + } // Store the perforation pressure for later usage. well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; @@ -693,7 +691,6 @@ namespace Opm // add vol * dF/dt + Q to the well equations; for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - // TODO: the F0_ here is not initialized yet here, which should happen in the first iteration, so it should happen in the assemble function EvalWell resWell_loc = (wellSurfaceVolumeFraction(componentIdx) - F0_[componentIdx]) * volume / dt; resWell_loc += getQs(componentIdx); for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { @@ -701,12 +698,10 @@ namespace Opm } resWell_[0][componentIdx] += resWell_loc.value(); - - // TODO: to incoporate the following polymer related later, which was introduced in PR 1220 - /* // add trivial equation for polymer - if (has_polymer_) { - invDuneD_[w][w][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; // - } */ + // add trivial equation for polymer + if (has_polymer) { + invDuneD_[0][0][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; + } } // do the local inversion of D. @@ -763,8 +758,6 @@ namespace Opm const int perf, std::vector& mob) const { - // TODO: not incoporating the PLYSHLOG related for now. - // which is incoporate from PR 1220 and should be included later. const int np = numberOfPhases(); const int cell_idx = wellCells()[perf]; assert (int(mob.size()) == numComponents()); From d470edfb0ba3a31b79abd17c2cc788c72877d095 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 31 Jul 2017 14:20:02 +0200 Subject: [PATCH 063/104] correct an assertion error in getWellConvergence in StandardWell and adding comments why it is the case for polymer case. --- opm/autodiff/StandardWell_impl.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a69ad31d5..13829e2a6 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1705,7 +1705,9 @@ namespace Opm const int np = numberOfPhases(); const int numComp = numComponents(); - assert(int(B_avg.size()) == numComp); + // the following implementation assume that the polymer is always after the w-o-g phases + // For the polymer case, there is one more mass balance equations of reservoir than wells + assert((int(B_avg.size()) == numComp) || has_polymer); const double tol_wells = param.tolerance_wells_; const double maxResidualAllowed = param.max_residual_allowed_; From d1727d018346a934ad4e5d8c167acf16777295ac Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 31 Jul 2017 15:04:25 +0200 Subject: [PATCH 064/104] correcting the injectivity with polymer injection. --- opm/autodiff/StandardWell.hpp | 1 + opm/autodiff/StandardWell_impl.hpp | 44 ++++++++++++++++++++++++++++++ opm/autodiff/WellInterface.hpp | 2 ++ 3 files changed, 47 insertions(+) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index a8797df79..ca7868ea0 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -62,6 +62,7 @@ namespace Opm using typename WellInterface::Mat; using typename WellInterface::BVector; using typename WellInterface::Eval; + using typename WellInterface::PolymerModule; using WellInterface::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 13829e2a6..88b816601 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -797,6 +797,50 @@ namespace Opm OPM_THROW(std::runtime_error, "individual mobility for wells does not work in combination with solvent"); } } + + // modify the water mobility if polymer is present + if (has_polymer) { + // assume fully mixture for wells. + EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); + + if (wellType() == INJECTOR) { + const auto& viscosityMultiplier = PolymerModule::plyviscViscosityMultiplierTable(intQuants.pvtRegionIndex()); + mob[ Water ] /= (extendEval(intQuants.waterViscosityCorrection()) * viscosityMultiplier.eval(polymerConcentration, /*extrapolate=*/true) ); + } + + /* if (PolymerModule::hasPlyshlog()) { + // compute the well water velocity with out shear effects. + const int numComp = numComponents(); + const bool allow_cf = crossFlowAllowed(ebosSimulator); + const EvalWell& bhp = getBhp(); + std::vector cq_s(numComp,0.0); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + double area = 2 * M_PI * wells_rep_radius_[perf] * wells_perf_length_[perf]; + const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); + const auto& scaledDrainageInfo = + materialLawManager->oilWaterScaledEpsInfoDrainage(cell_idx); + const Scalar& Swcr = scaledDrainageInfo.Swcr; + const EvalWell poro = extendEval(intQuants.porosity()); + const EvalWell Sw = extendEval(intQuants.fluidState().saturation(flowPhaseToEbosPhaseIdx(Water))); + // guard against zero porosity and no water + const EvalWell denom = Opm::max( (area * poro * (Sw - Swcr)), 1e-12); + EvalWell waterVelocity = cq_s[ Water ] / denom * extendEval(intQuants.fluidState().invB(flowPhaseToEbosPhaseIdx(Water))); + + if (PolymerModule::hasShrate()) { + // TODO Use the same conversion as for the reservoar equations. + // Need the "permeability" of the well? + // For now use the same formula as in legacy. + waterVelocity *= PolymerModule::shrate( intQuants.pvtRegionIndex() ) / wells_bore_diameter_[perf]; + } + EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); + EvalWell shearFactor = PolymerModule::computeShearFactor(polymerConcentration, + intQuants.pvtRegionIndex(), + waterVelocity); + + // modify the mobility with the shear factor and recompute the well fluxes. + mob[ Water ] /= shearFactor; + } */ + } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index d494363a6..1287ebcae 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -78,6 +78,8 @@ namespace Opm typedef Dune::BlockVector BVector; typedef DenseAd::Evaluation Eval; + typedef Ewoms::BlackOilPolymerModule PolymerModule; + static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent); static const bool has_polymer = GET_PROP_VALUE(TypeTag, EnablePolymer); From d4fa8c06f1aa1af88ac5bdee266f7fdc22f388b0 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 31 Jul 2017 16:42:26 +0200 Subject: [PATCH 065/104] adding computeRepRadiusPerfLength() to WellInterface. --- opm/autodiff/StandardWellsDense_impl.hpp | 97 +----------------------- opm/autodiff/WellInterface.hpp | 13 ++++ opm/autodiff/WellInterface_impl.hpp | 83 ++++++++++++++++++++ 3 files changed, 98 insertions(+), 95 deletions(-) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index c3ab836f5..a4bc2fe1b 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -964,106 +964,13 @@ namespace Opm { // to be fixed later. int number_of_cells = Opm::UgGridHelpers::numCells(grid); const int* global_cell = Opm::UgGridHelpers::globalCell(grid); - const int* cart_dims = Opm::UgGridHelpers::cartDims(grid); - auto cell_to_faces = Opm::UgGridHelpers::cell2Faces(grid); - auto begin_face_centroids = Opm::UgGridHelpers::beginFaceCentroids(grid); - - if (wells_ecl_.size() == 0) { - OPM_MESSAGE("No wells specified in Schedule section, " - "initializing no wells"); - return; - } - - const int nw = number_of_wells_; - const int nperf = wells().well_connpos[nw]; - - const size_t timeStep = current_timeIdx_; - - wells_rep_radius_.clear(); - wells_perf_length_.clear(); - wells_bore_diameter_.clear(); - - wells_rep_radius_.reserve(nperf); - wells_perf_length_.reserve(nperf); - wells_bore_diameter_.reserve(nperf); std::map cartesian_to_compressed; - setupCompressedToCartesian(global_cell, number_of_cells, cartesian_to_compressed); - int well_index = 0; - - for (auto wellIter= wells_ecl_.begin(); wellIter != wells_ecl_.end(); ++wellIter) { - const auto* well = (*wellIter); - - if (well->getStatus(timeStep) == WellCommon::SHUT) { - continue; - } - { // COMPDAT handling - const auto& completionSet = well->getCompletions(timeStep); - for (size_t c=0; c::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); - if (cgit == cartesian_to_compressed.end()) { - OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' - << k << " not found in grid (well = " << well->name() << ')'); - } - int cell = cgit->second; - - { - double radius = 0.5*completion.getDiameter(); - if (radius <= 0.0) { - radius = 0.5*unit::feet; - OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); - } - - const std::array cubical = - WellsManagerDetail::getCubeDim<3>(cell_to_faces, begin_face_centroids, cell); - - WellCompletion::DirectionEnum direction = completion.getDirection(); - - double re; // area equivalent radius of the grid block - double perf_length; // the length of the well perforation - - switch (direction) { - case Opm::WellCompletion::DirectionEnum::X: - re = std::sqrt(cubical[1] * cubical[2] / M_PI); - perf_length = cubical[0]; - break; - case Opm::WellCompletion::DirectionEnum::Y: - re = std::sqrt(cubical[0] * cubical[2] / M_PI); - perf_length = cubical[1]; - break; - case Opm::WellCompletion::DirectionEnum::Z: - re = std::sqrt(cubical[0] * cubical[1] / M_PI); - perf_length = cubical[2]; - break; - default: - OPM_THROW(std::runtime_error, " Dirtecion of well is not supported "); - } - - double repR = std::sqrt(re * radius); - wells_rep_radius_.push_back(repR); - wells_perf_length_.push_back(perf_length); - wells_bore_diameter_.push_back(2. * radius); - } - } else { - if (completion.getState() != WellCompletion::SHUT) { - OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion.getState() ) << " not handled"); - } - } - - } - } - well_index++; + for (const auto& well : well_container_) { + well->computeRepRadiusPerfLength(grid, cartesian_to_compressed); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 1287ebcae..472a5b4b5 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -63,6 +63,7 @@ namespace Opm using WellState = WellStateFullyImplicitBlackoilDense; typedef BlackoilModelParameters ModelParameters; + typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid; typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator; typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem; typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices; @@ -213,6 +214,9 @@ namespace Opm void updateListEconLimited(const WellState& well_state, DynamicListEconLimited& list_econ_limited) const; + + void computeRepRadiusPerfLength(const Grid& grid, const std::map& cartesian_to_compressed); + protected: // to indicate a invalid connection @@ -272,6 +276,15 @@ namespace Opm // saturation table nubmer for each well perforation std::vector saturation_table_number_; + // representative radius of the perforations, used in shear calculation + std::vector perf_rep_radius_; + + // length of the perforations, use in shear calculation + std::vector perf_length_; + + // well bore diameter + std::vector bore_diameters_; + const PhaseUsage* phase_usage_; const std::vector* active_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 0b60e2430..4a837756c 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -779,4 +779,87 @@ namespace Opm } } + + + + + template + void + WellInterface:: + computeRepRadiusPerfLength(const Grid& grid, + const std::map& cartesian_to_compressed) + { + const int* cart_dims = Opm::UgGridHelpers::cartDims(grid); + auto cell_to_faces = Opm::UgGridHelpers::cell2Faces(grid); + auto begin_face_centroids = Opm::UgGridHelpers::beginFaceCentroids(grid); + + const int nperf = numberOfPerforations(); + + perf_rep_radius_.clear(); + perf_length_.clear(); + bore_diameters_.clear(); + + perf_rep_radius_.resize(nperf); + perf_length_.resize(nperf); + bore_diameters_.resize(nperf); + + // COMPDAT handling + const auto& completionSet = well_ecl_->getCompletions(current_step_); + for (size_t c=0; c::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); + if (cgit == cartesian_to_compressed.end()) { + OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' + << k << " not found in grid (well = " << name() << ')'); + } + const int cell = cgit->second; + + { + double radius = 0.5*completion.getDiameter(); + if (radius <= 0.0) { + radius = 0.5*unit::feet; + OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); + } + + const std::array cubical = + WellsManagerDetail::getCubeDim<3>(cell_to_faces, begin_face_centroids, cell); + + WellCompletion::DirectionEnum direction = completion.getDirection(); + + double re; // area equivalent radius of the grid block + double perf_length; // the length of the well perforation + + switch (direction) { + case Opm::WellCompletion::DirectionEnum::X: + re = std::sqrt(cubical[1] * cubical[2] / M_PI); + perf_length = cubical[0]; + break; + case Opm::WellCompletion::DirectionEnum::Y: + re = std::sqrt(cubical[0] * cubical[2] / M_PI); + perf_length = cubical[1]; + break; + case Opm::WellCompletion::DirectionEnum::Z: + re = std::sqrt(cubical[0] * cubical[1] / M_PI); + perf_length = cubical[2]; + break; + default: + OPM_THROW(std::runtime_error, " Dirtecion of well is not supported "); + } + + const double repR = std::sqrt(re * radius); + perf_rep_radius_.push_back(repR); + perf_length_.push_back(perf_length); + bore_diameters_.push_back(2. * radius); + } + } + } + } + } From c1ee941195a19c4dafe185fdc9e5795348b82cf1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 31 Jul 2017 17:11:44 +0200 Subject: [PATCH 066/104] adding shear calculation to the polymer simulation. --- opm/autodiff/StandardWell.hpp | 5 +++++ opm/autodiff/StandardWell_impl.hpp | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index ca7868ea0..591837138 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -57,6 +57,7 @@ namespace Opm SFrac = 3 }; + using typename WellInterface::Scalar; using typename WellInterface::VectorBlockType; using typename WellInterface::MatrixBlockType; using typename WellInterface::Mat; @@ -196,6 +197,10 @@ namespace Opm using WellInterface::perf_depth_; using WellInterface::allow_cf_; + using WellInterface::perf_rep_radius_; + using WellInterface::perf_length_; + using WellInterface::bore_diameters_; + // densities of the fluid in each perforation std::vector perf_densities_; // pressure drop between different perforations diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 88b816601..341705713 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -808,14 +808,15 @@ namespace Opm mob[ Water ] /= (extendEval(intQuants.waterViscosityCorrection()) * viscosityMultiplier.eval(polymerConcentration, /*extrapolate=*/true) ); } - /* if (PolymerModule::hasPlyshlog()) { + if (PolymerModule::hasPlyshlog()) { // compute the well water velocity with out shear effects. const int numComp = numComponents(); const bool allow_cf = crossFlowAllowed(ebosSimulator); const EvalWell& bhp = getBhp(); std::vector cq_s(numComp,0.0); computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); - double area = 2 * M_PI * wells_rep_radius_[perf] * wells_perf_length_[perf]; + // TODO: make area a member + double area = 2 * M_PI * perf_rep_radius_[perf] * perf_length_[perf]; const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); const auto& scaledDrainageInfo = materialLawManager->oilWaterScaledEpsInfoDrainage(cell_idx); @@ -830,7 +831,7 @@ namespace Opm // TODO Use the same conversion as for the reservoar equations. // Need the "permeability" of the well? // For now use the same formula as in legacy. - waterVelocity *= PolymerModule::shrate( intQuants.pvtRegionIndex() ) / wells_bore_diameter_[perf]; + waterVelocity *= PolymerModule::shrate( intQuants.pvtRegionIndex() ) / bore_diameters_[perf]; } EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); EvalWell shearFactor = PolymerModule::computeShearFactor(polymerConcentration, @@ -839,7 +840,7 @@ namespace Opm // modify the mobility with the shear factor and recompute the well fluxes. mob[ Water ] /= shearFactor; - } */ + } } } From 6e0da756dcc4583123efe7f1a4513155846c02fe Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 1 Aug 2017 11:53:56 +0200 Subject: [PATCH 067/104] fixing the running of flow_ebos_2p. by not adding the dummy phase. --- opm/autodiff/StandardWell_impl.hpp | 146 ++++++++++++++--------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 341705713..75a819e85 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -626,84 +626,84 @@ namespace Opm getMobility(ebosSimulator, perf, mob); computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); - for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - // the cq_s entering mass balance equations need to consider the efficiency factors. - const EvalWell cq_s_effective = cq_s[componentIdx] * well_efficiency_factor_; - - if (!only_wells) { - // subtract sum of component fluxes in the reservoir equation. - // need to consider the efficiency factor - ebosResid[cell_idx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.value(); - } - - // subtract sum of phase fluxes in the well equations. - resWell_[0][componentIdx] -= cq_s[componentIdx].value(); - - // assemble the jacobians - for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { - if (!only_wells) { - // also need to consider the efficiency factor when manipulating the jacobians. - duneC_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix - } - invDuneD_[0][0][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); - } - - for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { - if (!only_wells) { - // also need to consider the efficiency factor when manipulating the jacobians. - ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); - duneB_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); - } - } - - // add trivial equation for 2p cases (Only support water + oil) - if (numComp == 2) { - assert(!active()[ Gas ]); - invDuneD_[0][0][Gas][Gas] = 1.0; - } - - // Store the perforation phase flux for later usage. - if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) - well_state.perfRateSolvent()[first_perf_ + perf] = cq_s[componentIdx].value(); - } else { - well_state.perfPhaseRates()[(first_perf_ + perf) * np + componentIdx] = cq_s[componentIdx].value(); - } - } - - if (has_polymer) { - EvalWell cq_s_poly = cq_s[Water]; - if (wellType() == INJECTOR) { - cq_s_poly *= wpolymer(); - } else { - cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); - } - if (!only_wells) { - for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { - ebosJac[cell_idx][cell_idx][contiPolymerEqIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_poly.derivative(pvIdx); - } - ebosResid[cell_idx][contiPolymerEqIdx] -= cq_s_poly.value(); - } - } - - // Store the perforation pressure for later usage. - well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; - } - - // add vol * dF/dt + Q to the well equations; for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { - EvalWell resWell_loc = (wellSurfaceVolumeFraction(componentIdx) - F0_[componentIdx]) * volume / dt; - resWell_loc += getQs(componentIdx); - for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { - invDuneD_[0][0][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); - } - resWell_[0][componentIdx] += resWell_loc.value(); + // the cq_s entering mass balance equations need to consider the efficiency factors. + const EvalWell cq_s_effective = cq_s[componentIdx] * well_efficiency_factor_; - // add trivial equation for polymer - if (has_polymer) { - invDuneD_[0][0][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; + if (!only_wells) { + // subtract sum of component fluxes in the reservoir equation. + // need to consider the efficiency factor + ebosResid[cell_idx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.value(); + } + + // subtract sum of phase fluxes in the well equations. + resWell_[0][componentIdx] -= cq_s[componentIdx].value(); + + // assemble the jacobians + for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { + if (!only_wells) { + // also need to consider the efficiency factor when manipulating the jacobians. + duneC_[0][cell_idx][pvIdx][flowPhaseToEbosCompIdx(componentIdx)] -= cq_s_effective.derivative(pvIdx+numEq); // intput in transformed matrix + } + invDuneD_[0][0][componentIdx][pvIdx] -= cq_s[componentIdx].derivative(pvIdx+numEq); + } + + for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { + if (!only_wells) { + // also need to consider the efficiency factor when manipulating the jacobians. + ebosJac[cell_idx][cell_idx][flowPhaseToEbosCompIdx(componentIdx)][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + duneB_[0][cell_idx][componentIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_effective.derivative(pvIdx); + } + } + + // add a trivial equation for the dummy phase for 2p cases (Only support water + oil) + if ( numComp < numWellEq ) { + assert(!active()[ Gas ]); + invDuneD_[0][0][Gas][Gas] = 1.0; + } + + // Store the perforation phase flux for later usage. + if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) + well_state.perfRateSolvent()[first_perf_ + perf] = cq_s[componentIdx].value(); + } else { + well_state.perfPhaseRates()[(first_perf_ + perf) * np + componentIdx] = cq_s[componentIdx].value(); } } + if (has_polymer) { + EvalWell cq_s_poly = cq_s[Water]; + if (wellType() == INJECTOR) { + cq_s_poly *= wpolymer(); + } else { + cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); + } + if (!only_wells) { + for (int pvIdx = 0; pvIdx < numEq; ++pvIdx) { + ebosJac[cell_idx][cell_idx][contiPolymerEqIdx][flowToEbosPvIdx(pvIdx)] -= cq_s_poly.derivative(pvIdx); + } + ebosResid[cell_idx][contiPolymerEqIdx] -= cq_s_poly.value(); + } + } + + // Store the perforation pressure for later usage. + well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; + } + + // add vol * dF/dt + Q to the well equations; + for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { + EvalWell resWell_loc = (wellSurfaceVolumeFraction(componentIdx) - F0_[componentIdx]) * volume / dt; + resWell_loc += getQs(componentIdx); + for (int pvIdx = 0; pvIdx < numWellEq; ++pvIdx) { + invDuneD_[0][0][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); + } + resWell_[0][componentIdx] += resWell_loc.value(); + + // add trivial equation for polymer + if (has_polymer) { + invDuneD_[0][0][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; + } + } + // do the local inversion of D. localInvert( invDuneD_ ); } From 4f716a633dfea712bce13fb1bd8094a9094b2e8d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 1 Aug 2017 16:11:55 +0200 Subject: [PATCH 068/104] using legacyDepth_ for the perfoation depth for StandardWell. It turns out that it can be different with the grid.getCellDepth() from the parser. --- opm/autodiff/StandardWell.hpp | 1 + opm/autodiff/StandardWell_impl.hpp | 9 ++++++++- opm/autodiff/StandardWellsDense_impl.hpp | 2 +- opm/autodiff/WellInterface.hpp | 2 ++ opm/autodiff/WellInterface_impl.hpp | 11 +---------- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 591837138..8bd8c8457 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -116,6 +116,7 @@ namespace Opm virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, + const std::vector& depth_arg, const double gravity_arg, const int num_cells); diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 75a819e85..aecd31dcc 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -45,12 +45,19 @@ namespace Opm init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, + const std::vector& depth_arg, const double gravity_arg, const int num_cells) { WellInterface::init(phase_usage_arg, active_arg, - vfp_properties_arg, gravity_arg, num_cells); + vfp_properties_arg, depth_arg, + gravity_arg, num_cells); + perf_depth_.resize(numberOfPerforations(), 0.); + for (int perf = 0; perf < numberOfPerforations(); ++perf) { + const int cell_idx = wellCells()[perf]; + perf_depth_[perf] = depth_arg[cell_idx]; + } // setup sparsity pattern for the matrices //[A C^T [x = [ res diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index a4bc2fe1b..bc5a510e3 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -84,7 +84,7 @@ namespace Opm { // TODO: to see whether we can postpone of the intialization of the well containers to // optimize the usage of the following several member variables for (auto& well : well_container_) { - well->init(&phase_usage_, &active_, vfp_properties_, gravity_, nc); + well->init(&phase_usage_, &active_, vfp_properties_, depth_arg, gravity_, nc); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 472a5b4b5..ce2b01064 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -134,6 +134,7 @@ namespace Opm virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, + const std::vector& depth_arg, const double gravity_arg, const int num_cells); @@ -262,6 +263,7 @@ namespace Opm // well index for each perforation std::vector well_index_; + // TODO: it might should go to StandardWell // depth for each perforation std::vector perf_depth_; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 4a837756c..5bb9d76d4 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -83,16 +83,6 @@ namespace Opm std::copy(wells->sat_table_id + perf_index_begin, wells->sat_table_id + perf_index_end, saturation_table_number_.begin() ); - - - // TODO: not sure about the processing of depth for perforations here - // Will revisit here later. There are different ways and the definition for different wells - // can be different, it is possible that we need to remove this from the WellInterface - perf_depth_.resize(number_of_perforations_, 0.); - const auto& completion_set = well->getCompletions(time_step); - for (int i = 0; i < number_of_perforations_; ++i) { - perf_depth_[i] = completion_set.get(i).getCenterDepth(); - } } well_efficiency_factor_ = 1.0; @@ -108,6 +98,7 @@ namespace Opm init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, + const std::vector& /* depth_arg */, const double gravity_arg, const int /* num_cells */) { From bd84e3c9f3a3c06cc11f2abe8ee56b428c4e6f10 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 2 Aug 2017 15:14:54 +0200 Subject: [PATCH 069/104] making a trival way to reduce the covergence of the wells. to be improved later. --- opm/autodiff/StandardWellsDense_impl.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index bc5a510e3..2d2eeeb41 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -583,6 +583,17 @@ namespace Opm { } } + { + const auto& grid = ebosSimulator.gridManager().grid(); + int value = 0; + if (converged_well) { + value = 1; + } + if (grid.comm().min(value) < 1) { + converged_well = false; + } + } + // TODO: to think about the output here. /* if ( terminal_output_ ) { From f19dd03387abb83e03afe8b79ef4d880872b17d6 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 2 Aug 2017 16:19:27 +0200 Subject: [PATCH 070/104] using Base to represent WellInterface for better readability. --- opm/autodiff/StandardWell.hpp | 104 ++++++++++++++--------------- opm/autodiff/StandardWell_impl.hpp | 9 +-- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 8bd8c8457..b8d46c19a 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -33,19 +33,17 @@ namespace Opm { public: + typedef WellInterface Base; // TODO: some functions working with AD variables handles only with values (double) without // dealing with derivatives. It can be beneficial to make functions can work with either AD or scalar value. // And also, it can also be beneficial to make these functions hanle different types of AD variables. - // TODO: several functions related to polymer and PLYSHLOG are not incorprated yet, - // like the function wpolymer, setupCompressedToCartesian, computeRepRadiusPerfLength, - // They are introduced though PR 1220 and will be included later. - using typename WellInterface::Simulator; - using typename WellInterface::WellState; - using typename WellInterface::IntensiveQuantities; - using typename WellInterface::FluidSystem; - using typename WellInterface::MaterialLaw; - using typename WellInterface::ModelParameters; - using typename WellInterface::BlackoilIndices; + using typename Base::Simulator; + using typename Base::WellState; + using typename Base::IntensiveQuantities; + using typename Base::FluidSystem; + using typename Base::MaterialLaw; + using typename Base::ModelParameters; + using typename Base::BlackoilIndices; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -57,15 +55,15 @@ namespace Opm SFrac = 3 }; - using typename WellInterface::Scalar; - using typename WellInterface::VectorBlockType; - using typename WellInterface::MatrixBlockType; - using typename WellInterface::Mat; - using typename WellInterface::BVector; - using typename WellInterface::Eval; - using typename WellInterface::PolymerModule; + using typename Base::Scalar; + using typename Base::VectorBlockType; + using typename Base::MatrixBlockType; + using typename Base::Mat; + using typename Base::BVector; + using typename Base::Eval; + using typename Base::PolymerModule; - using WellInterface::numEq; + using Base::numEq; static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer // TODO: should these go to WellInterface? static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; @@ -156,29 +154,29 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const; - using WellInterface::has_solvent; - using WellInterface::has_polymer; + using Base::has_solvent; + using Base::has_polymer; - using WellInterface::phaseUsage; - using WellInterface::active; - using WellInterface::numberOfPerforations; - using WellInterface::wellCells; - using WellInterface::saturationTableNumber; - using WellInterface::indexOfWell; - using WellInterface::name; - using WellInterface::wellType; - using WellInterface::wellControls; - using WellInterface::compFrac; - using WellInterface::numberOfPhases; - using WellInterface::perfDepth; - using WellInterface::flowToEbosPvIdx; - using WellInterface::flowPhaseToEbosPhaseIdx; - using WellInterface::flowPhaseToEbosCompIdx; - using WellInterface::numComponents; - using WellInterface::numPhases; - using WellInterface::wellIndex; - using WellInterface::wsolvent; - using WellInterface::wpolymer; + using Base::phaseUsage; + using Base::active; + using Base::numberOfPerforations; + using Base::wellCells; + using Base::saturationTableNumber; + using Base::indexOfWell; + using Base::name; + using Base::wellType; + using Base::wellControls; + using Base::compFrac; + using Base::numberOfPhases; + using Base::perfDepth; + using Base::flowToEbosPvIdx; + using Base::flowPhaseToEbosPhaseIdx; + using Base::flowPhaseToEbosCompIdx; + using Base::numComponents; + using Base::numPhases; + using Base::wellIndex; + using Base::wsolvent; + using Base::wpolymer; protected: @@ -189,18 +187,18 @@ namespace Opm void recoverSolutionWell(const BVector& x, BVector& xw) const; // TODO: decide wether to use member function to refer to private member later - using WellInterface::vfp_properties_; - using WellInterface::gravity_; - using WellInterface::well_efficiency_factor_; - using WellInterface::phase_usage_; - using WellInterface::first_perf_; - using WellInterface::ref_depth_; - using WellInterface::perf_depth_; - using WellInterface::allow_cf_; + using Base::vfp_properties_; + using Base::gravity_; + using Base::well_efficiency_factor_; + using Base::phase_usage_; + using Base::first_perf_; + using Base::ref_depth_; + using Base::perf_depth_; + using Base::allow_cf_; - using WellInterface::perf_rep_radius_; - using WellInterface::perf_length_; - using WellInterface::bore_diameters_; + using Base::perf_rep_radius_; + using Base::perf_length_; + using Base::bore_diameters_; // densities of the fluid in each perforation std::vector perf_densities_; @@ -262,8 +260,8 @@ namespace Opm const ModelParameters& param, WellState& well_state); - using WellInterface::wellHasTHPConstraints; - using WellInterface::mostStrictBhpFromBhpLimits; + using Base::wellHasTHPConstraints; + using Base::mostStrictBhpFromBhpLimits; // TODO: maybe we should provide a light version of computeWellFlux, which does not include the // calculation of the derivatives diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index aecd31dcc..24402140d 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -24,7 +24,7 @@ namespace Opm template StandardWell:: StandardWell(const Well* well, const int time_step, const Wells* wells) - : WellInterface(well, time_step, wells) + : Base(well, time_step, wells) , perf_densities_(numberOfPerforations()) , perf_pressure_diffs_(numberOfPerforations()) , well_variables_(numWellEq) // the number of the primary variables @@ -49,9 +49,9 @@ namespace Opm const double gravity_arg, const int num_cells) { - WellInterface::init(phase_usage_arg, active_arg, - vfp_properties_arg, depth_arg, - gravity_arg, num_cells); + Base::init(phase_usage_arg, active_arg, + vfp_properties_arg, depth_arg, + gravity_arg, num_cells); perf_depth_.resize(numberOfPerforations(), 0.); for (int perf = 0; perf < numberOfPerforations(); ++perf) { @@ -245,6 +245,7 @@ namespace Opm if (wellType() == INJECTOR) { if (has_solvent) { // TODO: investigate whether the use of the comp_frac is justified. + // The usage of the comp_frac is not correct, which should be changed later. double comp_frac = 0.0; if (has_solvent && comp_idx == contiSolventEqIdx) { // solvent comp_frac = compFrac()[pu.phase_pos[ Gas ]] * wsolvent(); From 894529be57484096b1b3305d6747b8fceb3c3f47 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 2 Aug 2017 16:30:29 +0200 Subject: [PATCH 071/104] small cleaning up. --- opm/autodiff/StandardWell_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 24402140d..384a403a8 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -2077,7 +2077,7 @@ namespace Opm // Calculating the BHP value based on THP // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. - const double rho = 0.; // perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface + const double rho = perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface const double well_ref_depth = perf_depth_[0]; if (wellType() == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); From 3e26a8b467069989e98ae1589cbe9205b241567e Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 3 Aug 2017 15:14:36 +0200 Subject: [PATCH 072/104] using different size of block size of well and reservoir --- opm/autodiff/StandardWell.hpp | 46 +++++++++++++++++++---------- opm/autodiff/StandardWell_impl.hpp | 32 ++++++++++---------- opm/autodiff/WellInterface_impl.hpp | 2 +- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b8d46c19a..ea9ed2c80 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -44,6 +44,7 @@ namespace Opm using typename Base::MaterialLaw; using typename Base::ModelParameters; using typename Base::BlackoilIndices; + using typename Base::PolymerModule; // the positions of the primary variables for StandardWell // there are three primary variables, the second and the third ones are F_w and F_g @@ -56,22 +57,37 @@ namespace Opm }; using typename Base::Scalar; - using typename Base::VectorBlockType; - using typename Base::MatrixBlockType; + using Base::numEq; + // TODO: with flow_ebos,for a 2P deck, // TODO: for the 2p deck, numEq will be 3, a dummy phase is already added from the reservoir side. + // it will cause problem here without processing the dummy phase. + static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // number of wellEq is only numEq - 1 for polymer using typename Base::Mat; using typename Base::BVector; using typename Base::Eval; - using typename Base::PolymerModule; - using Base::numEq; - static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // //numEq; //number of wellEq is only numEq for polymer + typedef Dune::FieldVector VectorBlockWellType; + typedef Dune::BlockVector BVectorWell; + + // sparsity pattern for the matrices + //[A C^T [x = [ res + // B D ] x_well] res_well] + + // the matrix type for the diagonal matrix D + typedef Dune::FieldMatrix DiagMatrixBlockWellType; + typedef Dune::BCRSMatrix DiagMatWell; + + // the matrix type for the non-diagonal matrix B and C^T + typedef Dune::FieldMatrix OffDiagMatrixBlockWellType; + typedef Dune::BCRSMatrix OffDiagMatWell; + + typedef DenseAd::Evaluation EvalWell; + // TODO: should these go to WellInterface? static const int contiSolventEqIdx = BlackoilIndices::contiSolventEqIdx; static const int contiPolymerEqIdx = BlackoilIndices::contiPolymerEqIdx; static const int solventSaturationIdx = BlackoilIndices::solventSaturationIdx; static const int polymerConcentrationIdx = BlackoilIndices::polymerConcentrationIdx; - typedef DenseAd::Evaluation EvalWell; StandardWell(const Well* well, const int time_step, const Wells* wells); @@ -119,7 +135,7 @@ namespace Opm const int num_cells); // Update the well_state based on solution - void updateWellState(const BVector& dwells, + void updateWellState(const BVectorWell& dwells, const BlackoilModelParameters& param, WellState& well_state) const; @@ -181,10 +197,10 @@ namespace Opm protected: // TODO: maybe this function can go to some helper file. - void localInvert(Mat& istlA) const; + void localInvert(DiagMatWell& istlA) const; // xw = inv(D)*(rw - C*x) - void recoverSolutionWell(const BVector& x, BVector& xw) const; + void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; // TODO: decide wether to use member function to refer to private member later using Base::vfp_properties_; @@ -208,17 +224,17 @@ namespace Opm // TODO: probably, they should be moved to the WellInterface, when // we decide the template paramters. // two off-diagonal matrices - Mat duneB_; - Mat duneC_; + OffDiagMatWell duneB_; + OffDiagMatWell duneC_; // diagonal matrix for the well - Mat invDuneD_; + DiagMatWell invDuneD_; // several vector used in the matrix calculation - mutable BVector Bx_; - mutable BVector invDrw_; + mutable BVectorWell Bx_; + mutable BVectorWell invDrw_; mutable BVector scaleAddRes_; - BVector resWell_; + BVectorWell resWell_; std::vector well_variables_; std::vector F0_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 384a403a8..dcaac8915 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -30,9 +30,9 @@ namespace Opm , well_variables_(numWellEq) // the number of the primary variables , F0_(numWellEq) { - duneB_.setBuildMode( Mat::row_wise ); - duneC_.setBuildMode( Mat::row_wise ); - invDuneD_.setBuildMode( Mat::row_wise ); + duneB_.setBuildMode( OffDiagMatWell::row_wise ); + duneC_.setBuildMode( OffDiagMatWell::row_wise ); + invDuneD_.setBuildMode( DiagMatWell::row_wise ); } @@ -152,9 +152,7 @@ namespace Opm setWellVariables(const WellState& well_state) { const int nw = well_state.bhp().size(); - // for two-phase numComp < numWellEq - const int numComp = numComponents(); - for (int eqIdx = 0; eqIdx < numComp; ++eqIdx) { + for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { const unsigned int idx = nw * eqIdx + indexOfWell(); assert( eqIdx < well_variables_.size() ); assert( idx < well_state.wellSolutions().size() ); @@ -665,10 +663,10 @@ namespace Opm } // add a trivial equation for the dummy phase for 2p cases (Only support water + oil) - if ( numComp < numWellEq ) { + /* if ( numComp < numWellEq ) { assert(!active()[ Gas ]); invDuneD_[0][0][Gas][Gas] = 1.0; - } + } */ // Store the perforation phase flux for later usage. if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) @@ -707,9 +705,9 @@ namespace Opm resWell_[0][componentIdx] += resWell_loc.value(); // add trivial equation for polymer - if (has_polymer) { + /* if (has_polymer) { invDuneD_[0][0][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; - } + } */ } // do the local inversion of D. @@ -859,7 +857,7 @@ namespace Opm template void StandardWell:: - updateWellState(const BVector& dwells, + updateWellState(const BVectorWell& dwells, const BlackoilModelParameters& param, WellState& well_state) const { @@ -1173,7 +1171,7 @@ namespace Opm template void StandardWell:: - localInvert(Mat& istlA) const + localInvert(DiagMatWell& istlA) const { for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) { for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) { @@ -1890,7 +1888,7 @@ namespace Opm { // We assemble the well equations, then we check the convergence, // which is why we do not put the assembleWellEq here. - BVector dx_well(1); + BVectorWell dx_well(1); invDuneD_.mv(resWell_, dx_well); updateWellState(dx_well, param, well_state); @@ -1927,7 +1925,7 @@ namespace Opm // invDBx = invDuneD_ * Bx_ // TODO: with this, we modified the content of the invDrw_. // Is it necessary to do this to save some memory? - BVector& invDBx = invDrw_; + BVectorWell& invDBx = invDrw_; invDuneD_.mv(Bx_, invDBx); // Ax = Ax - duneC_^T * invDBx @@ -1957,9 +1955,9 @@ namespace Opm template void StandardWell:: - recoverSolutionWell(const BVector& x, BVector& xw) const + recoverSolutionWell(const BVector& x, BVectorWell& xw) const { - BVector resWell = resWell_; + BVectorWell resWell = resWell_; // resWell = resWell - B * x duneB_.mmv(x, resWell); // xw = D^-1 * resWell @@ -1977,7 +1975,7 @@ namespace Opm const ModelParameters& param, WellState& well_state) const { - BVector xw(1); + BVectorWell xw(1); recoverSolutionWell(x, xw); updateWellState(xw, param, well_state); } diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 5bb9d76d4..39bd6c8e6 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -364,7 +364,7 @@ namespace Opm { // TODO: how about two phase polymer if (numPhases() == 2) { - return 2; + return 2; } int numComp = FluidSystem::numComponents; From 6bbbe5061de3c0c0c93002d4f6852dc2ac8fd6a5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 3 Aug 2017 16:45:59 +0200 Subject: [PATCH 073/104] adding the copyright information. --- opm/autodiff/StandardWell.hpp | 3 ++- opm/autodiff/StandardWell_impl.hpp | 3 ++- opm/autodiff/WellInterface.hpp | 2 +- opm/autodiff/WellInterface_impl.hpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index ea9ed2c80..b06e27c90 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -1,6 +1,7 @@ /* - Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. Copyright 2017 Statoil ASA. + Copyright 2016 - 2017 IRIS AS. This file is part of the Open Porous Media project (OPM). diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index dcaac8915..40ec2de31 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1,6 +1,7 @@ /* - Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. Copyright 2017 Statoil ASA. + Copyright 2016 - 2017 IRIS AS. This file is part of the Open Porous Media project (OPM). diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index ce2b01064..ef836b772 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -1,5 +1,5 @@ /* - Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. Copyright 2017 Statoil ASA. This file is part of the Open Porous Media project (OPM). diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 39bd6c8e6..8a912c021 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -1,5 +1,5 @@ /* - Copyright 2017 SINTEF ICT, Applied Mathematics. + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. Copyright 2017 Statoil ASA. This file is part of the Open Porous Media project (OPM). From c43505d007f99fe7bb25f5064a03079e73d6cebb Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 3 Aug 2017 17:30:47 +0200 Subject: [PATCH 074/104] cleaning up some interface in WellInterface. --- opm/autodiff/StandardWell.hpp | 8 -- opm/autodiff/StandardWell_impl.hpp | 58 +----------- opm/autodiff/WellInterface.hpp | 145 +++++++++++++---------------- 3 files changed, 70 insertions(+), 141 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b06e27c90..ae3b1dd1d 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -92,14 +92,6 @@ namespace Opm StandardWell(const Well* well, const int time_step, const Wells* wells); - /// the densities of the fluid in each perforation - virtual const std::vector& perfDensities() const; - virtual std::vector& perfDensities(); - - /// the pressure difference between different perforations - virtual const std::vector& perfPressureDiffs() const; - virtual std::vector& perfPressureDiffs(); - virtual void setWellVariables(const WellState& well_state); EvalWell wellVolumeFractionScaled(const int phase) const; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 40ec2de31..e68567b03 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -100,54 +100,6 @@ namespace Opm - template - const std::vector& - StandardWell:: - perfDensities() const - { - return perf_densities_; - } - - - - - - template - std::vector& - StandardWell:: - perfDensities() - { - return perf_densities_; - } - - - - - - template - const std::vector& - StandardWell:: - perfPressureDiffs() const - { - return perf_pressure_diffs_; - } - - - - - - template - std::vector& - StandardWell:: - perfPressureDiffs() - { - return perf_pressure_diffs_; - } - - - - - template void StandardWell:: setWellVariables(const WellState& well_state) @@ -631,7 +583,7 @@ namespace Opm std::vector cq_s(numComp,0.0); std::vector mob(numComp, 0.0); getMobility(ebosSimulator, perf, mob); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { // the cq_s entering mass balance equations need to consider the efficiency factors. @@ -693,7 +645,7 @@ namespace Opm } // Store the perforation pressure for later usage. - well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perfPressureDiffs()[perf]; + well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perf_pressure_diffs_[perf]; } // add vol * dF/dt + Q to the well equations; @@ -740,7 +692,7 @@ namespace Opm EvalWell bhp = getBhp(); // Pressure drawdown (also used to determine direction of flow) - EvalWell well_pressure = bhp + perfPressureDiffs()[perf]; + EvalWell well_pressure = bhp + perf_pressure_diffs_[perf]; EvalWell drawdown = pressure - well_pressure; if (drawdown.value() < 0 && wellType() == INJECTOR) { @@ -821,7 +773,7 @@ namespace Opm const bool allow_cf = crossFlowAllowed(ebosSimulator); const EvalWell& bhp = getBhp(); std::vector cq_s(numComp,0.0); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); // TODO: make area a member double area = 2 * M_PI * perf_rep_radius_[perf] * perf_length_[perf]; const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); @@ -2005,7 +1957,7 @@ namespace Opm std::vector cq_s(numComp, 0.0); std::vector mob(numComp, 0.0); getMobility(ebosSimulator, perf, mob); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perfPressureDiffs()[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); for(int p = 0; p < np; ++p) { well_flux[p] += cq_s[p].value(); diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index ef836b772..cf253cb1c 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -85,52 +85,23 @@ namespace Opm static const bool has_polymer = GET_PROP_VALUE(TypeTag, EnablePolymer); /// Constructor - // TODO: Well can be reference. WellInterface(const Well* well, const int time_step, const Wells* wells); /// Well name. const std::string& name() const; - /// The index of the well in Wells struct - // It is used to locate the inforation in Wells and also WellState for now. - int indexOfWell() const; - /// Well type, INJECTOR or PRODUCER. WellType wellType() const; - /// number of phases - int numberOfPhases() const; - - /// Component fractions for each phase for the well - const std::vector& compFrac() const; - /// Well controls - // TODO: later to see whether we need to return const. WellControls* wellControls() const; /// Number of the perforations int numberOfPerforations() const; - /// Well productivity index for each perforation. - const std::vector& wellIndex() const; - /// Depth of perforations const std::vector& perfDepth() const; - /// Indices of the grid cells/blocks that perforations are completed within. - const std::vector& wellCells() const; - - // TODO: the following function should be able to be removed by refactoring the well class - // It is probably only needed for StandardWell - /// the densities of the fluid in each perforation - virtual const std::vector& perfDensities() const = 0; - virtual std::vector& perfDensities() = 0; - - /// the pressure difference between different perforations - virtual const std::vector& perfPressureDiffs() const = 0; - virtual std::vector& perfPressureDiffs() = 0; - - // TODO: the parameters need to be optimized/adjusted virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, @@ -138,9 +109,59 @@ namespace Opm const double gravity_arg, const int num_cells); - // TODO: temporary virtual void setWellVariables(const WellState& well_state) = 0; + virtual bool getWellConvergence(Simulator& ebosSimulator, + const std::vector& B_avg, + const ModelParameters& param) const = 0; + + virtual void wellEqIteration(Simulator& ebosSimulator, + const ModelParameters& param, + WellState& well_state) = 0; + + virtual void assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells) = 0; + + void updateListEconLimited(const WellState& well_state, + DynamicListEconLimited& list_econ_limited) const; + + void setWellEfficiencyFactor(const double efficiency_factor); + + void computeRepRadiusPerfLength(const Grid& grid, const std::map& cartesian_to_compressed); + + // using the solution x to recover the solution xw for wells and applying + // xw to update Well State + virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, + WellState& well_state) const = 0; + + // Ax = Ax - C D^-1 B x + virtual void apply(const BVector& x, BVector& Ax) const = 0; + + // r = r - C D^-1 Rw + virtual void apply(BVector& r) const = 0; + + virtual void computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const = 0; + + virtual void computeAccumWell() = 0; + + // TODO: it should come with a different name + // for MS well, the definition is different and should not use this name anymore + virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw) = 0; + + virtual void updateWellStateWithTarget(const int current, + WellState& xw) const = 0; + + virtual void updateWellControl(WellState& xw) const = 0; + + protected: + // Indices of the grid cells/blocks that perforations are completed within. + const std::vector& wellCells() const; + const std::vector& active() const; const PhaseUsage& phaseUsage() const; @@ -153,6 +174,10 @@ namespace Opm int numPhases() const; + // number of phases + int numberOfPhases() const; + + // TODO: it is dumplicated with StandardWellsDense int numComponents() const; // simply returning allow_cf_ @@ -168,58 +193,9 @@ namespace Opm double wpolymer() const; - virtual bool getWellConvergence(Simulator& ebosSimulator, - const std::vector& B_avg, - const ModelParameters& param) const = 0; - - virtual void wellEqIteration(Simulator& ebosSimulator, - const ModelParameters& param, - WellState& well_state) = 0; - - virtual void assembleWellEq(Simulator& ebosSimulator, - const double dt, - WellState& well_state, - bool only_wells) = 0; - - virtual void updateWellStateWithTarget(const int current, - WellState& xw) const = 0; - - virtual void updateWellControl(WellState& xw) const = 0; - - virtual void computeAccumWell() = 0; - - // TODO: it should come with a different name - // for MS well, the definition is different and should not use this name anymore - virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, - const WellState& xw) = 0; - - // Ax = Ax - C D^-1 B x - virtual void apply(const BVector& x, BVector& Ax) const = 0; - - // r = r - C D^-1 Rw - virtual void apply(BVector& r) const = 0; - - // using the solution x to recover the solution xw for wells and applying - // xw to update Well State - virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, - WellState& well_state) const = 0; - - virtual void computeWellPotentials(const Simulator& ebosSimulator, - const WellState& well_state, - std::vector& well_potentials) const = 0; - - void setWellEfficiencyFactor(const double efficiency_factor); - bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, const WellState& well_state) const; - void updateListEconLimited(const WellState& well_state, - DynamicListEconLimited& list_econ_limited) const; - - void computeRepRadiusPerfLength(const Grid& grid, const std::map& cartesian_to_compressed); - - protected: - // to indicate a invalid connection static const int INVALIDCONNECTION = -100000; @@ -297,6 +273,16 @@ namespace Opm bool wellHasTHPConstraints() const; + // The index of the well in Wells struct + // It is used to locate the inforation in Wells and also WellState for now. + int indexOfWell() const; + + // Component fractions for each phase for the well + const std::vector& compFrac() const; + + /// Well productivity index for each perforation. + const std::vector& wellIndex() const; + double mostStrictBhpFromBhpLimits() const; // a tuple type for ratio limit check. @@ -308,7 +294,6 @@ namespace Opm // the ratio of the actual value to the value of the violated limit. using RatioCheckTuple = std::tuple; - RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits, const WellState& well_state) const; From d4f26ad47aea4244c6cdb70fdc2f791ee1892821 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 4 Aug 2017 10:06:37 +0200 Subject: [PATCH 075/104] recovering the running of flow_ebos for 2p + dummy phase. --- opm/autodiff/StandardWell.hpp | 7 ++++--- opm/autodiff/StandardWell_impl.hpp | 14 ++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index ae3b1dd1d..ee4547b5c 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -66,13 +66,14 @@ namespace Opm using typename Base::BVector; using typename Base::Eval; - typedef Dune::FieldVector VectorBlockWellType; - typedef Dune::BlockVector BVectorWell; - // sparsity pattern for the matrices //[A C^T [x = [ res // B D ] x_well] res_well] + // the vector type for the res_well and x_well + typedef Dune::FieldVector VectorBlockWellType; + typedef Dune::BlockVector BVectorWell; + // the matrix type for the diagonal matrix D typedef Dune::FieldMatrix DiagMatrixBlockWellType; typedef Dune::BCRSMatrix DiagMatWell; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index e68567b03..1652782d3 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -105,7 +105,10 @@ namespace Opm setWellVariables(const WellState& well_state) { const int nw = well_state.bhp().size(); - for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { + // TODO: using numComp here is only to make the 2p + dummy phase work + // TODO: in theory, we should use numWellEq here. + // for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { + for (int eqIdx = 0; eqIdx < numComponents(); ++eqIdx) { const unsigned int idx = nw * eqIdx + indexOfWell(); assert( eqIdx < well_variables_.size() ); assert( idx < well_state.wellSolutions().size() ); @@ -616,10 +619,10 @@ namespace Opm } // add a trivial equation for the dummy phase for 2p cases (Only support water + oil) - /* if ( numComp < numWellEq ) { + if ( numComp < numWellEq ) { assert(!active()[ Gas ]); invDuneD_[0][0][Gas][Gas] = 1.0; - } */ + } // Store the perforation phase flux for later usage. if (has_solvent && componentIdx == contiSolventEqIdx) {// if (flowPhaseToEbosCompIdx(componentIdx) == Solvent) @@ -656,11 +659,6 @@ namespace Opm invDuneD_[0][0][componentIdx][pvIdx] += resWell_loc.derivative(pvIdx+numEq); } resWell_[0][componentIdx] += resWell_loc.value(); - - // add trivial equation for polymer - /* if (has_polymer) { - invDuneD_[0][0][contiPolymerEqIdx][polymerConcentrationIdx] = 1.0; - } */ } // do the local inversion of D. From 1550fb76001d0cae6a1e29d415a670877fa8f916 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 4 Aug 2017 11:20:55 +0200 Subject: [PATCH 076/104] WIP in cleaning up the interfaces of WellInterface and StandardWell commiting now to save some changes. --- opm/autodiff/StandardWell.hpp | 13 ++++++------- opm/autodiff/StandardWell_impl.hpp | 30 ++++++++++++++--------------- opm/autodiff/WellInterface.hpp | 15 +++++---------- opm/autodiff/WellInterface_impl.hpp | 27 ++------------------------ 4 files changed, 28 insertions(+), 57 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index ee4547b5c..4a47b41ee 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -167,18 +167,19 @@ namespace Opm using Base::has_solvent; using Base::has_polymer; + using Base::name; + using Base::wellType; + using Base::wellControls; + + protected: + using Base::phaseUsage; using Base::active; using Base::numberOfPerforations; using Base::wellCells; using Base::saturationTableNumber; using Base::indexOfWell; - using Base::name; - using Base::wellType; - using Base::wellControls; using Base::compFrac; - using Base::numberOfPhases; - using Base::perfDepth; using Base::flowToEbosPvIdx; using Base::flowPhaseToEbosPhaseIdx; using Base::flowPhaseToEbosCompIdx; @@ -188,8 +189,6 @@ namespace Opm using Base::wsolvent; using Base::wpolymer; - protected: - // TODO: maybe this function can go to some helper file. void localInvert(DiagMatWell& istlA) const; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 1652782d3..ab9117b89 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -167,7 +167,7 @@ namespace Opm // pick the density in the top layer const double rho = perf_densities_[0]; // TODO: not sure whether it is always correct - const double well_ref_depth = perfDepth()[0]; + const double well_ref_depth = perf_depth_[0]; const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); bhp -= dp; return bhp; @@ -188,7 +188,7 @@ namespace Opm EvalWell qs = 0.0; const WellControls* wc = wellControls(); - const int np = numberOfPhases(); + const int np = numPhases(); const double target_rate = well_controls_get_current_target(wc); assert(comp_idx < numComponents()); @@ -715,7 +715,7 @@ namespace Opm const int perf, std::vector& mob) const { - const int np = numberOfPhases(); + const int np = numPhases(); const int cell_idx = wellCells()[perf]; assert (int(mob.size()) == numComponents()); const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); @@ -813,7 +813,7 @@ namespace Opm WellState& well_state) const { // TODO: to check whether all the things from PR 1220 were incoporated. - const int np = numberOfPhases(); + const int np = numPhases(); const int nw = well_state.bhp().size(); const double dBHPLimit = param.dbhp_max_rel_; const double dFLimit = param.dwell_fraction_max_; @@ -989,7 +989,7 @@ namespace Opm const WellType& well_type = wellType(); // pick the density in the top layer const double rho = perf_densities_[0]; - const double well_ref_depth = perfDepth()[0]; + const double well_ref_depth = perf_depth_[0]; if (well_type == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); @@ -1082,7 +1082,7 @@ namespace Opm const WellType& well_type = wellType(); const double rho = perf_densities_[0]; - const double well_ref_depth = perfDepth()[0]; + const double well_ref_depth = perf_depth_[0]; if (well_type == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); @@ -1143,7 +1143,7 @@ namespace Opm WellState& xw) const { // number of phases - const int np = numberOfPhases(); + const int np = numPhases(); const int well_index = indexOfWell(); const WellControls* wc = wellControls(); // Updating well state and primary variables. @@ -1184,7 +1184,7 @@ namespace Opm // pick the density in the top layer const double rho = perf_densities_[0]; - const double well_ref_depth = perfDepth()[0]; + const double well_ref_depth = perf_depth_[0]; // TODO: make the following a function and we call it so many times. if (wellType() == INJECTOR) { @@ -1362,7 +1362,7 @@ namespace Opm StandardWell:: updateWellControl(WellState& xw) const { - const int np = numberOfPhases(); + const int np = numPhases(); const int nw = xw.bhp().size(); const int w = indexOfWell(); @@ -1566,7 +1566,7 @@ namespace Opm const std::vector& surf_dens_perf) { // Verify that we have consistent input. - const int np = numberOfPhases(); + const int np = numPhases(); const int nperf = numberOfPerforations(); const int num_comp = numComponents(); const PhaseUsage* phase_usage = phase_usage_; @@ -1704,7 +1704,7 @@ namespace Opm typedef double Scalar; typedef std::vector< Scalar > Vector; - const int np = numberOfPhases(); + const int np = numPhases(); const int numComp = numComponents(); // the following implementation assume that the polymer is always after the w-o-g phases @@ -1787,7 +1787,7 @@ namespace Opm // Compute densities const int nperf = numberOfPerforations(); const int numComponent = numComponents(); - const int np = numberOfPhases(); + const int np = numPhases(); std::vector perfRates(b_perf.size(),0.0); for (int perf = 0; perf < nperf; ++perf) { @@ -1942,7 +1942,7 @@ namespace Opm const EvalWell& bhp, std::vector& well_flux) const { - const int np = numberOfPhases(); + const int np = numPhases(); const int numComp = numComponents(); well_flux.resize(np, 0.0); @@ -1976,7 +1976,7 @@ namespace Opm { // TODO: pay attention to the situation that finally the potential is calculated based on the bhp control // TODO: should we consider the bhp constraints during the iterative process? - const int np = numberOfPhases(); + const int np = numPhases(); assert( np == int(initial_potential.size()) ); @@ -2099,7 +2099,7 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const { - const int np = numberOfPhases(); + const int np = numPhases(); well_potentials.resize(np, 0.0); diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index cf253cb1c..0ea129ea8 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -96,12 +96,6 @@ namespace Opm /// Well controls WellControls* wellControls() const; - /// Number of the perforations - int numberOfPerforations() const; - - /// Depth of perforations - const std::vector& perfDepth() const; - virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, const VFPProperties* vfp_properties_arg, @@ -172,14 +166,15 @@ namespace Opm int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; - int numPhases() const; - // number of phases - int numberOfPhases() const; + int numPhases() const; // TODO: it is dumplicated with StandardWellsDense int numComponents() const; + // Number of the perforations + int numberOfPerforations() const; + // simply returning allow_cf_ // TODO: to check whether needed, it causes name problem with the crossFlowAllowed bool allowCrossFlow() const; @@ -280,7 +275,7 @@ namespace Opm // Component fractions for each phase for the well const std::vector& compFrac() const; - /// Well productivity index for each perforation. + // Well productivity index for each perforation. const std::vector& wellIndex() const; double mostStrictBhpFromBhpLimits() const; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 8a912c021..5f88a6aec 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -148,17 +148,6 @@ namespace Opm - template - int - WellInterface:: - numberOfPhases() const - { - return number_of_phases_; - } - - - - template const std::vector& WellInterface:: @@ -219,18 +208,6 @@ namespace Opm - template - const std::vector& - WellInterface:: - perfDepth() const - { - return perf_depth_; - } - - - - - template const std::vector& WellInterface:: @@ -502,7 +479,7 @@ namespace Opm const WellState& well_state) const { const Opm::PhaseUsage& pu = *phase_usage_; - const int np = numberOfPhases(); + const int np = numPhases(); if (econ_production_limits.onMinOilRate()) { assert(active()[Oil]); @@ -557,7 +534,7 @@ namespace Opm bool last_connection = false; double violation_extent = -1.0; - const int np = numberOfPhases(); + const int np = numPhases(); const Opm::PhaseUsage& pu = *phase_usage_; const int well_number = index_of_well_; From 78dd9d1d16a58f747e7a3699314240dc732518a1 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 4 Aug 2017 13:07:16 +0200 Subject: [PATCH 077/104] more cleaning up of the interface of WellInterface and StandardWell --- opm/autodiff/StandardWell.hpp | 99 +++++------ opm/autodiff/StandardWell_impl.hpp | 258 ++++++++++++++-------------- opm/autodiff/WellInterface.hpp | 25 +-- opm/autodiff/WellInterface_impl.hpp | 110 +----------- 4 files changed, 188 insertions(+), 304 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 4a47b41ee..7af9e27b1 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -59,6 +59,10 @@ namespace Opm using typename Base::Scalar; using Base::numEq; + + using Base::has_solvent; + using Base::has_polymer; + // TODO: with flow_ebos,for a 2P deck, // TODO: for the 2p deck, numEq will be 3, a dummy phase is already added from the reservoir side. // it will cause problem here without processing the dummy phase. static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq-1 : numEq; // number of wellEq is only numEq - 1 for polymer @@ -93,16 +97,16 @@ namespace Opm StandardWell(const Well* well, const int time_step, const Wells* wells); + virtual void init(const PhaseUsage* phase_usage_arg, + const std::vector* active_arg, + const VFPProperties* vfp_properties_arg, + const std::vector& depth_arg, + const double gravity_arg, + const int num_cells); + + virtual void setWellVariables(const WellState& well_state); - EvalWell wellVolumeFractionScaled(const int phase) const; - - EvalWell wellVolumeFraction(const int phase) const; - - EvalWell wellSurfaceVolumeFraction(const int phase) const; - - EvalWell extendEval(const Eval& in) const; - // TODO: to check whether all the paramters are required void computePerfRate(const IntensiveQuantities& intQuants, const std::vector& mob_perfcells_dense, @@ -116,23 +120,12 @@ namespace Opm virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const; - void getMobility(const Simulator& ebosSimulator, - const int perf, - std::vector& mob) const; - - // TODO: the parameters need to be optimized/adjusted - virtual void init(const PhaseUsage* phase_usage_arg, - const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, - const std::vector& depth_arg, - const double gravity_arg, - const int num_cells); - - // Update the well_state based on solution + /// updating the well_state based on well solution dwells void updateWellState(const BVectorWell& dwells, const BlackoilModelParameters& param, WellState& well_state) const; + /// updating the well state based the control mode specified with current // TODO: later will check wheter we need current virtual void updateWellStateWithTarget(const int current, WellState& xw) const; @@ -141,10 +134,12 @@ namespace Opm // will need touch different types of well_state, we will see. virtual void updateWellControl(WellState& xw) const; + /// check whether the well equations get converged for this well virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, const ModelParameters& param) const; + /// computing the accumulation term for later use in well mass equations virtual void computeAccumWell(); virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, @@ -155,47 +150,30 @@ namespace Opm // r = r - C D^-1 Rw virtual void apply(BVector& r) const; - // using the solution x to recover the solution xw for wells and applying - // xw to update Well State + /// using the solution x to recover the solution xw for wells and applying + /// xw to update Well State virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, WellState& well_state) const; + /// computing the well potentials for group control virtual void computeWellPotentials(const Simulator& ebosSimulator, const WellState& well_state, std::vector& well_potentials) const; - - using Base::has_solvent; - using Base::has_polymer; - - using Base::name; - using Base::wellType; - using Base::wellControls; - protected: using Base::phaseUsage; using Base::active; - using Base::numberOfPerforations; - using Base::wellCells; - using Base::saturationTableNumber; - using Base::indexOfWell; - using Base::compFrac; using Base::flowToEbosPvIdx; using Base::flowPhaseToEbosPhaseIdx; using Base::flowPhaseToEbosCompIdx; using Base::numComponents; - using Base::numPhases; - using Base::wellIndex; using Base::wsolvent; using Base::wpolymer; - - // TODO: maybe this function can go to some helper file. - void localInvert(DiagMatWell& istlA) const; - - // xw = inv(D)*(rw - C*x) - void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; + using Base::wellHasTHPConstraints; + using Base::mostStrictBhpFromBhpLimits; // TODO: decide wether to use member function to refer to private member later + using Base::name_; using Base::vfp_properties_; using Base::gravity_; using Base::well_efficiency_factor_; @@ -204,6 +182,15 @@ namespace Opm using Base::ref_depth_; using Base::perf_depth_; using Base::allow_cf_; + using Base::well_cells_; + using Base::number_of_perforations_; + using Base::number_of_phases_; + using Base::saturation_table_number_; + using Base::comp_frac_; + using Base::well_index_; + using Base::index_of_well_; + using Base::well_controls_; + using Base::well_type_; using Base::perf_rep_radius_; using Base::perf_length_; @@ -214,7 +201,7 @@ namespace Opm // pressure drop between different perforations std::vector perf_pressure_diffs_; - // TODO: probably, they should be moved to the WellInterface, when + // TODO: probably, they should be moved to the WellInterface, then // we decide the template paramters. // two off-diagonal matrices OffDiagMatWell duneB_; @@ -240,6 +227,20 @@ namespace Opm // TODO: it is also possible to be moved to the base class. EvalWell getQs(const int comp_idx) const; + EvalWell wellVolumeFractionScaled(const int phase) const; + + EvalWell wellVolumeFraction(const int phase) const; + + EvalWell wellSurfaceVolumeFraction(const int phase) const; + + EvalWell extendEval(const Eval& in) const; + + // TODO: maybe this function can go to some helper file. + void localInvert(DiagMatWell& istlA) const; + + // xw = inv(D)*(rw - C*x) + void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; + // calculate the properties for the well connections // to calulate the pressure difference between well connections. void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, @@ -269,9 +270,6 @@ namespace Opm const ModelParameters& param, WellState& well_state); - using Base::wellHasTHPConstraints; - using Base::mostStrictBhpFromBhpLimits; - // TODO: maybe we should provide a light version of computeWellFlux, which does not include the // calculation of the derivatives void computeWellRatesWithBhp(const Simulator& ebosSimulator, @@ -281,6 +279,11 @@ namespace Opm std::vector computeWellPotentialWithTHP(const Simulator& ebosSimulator, const double initial_bhp, // bhp from BHP constraints const std::vector& initial_potential) const; + + // get the mobility for specific perforation + void getMobility(const Simulator& ebosSimulator, + const int perf, + std::vector& mob) const; }; } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index ab9117b89..d8c3f0dae 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -26,8 +26,8 @@ namespace Opm StandardWell:: StandardWell(const Well* well, const int time_step, const Wells* wells) : Base(well, time_step, wells) - , perf_densities_(numberOfPerforations()) - , perf_pressure_diffs_(numberOfPerforations()) + , perf_densities_(number_of_perforations_) + , perf_pressure_diffs_(number_of_perforations_) , well_variables_(numWellEq) // the number of the primary variables , F0_(numWellEq) { @@ -54,9 +54,9 @@ namespace Opm vfp_properties_arg, depth_arg, gravity_arg, num_cells); - perf_depth_.resize(numberOfPerforations(), 0.); - for (int perf = 0; perf < numberOfPerforations(); ++perf) { - const int cell_idx = wellCells()[perf]; + perf_depth_.resize(number_of_perforations_, 0.); + for (int perf = 0; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; perf_depth_[perf] = depth_arg[cell_idx]; } @@ -65,8 +65,8 @@ namespace Opm // B D] x_well] res_well] // set the size of the matrices invDuneD_.setSize(1, 1, 1); - duneB_.setSize(1, num_cells, numberOfPerforations()); - duneC_.setSize(1, num_cells, numberOfPerforations()); + duneB_.setSize(1, num_cells, number_of_perforations_); + duneC_.setSize(1, num_cells, number_of_perforations_); for (auto row=invDuneD_.createbegin(), end = invDuneD_.createend(); row!=end; ++row) { // Add nonzeros for diagonal @@ -75,16 +75,16 @@ namespace Opm for (auto row = duneB_.createbegin(), end = duneB_.createend(); row!=end; ++row) { // Add nonzeros for diagonal - for (int perf = 0 ; perf < numberOfPerforations(); ++perf) { - const int cell_idx = wellCells()[perf]; + for (int perf = 0 ; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; row.insert(cell_idx); } } // make the C^T matrix for (auto row = duneC_.createbegin(), end = duneC_.createend(); row!=end; ++row) { - for (int perf = 0; perf < numberOfPerforations(); ++perf) { - const int cell_idx = wellCells()[perf]; + for (int perf = 0; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; row.insert(cell_idx); } } @@ -109,7 +109,7 @@ namespace Opm // TODO: in theory, we should use numWellEq here. // for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { for (int eqIdx = 0; eqIdx < numComponents(); ++eqIdx) { - const unsigned int idx = nw * eqIdx + indexOfWell(); + const unsigned int idx = nw * eqIdx + index_of_well_; assert( eqIdx < well_variables_.size() ); assert( idx < well_state.wellSolutions().size() ); @@ -128,7 +128,7 @@ namespace Opm StandardWell:: getBhp() const { - const WellControls* wc = wellControls(); + const WellControls* wc = well_controls_; if (well_controls_get_current_type(wc) == BHP) { EvalWell bhp = 0.0; const double target_rate = well_controls_get_current_target(wc); @@ -156,7 +156,7 @@ namespace Opm if (active()[ Gas ]) { vapour = getQs(pu.phase_pos[ Gas ]); } - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { bhp = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp); vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); } else { @@ -187,8 +187,8 @@ namespace Opm { EvalWell qs = 0.0; - const WellControls* wc = wellControls(); - const int np = numPhases(); + const WellControls* wc = well_controls_; + const int np = number_of_phases_; const double target_rate = well_controls_get_current_target(wc); assert(comp_idx < numComponents()); @@ -196,17 +196,17 @@ namespace Opm // TODO: the formulation for the injectors decides it only work with single phase // surface rate injection control. Improvement will be required. - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { if (has_solvent) { // TODO: investigate whether the use of the comp_frac is justified. // The usage of the comp_frac is not correct, which should be changed later. double comp_frac = 0.0; if (has_solvent && comp_idx == contiSolventEqIdx) { // solvent - comp_frac = compFrac()[pu.phase_pos[ Gas ]] * wsolvent(); + comp_frac = comp_frac_[pu.phase_pos[ Gas ]] * wsolvent(); } else if (comp_idx == pu.phase_pos[ Gas ]) { - comp_frac = compFrac()[comp_idx] * (1.0 - wsolvent()); + comp_frac = comp_frac_[comp_idx] * (1.0 - wsolvent()); } else { - comp_frac = compFrac()[comp_idx]; + comp_frac = comp_frac_[comp_idx]; } if (comp_frac == 0.0) { return qs; //zero @@ -220,7 +220,7 @@ namespace Opm return qs; } - const double comp_frac = compFrac()[comp_idx]; + const double comp_frac = comp_frac_[comp_idx]; if (comp_frac == 0.0) { return qs; } @@ -308,7 +308,7 @@ namespace Opm // ReservoirRate return target_rate * wellVolumeFractionScaled(comp_idx); } else { - OPM_THROW(std::logic_error, "Unknown control type for well " << name()); + OPM_THROW(std::logic_error, "Unknown control type for well " << name_); } // avoid warning of condition reaches end of non-void function @@ -327,7 +327,7 @@ namespace Opm { // TODO: we should be able to set the g for the well based on the control type // instead of using explicit code for g all the times - const WellControls* wc = wellControls(); + const WellControls* wc = well_controls_; if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { if (has_solvent && compIdx == contiSolventEqIdx) { @@ -434,7 +434,7 @@ namespace Opm const bool& allow_cf, std::vector& cq_s) const { const Opm::PhaseUsage& pu = phaseUsage(); - const int np = numPhases(); + const int np = number_of_phases_; const int numComp = numComponents(); std::vector cmix_s(numComp,0.0); for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { @@ -461,7 +461,7 @@ namespace Opm // producing perforations if ( drawdown.value() > 0 ) { //Do nothing if crossflow is not allowed - if (!allow_cf && wellType() == INJECTOR) { + if (!allow_cf && well_type_ == INJECTOR) { return; } @@ -482,7 +482,7 @@ namespace Opm } else { //Do nothing if crossflow is not allowed - if (!allow_cf && wellType() == PRODUCER) { + if (!allow_cf && well_type_ == PRODUCER) { return; } @@ -514,7 +514,7 @@ namespace Opm const EvalWell d = 1.0 - rv * rs; if (d.value() == 0.0) { - OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << name() << " during flux calcuation" + OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << name_ << " during flux calcuation" << " with rs " << rs << " and rv " << rv); } @@ -561,7 +561,7 @@ namespace Opm // TODO: accessing well_state information is the only place to use nw at the moment const int nw = well_state.bhp().size(); const int numComp = numComponents(); - const int np = numPhases(); + const int np = number_of_phases_; // clear all entries duneB_ = 0.0; @@ -579,14 +579,14 @@ namespace Opm const EvalWell& bhp = getBhp(); - for (int perf = 0; perf < numberOfPerforations(); ++perf) { + for (int perf = 0; perf < number_of_perforations_; ++perf) { - const int cell_idx = wellCells()[perf]; + const int cell_idx = well_cells_[perf]; const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/ 0)); std::vector cq_s(numComp,0.0); std::vector mob(numComp, 0.0); getMobility(ebosSimulator, perf, mob); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, well_index_[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); for (int componentIdx = 0; componentIdx < numComp; ++componentIdx) { // the cq_s entering mass balance equations need to consider the efficiency factors. @@ -634,7 +634,7 @@ namespace Opm if (has_polymer) { EvalWell cq_s_poly = cq_s[Water]; - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { cq_s_poly *= wpolymer(); } else { cq_s_poly *= extendEval(intQuants.polymerConcentration() * intQuants.polymerViscosityCorrection()); @@ -648,7 +648,7 @@ namespace Opm } // Store the perforation pressure for later usage. - well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[indexOfWell()] + perf_pressure_diffs_[perf]; + well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[index_of_well_] + perf_pressure_diffs_[perf]; } // add vol * dF/dt + Q to the well equations; @@ -682,8 +682,8 @@ namespace Opm // check for special case where all perforations have cross flow // then the wells must allow for cross flow - for (int perf = 0; perf < numberOfPerforations(); ++perf) { - const int cell_idx = wellCells()[perf]; + for (int perf = 0; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); const auto& fs = intQuants.fluidState(); EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); @@ -693,11 +693,11 @@ namespace Opm EvalWell well_pressure = bhp + perf_pressure_diffs_[perf]; EvalWell drawdown = pressure - well_pressure; - if (drawdown.value() < 0 && wellType() == INJECTOR) { + if (drawdown.value() < 0 && well_type_ == INJECTOR) { return false; } - if (drawdown.value() > 0 && wellType() == PRODUCER) { + if (drawdown.value() > 0 && well_type_ == PRODUCER) { return false; } } @@ -715,15 +715,15 @@ namespace Opm const int perf, std::vector& mob) const { - const int np = numPhases(); - const int cell_idx = wellCells()[perf]; + const int np = number_of_phases_; + const int cell_idx = well_cells_[perf]; assert (int(mob.size()) == numComponents()); const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); // either use mobility of the perforation cell or calcualte its own // based on passing the saturation table index - const int satid = saturationTableNumber()[perf] - 1; + const int satid = saturation_table_number_[perf] - 1; const int satid_elem = materialLawManager->satnumRegionIdx(cell_idx); if( satid == satid_elem ) { // the same saturation number is used. i.e. just use the mobilty from the cell @@ -760,7 +760,7 @@ namespace Opm // assume fully mixture for wells. EvalWell polymerConcentration = extendEval(intQuants.polymerConcentration()); - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { const auto& viscosityMultiplier = PolymerModule::plyviscViscosityMultiplierTable(intQuants.pvtRegionIndex()); mob[ Water ] /= (extendEval(intQuants.waterViscosityCorrection()) * viscosityMultiplier.eval(polymerConcentration, /*extrapolate=*/true) ); } @@ -771,7 +771,7 @@ namespace Opm const bool allow_cf = crossFlowAllowed(ebosSimulator); const EvalWell& bhp = getBhp(); std::vector cq_s(numComp,0.0); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, well_index_[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); // TODO: make area a member double area = 2 * M_PI * perf_rep_radius_[perf] * perf_length_[perf]; const auto& materialLawManager = ebosSimulator.problem().materialLawManager(); @@ -813,7 +813,7 @@ namespace Opm WellState& well_state) const { // TODO: to check whether all the things from PR 1220 were incoporated. - const int np = numPhases(); + const int np = number_of_phases_; const int nw = well_state.bhp().size(); const double dBHPLimit = param.dbhp_max_rel_; const double dFLimit = param.dwell_fraction_max_; @@ -821,7 +821,7 @@ namespace Opm std::vector xvar_well_old(numWellEq); // TODO: better way to handle this? for (int i = 0; i < numWellEq; ++i) { - xvar_well_old[i] = well_state.wellSolutions()[i * nw + indexOfWell()]; + xvar_well_old[i] = well_state.wellSolutions()[i * nw + index_of_well_]; } // update the second and third well variable (The flux fractions) @@ -829,37 +829,37 @@ namespace Opm if (active()[ Water ]) { const int sign2 = dwells[0][WFrac] > 0 ? 1: -1; const double dx2_limited = sign2 * std::min(std::abs(dwells[0][WFrac]),dFLimit); - well_state.wellSolutions()[WFrac * nw + indexOfWell()] = xvar_well_old[WFrac] - dx2_limited; + well_state.wellSolutions()[WFrac * nw + index_of_well_] = xvar_well_old[WFrac] - dx2_limited; } if (active()[ Gas ]) { const int sign3 = dwells[0][GFrac] > 0 ? 1: -1; const double dx3_limited = sign3 * std::min(std::abs(dwells[0][GFrac]),dFLimit); - well_state.wellSolutions()[GFrac*nw + indexOfWell()] = xvar_well_old[GFrac] - dx3_limited; + well_state.wellSolutions()[GFrac*nw + index_of_well_] = xvar_well_old[GFrac] - dx3_limited; } if (has_solvent) { const int sign4 = dwells[0][SFrac] > 0 ? 1: -1; const double dx4_limited = sign4 * std::min(std::abs(dwells[0][SFrac]),dFLimit); - well_state.wellSolutions()[SFrac*nw + indexOfWell()] = xvar_well_old[SFrac] - dx4_limited; + well_state.wellSolutions()[SFrac*nw + index_of_well_] = xvar_well_old[SFrac] - dx4_limited; } assert(active()[ Oil ]); F[Oil] = 1.0; if (active()[ Water ]) { - F[Water] = well_state.wellSolutions()[WFrac*nw + indexOfWell()]; + F[Water] = well_state.wellSolutions()[WFrac*nw + index_of_well_]; F[Oil] -= F[Water]; } if (active()[ Gas ]) { - F[Gas] = well_state.wellSolutions()[GFrac*nw + indexOfWell()]; + F[Gas] = well_state.wellSolutions()[GFrac*nw + index_of_well_]; F[Oil] -= F[Gas]; } double F_solvent = 0.0; if (has_solvent) { - F_solvent = well_state.wellSolutions()[SFrac*nw + indexOfWell()]; + F_solvent = well_state.wellSolutions()[SFrac*nw + index_of_well_]; F[Oil] -= F_solvent; } @@ -903,13 +903,13 @@ namespace Opm } if (active()[ Water ]) { - well_state.wellSolutions()[WFrac*nw + indexOfWell()] = F[Water]; + well_state.wellSolutions()[WFrac*nw + index_of_well_] = F[Water]; } if (active()[ Gas ]) { - well_state.wellSolutions()[GFrac*nw + indexOfWell()] = F[Gas]; + well_state.wellSolutions()[GFrac*nw + index_of_well_] = F[Gas]; } if(has_solvent) { - well_state.wellSolutions()[SFrac*nw + indexOfWell()] = F_solvent; + well_state.wellSolutions()[SFrac*nw + index_of_well_] = F_solvent; } // F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent. @@ -919,11 +919,11 @@ namespace Opm } // The interpretation of the first well variable depends on the well control - const WellControls* wc = wellControls(); + const WellControls* wc = well_controls_; // TODO: we should only maintain one current control either from the well_state or from well_controls struct. // Either one can be more favored depending on the final strategy for the initilzation of the well control - const int current = well_state.currentControls()[indexOfWell()]; + const int current = well_state.currentControls()[index_of_well_]; const double target_rate = well_controls_iget_target(wc, current); std::vector g = {1,1,0.01}; @@ -946,18 +946,18 @@ namespace Opm case THP: // The BHP and THP both uses the total rate as first well variable. case BHP: { - well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; + well_state.wellSolutions()[nw*XvarWell + index_of_well_] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; - switch (wellType()) { + switch (well_type_) { case INJECTOR: for (int p = 0; p < np; ++p) { - const double comp_frac = compFrac()[p]; - well_state.wellRates()[indexOfWell() * np + p] = comp_frac * well_state.wellSolutions()[nw*XvarWell + indexOfWell()]; + const double comp_frac = comp_frac_[p]; + well_state.wellRates()[index_of_well_ * np + p] = comp_frac * well_state.wellSolutions()[nw*XvarWell + index_of_well_]; } break; case PRODUCER: for (int p = 0; p < np; ++p) { - well_state.wellRates()[indexOfWell() * np + p] = well_state.wellSolutions()[nw*XvarWell + indexOfWell()] * F[p]; + well_state.wellRates()[index_of_well_ * np + p] = well_state.wellSolutions()[nw*XvarWell + index_of_well_] * F[p]; } break; } @@ -972,13 +972,13 @@ namespace Opm const Opm::PhaseUsage& pu = phaseUsage(); if (active()[ Water ]) { - aqua = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Water ] ]; + aqua = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Oil ] ]; + liquid = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = well_state.wellRates()[indexOfWell() * np + pu.phase_pos[ Gas ] ]; + vapour = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Gas ] ]; } const int vfp = well_controls_iget_vfp(wc, current); @@ -986,7 +986,7 @@ namespace Opm const double& alq = well_controls_iget_alq(wc, current); // Set *BHP* target by calculating bhp from THP - const WellType& well_type = wellType(); + const WellType& well_type = well_type_; // pick the density in the top layer const double rho = perf_densities_[0]; const double well_ref_depth = perf_depth_[0]; @@ -996,14 +996,14 @@ namespace Opm const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - well_state.bhp()[indexOfWell()] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; + well_state.bhp()[index_of_well_] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; } else if (well_type == PRODUCER) { const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - well_state.bhp()[indexOfWell()] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; + well_state.bhp()[index_of_well_] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; } else { OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); @@ -1016,11 +1016,11 @@ namespace Opm { const int sign1 = dwells[0][XvarWell] > 0 ? 1: -1; const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[XvarWell])*dBHPLimit); - well_state.wellSolutions()[nw*XvarWell + indexOfWell()] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); - well_state.bhp()[indexOfWell()] = well_state.wellSolutions()[nw*XvarWell + indexOfWell()]; + well_state.wellSolutions()[nw*XvarWell + index_of_well_] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); + well_state.bhp()[index_of_well_] = well_state.wellSolutions()[nw*XvarWell + index_of_well_]; if (well_controls_iget_type(wc, current) == SURFACE_RATE) { - if (wellType() == PRODUCER) { + if (well_type_ == PRODUCER) { const double* distr = well_controls_iget_distr(wc, current); @@ -1029,17 +1029,17 @@ namespace Opm F_target += distr[p] * F[p]; } for (int p = 0; p < np; ++p) { - well_state.wellRates()[np * indexOfWell() + p] = F[p] * target_rate / F_target; + well_state.wellRates()[np * index_of_well_ + p] = F[p] * target_rate / F_target; } } else { for (int p = 0; p < np; ++p) { - well_state.wellRates()[indexOfWell() * np + p] = compFrac()[p] * target_rate; + well_state.wellRates()[index_of_well_ * np + p] = comp_frac_[p] * target_rate; } } } else { // RESERVOIR_RATE for (int p = 0; p < np; ++p) { - well_state.wellRates()[np * indexOfWell() + p] = F[p] * target_rate; + well_state.wellRates()[np * index_of_well_ + p] = F[p] * target_rate; } } } @@ -1055,11 +1055,11 @@ namespace Opm for ( ; ctrl_index < nwc; ++ctrl_index) { if (well_controls_iget_type(wc, ctrl_index) == THP) { // the current control - const int current = well_state.currentControls()[indexOfWell()]; + const int current = well_state.currentControls()[index_of_well_]; // If under THP control at the moment if (current == ctrl_index) { const double thp_target = well_controls_iget_target(wc, current); - well_state.thp()[indexOfWell()] = thp_target; + well_state.thp()[index_of_well_] = thp_target; } else { // otherwise we calculate the thp from the bhp value double aqua = 0.0; double liquid = 0.0; @@ -1068,19 +1068,19 @@ namespace Opm const Opm::PhaseUsage& pu = phaseUsage(); if (active()[ Water ]) { - aqua = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Water ] ]; + aqua = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Oil ] ]; + liquid = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = well_state.wellRates()[indexOfWell()*np + pu.phase_pos[ Gas ] ]; + vapour = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Gas ] ]; } const double alq = well_controls_iget_alq(wc, ctrl_index); const int table_id = well_controls_iget_vfp(wc, ctrl_index); - const WellType& well_type = wellType(); + const WellType& well_type = well_type_; const double rho = perf_densities_[0]; const double well_ref_depth = perf_depth_[0]; if (well_type == INJECTOR) { @@ -1088,17 +1088,17 @@ namespace Opm const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - const double bhp = well_state.bhp()[indexOfWell()]; + const double bhp = well_state.bhp()[index_of_well_]; - well_state.thp()[indexOfWell()] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp + dp); + well_state.thp()[index_of_well_] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp + dp); } else if (well_type == PRODUCER) { const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - const double bhp = well_state.bhp()[indexOfWell()]; + const double bhp = well_state.bhp()[index_of_well_]; - well_state.thp()[indexOfWell()] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp + dp, alq); + well_state.thp()[index_of_well_] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp + dp, alq); } else { OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); } @@ -1111,7 +1111,7 @@ namespace Opm // no THP constraint found if (ctrl_index == nwc) { // not finding a THP contstraints - well_state.thp()[indexOfWell()] = 0.0; + well_state.thp()[index_of_well_] = 0.0; } } @@ -1143,9 +1143,9 @@ namespace Opm WellState& xw) const { // number of phases - const int np = numPhases(); - const int well_index = indexOfWell(); - const WellControls* wc = wellControls(); + const int np = number_of_phases_; + const int well_index = index_of_well_; + const WellControls* wc = well_controls_; // Updating well state and primary variables. // Target values are used as initial conditions for BHP, THP, and SURFACE_RATE const double target = well_controls_iget_target(wc, current); @@ -1187,7 +1187,7 @@ namespace Opm const double well_ref_depth = perf_depth_[0]; // TODO: make the following a function and we call it so many times. - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); @@ -1195,7 +1195,7 @@ namespace Opm xw.bhp()[well_index] = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp) - dp; } - else if (wellType() == PRODUCER) { + else if (well_type_ == PRODUCER) { const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); @@ -1220,7 +1220,7 @@ namespace Opm assert(numPhasesWithTargetsUnderThisControl > 0); - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { // assign target value as initial guess for injectors // only handles single phase control at the moment assert(numPhasesWithTargetsUnderThisControl == 1); @@ -1232,7 +1232,7 @@ namespace Opm xw.wellRates()[np * well_index + phase] = 0.; } } - } else if (wellType() == PRODUCER) { + } else if (well_type_ == PRODUCER) { // update the rates of phases under control based on the target, // and also update rates of phases not under control to keep the rate ratio, // assuming the mobility ratio does not change for the production wells @@ -1283,9 +1283,9 @@ namespace Opm case THP: case BHP: { xw.wellSolutions()[nw*XvarWell + well_index] = 0.0; - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { for (int p = 0; p < np; ++p) { - xw.wellSolutions()[nw*XvarWell + well_index] += xw.wellRates()[np*well_index + p] * compFrac()[p]; + xw.wellSolutions()[nw*XvarWell + well_index] += xw.wellRates()[np*well_index + p] * comp_frac_[p]; } } else { for (int p = 0; p < np; ++p) { @@ -1315,7 +1315,7 @@ namespace Opm xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; } } else { // tot_well_rate == 0 - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { // only single phase injection handled if (active()[Water]) { if (distr[Water] > 0.0) { @@ -1339,7 +1339,7 @@ namespace Opm // TODO: it is possible to leave injector as a oil well, // when F_w and F_g both equals to zero, not sure under what kind of circumstance // this will happen. - } else if (wellType() == PRODUCER) { // producers + } else if (well_type_ == PRODUCER) { // producers // TODO: the following are not addressed for the solvent case yet if (active()[Water]) { xw.wellSolutions()[WFrac * nw + well_index] = 1.0 / np; @@ -1362,15 +1362,15 @@ namespace Opm StandardWell:: updateWellControl(WellState& xw) const { - const int np = numPhases(); + const int np = number_of_phases_; const int nw = xw.bhp().size(); - const int w = indexOfWell(); + const int w = index_of_well_; const int old_control_index = xw.currentControls()[w]; // Find, for each well, if any constraints are broken. If so, // switch control to first broken constraint. - WellControls* wc = wellControls(); + WellControls* wc = well_controls_; // Loop over all controls except the current one, and also // skip any RESERVOIR_RATE controls, since we cannot @@ -1388,7 +1388,7 @@ namespace Opm } if (wellhelpers::constraintBroken( xw.bhp(), xw.thp(), xw.wellRates(), - w, np, wellType(), wc, ctrl_index)) { + w, np, well_type_, wc, ctrl_index)) { // ctrl_index will be the index of the broken constraint after the loop. break; } @@ -1422,7 +1422,7 @@ namespace Opm // checking whether control changed wellhelpers::WellSwitchingLogger logger; if (updated_control_index != old_control_index) { - logger.wellSwitched(name(), + logger.wellSwitched(name_, well_controls_iget_type(wc, old_control_index), well_controls_iget_type(wc, updated_control_index)); } @@ -1457,14 +1457,14 @@ namespace Opm std::vector& rvmax_perf, std::vector& surf_dens_perf) const { - const int nperf = numberOfPerforations(); + const int nperf = number_of_perforations_; // TODO: can make this a member? const int nw = xw.bhp().size(); const int numComp = numComponents(); const PhaseUsage& pu = *phase_usage_; b_perf.resize(nperf*numComp); surf_dens_perf.resize(nperf*numComp); - const int w = indexOfWell(); + const int w = index_of_well_; //rs and rv are only used if both oil and gas is present if (pu.phase_used[BlackoilPhases::Vapour] && pu.phase_pos[BlackoilPhases::Liquid]) { @@ -1474,7 +1474,7 @@ namespace Opm // Compute the average pressure in each well block for (int perf = 0; perf < nperf; ++perf) { - const int cell_idx = wellCells()[perf]; + const int cell_idx = well_cells_[perf]; const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); const auto& fs = intQuants.fluidState(); @@ -1566,8 +1566,8 @@ namespace Opm const std::vector& surf_dens_perf) { // Verify that we have consistent input. - const int np = numPhases(); - const int nperf = numberOfPerforations(); + const int np = number_of_phases_; + const int nperf = number_of_perforations_; const int num_comp = numComponents(); const PhaseUsage* phase_usage = phase_usage_; @@ -1615,7 +1615,7 @@ namespace Opm } else { // No flow => use well specified fractions for mix. for (int phase = 0; phase < np; ++phase) { - mix[phase] = compFrac()[phase]; + mix[phase] = comp_frac_[phase]; } // intialize 0.0 for comIdx >= np; } @@ -1672,7 +1672,7 @@ namespace Opm // perforation for each well, for which it will be the // difference to the reference (bhp) depth. - const int nperf = numberOfPerforations(); + const int nperf = number_of_perforations_; perf_pressure_diffs_.resize(nperf, 0.0); for (int perf = 0; perf < nperf; ++perf) { @@ -1704,7 +1704,7 @@ namespace Opm typedef double Scalar; typedef std::vector< Scalar > Vector; - const int np = numPhases(); + const int np = number_of_phases_; const int numComp = numComponents(); // the following implementation assume that the polymer is always after the w-o-g phases @@ -1736,11 +1736,11 @@ namespace Opm const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); if (std::isnan(well_flux_residual[phaseIdx])) { - OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name()); + OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name_); } if (well_flux_residual[phaseIdx] > maxResidualAllowed) { - OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name()); + OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name_); } } @@ -1785,9 +1785,9 @@ namespace Opm const std::vector& surf_dens_perf) { // Compute densities - const int nperf = numberOfPerforations(); + const int nperf = number_of_perforations_; const int numComponent = numComponents(); - const int np = numPhases(); + const int np = number_of_phases_; std::vector perfRates(b_perf.size(),0.0); for (int perf = 0; perf < nperf; ++perf) { @@ -1942,20 +1942,20 @@ namespace Opm const EvalWell& bhp, std::vector& well_flux) const { - const int np = numPhases(); + const int np = number_of_phases_; const int numComp = numComponents(); well_flux.resize(np, 0.0); const bool allow_cf = crossFlowAllowed(ebosSimulator); - for (int perf = 0; perf < numberOfPerforations(); ++perf) { - const int cell_idx = wellCells()[perf]; + for (int perf = 0; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/ 0)); // flux for each perforation std::vector cq_s(numComp, 0.0); std::vector mob(numComp, 0.0); getMobility(ebosSimulator, perf, mob); - computePerfRate(intQuants, mob, wellIndex()[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); + computePerfRate(intQuants, mob, well_index_[perf], bhp, perf_pressure_diffs_[perf], allow_cf, cq_s); for(int p = 0; p < np; ++p) { well_flux[p] += cq_s[p].value(); @@ -1976,7 +1976,7 @@ namespace Opm { // TODO: pay attention to the situation that finally the potential is calculated based on the bhp control // TODO: should we consider the bhp constraints during the iterative process? - const int np = numPhases(); + const int np = number_of_phases_; assert( np == int(initial_potential.size()) ); @@ -2000,10 +2000,10 @@ namespace Opm bhp = initial_bhp; // The number of the well controls/constraints - const int nwc = well_controls_get_num(wellControls()); + const int nwc = well_controls_get_num(well_controls_); for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { - if (well_controls_iget_type(wellControls(), ctrl_index) == THP) { + if (well_controls_iget_type(well_controls_, ctrl_index) == THP) { double aqua = 0.0; double liquid = 0.0; double vapour = 0.0; @@ -2020,15 +2020,15 @@ namespace Opm vapour = potentials[pu.phase_pos[ Gas ] ]; } - const int vfp = well_controls_iget_vfp(wellControls(), ctrl_index); - const double thp = well_controls_iget_target(wellControls(), ctrl_index); - const double alq = well_controls_iget_alq(wellControls(), ctrl_index); + const int vfp = well_controls_iget_vfp(well_controls_, ctrl_index); + const double thp = well_controls_iget_target(well_controls_, ctrl_index); + const double alq = well_controls_iget_alq(well_controls_, ctrl_index); // Calculating the BHP value based on THP // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. const double rho = perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface const double well_ref_depth = perf_depth_[0]; - if (wellType() == INJECTOR) { + if (well_type_ == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); const double bhp_calculated = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; @@ -2037,7 +2037,7 @@ namespace Opm bhp = bhp_calculated; } } - else if (wellType() == PRODUCER) { + else if (well_type_ == PRODUCER) { const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); const double bhp_calculated = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; @@ -2053,7 +2053,7 @@ namespace Opm // there should be always some available bhp/thp constraints there if (std::isinf(bhp) || std::isnan(bhp)) { - OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name()); + OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name_); } converged = std::abs(old_bhp - bhp) < bhp_tolerance; @@ -2063,7 +2063,7 @@ namespace Opm // checking whether the potentials have valid values for (const double value : potentials) { if (std::isinf(value) || std::isnan(value)) { - OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name()); + OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name_); } } @@ -2082,7 +2082,7 @@ namespace Opm } if (!converged) { - OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name()); + OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name_); } return potentials; @@ -2099,7 +2099,7 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const { - const int np = numPhases(); + const int np = number_of_phases_; well_potentials.resize(np, 0.0); @@ -2114,11 +2114,11 @@ namespace Opm } else { // the well has a THP related constraint // checking whether a well is newly added, it only happens at the beginning of the report step - if ( !well_state.isNewWell(indexOfWell()) ) { + if ( !well_state.isNewWell(index_of_well_) ) { for (int p = 0; p < np; ++p) { // This is dangerous for new added well // since we are not handling the initialization correctly for now - well_potentials[p] = well_state.wellRates()[indexOfWell() * np + p]; + well_potentials[p] = well_state.wellRates()[index_of_well_ * np + p]; } } else { // We need to generate a reasonable rates to start the iteration process diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 0ea129ea8..d30eff06e 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -153,9 +153,6 @@ namespace Opm virtual void updateWellControl(WellState& xw) const = 0; protected: - // Indices of the grid cells/blocks that perforations are completed within. - const std::vector& wellCells() const; - const std::vector& active() const; const PhaseUsage& phaseUsage() const; @@ -166,24 +163,11 @@ namespace Opm int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; - // number of phases - int numPhases() const; - // TODO: it is dumplicated with StandardWellsDense int numComponents() const; - // Number of the perforations - int numberOfPerforations() const; - - // simply returning allow_cf_ - // TODO: to check whether needed, it causes name problem with the crossFlowAllowed - bool allowCrossFlow() const; - virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const = 0; - // TODO: for this kind of function, maybe can make a function with parameter perf - const std::vector& saturationTableNumber() const; - double wsolvent() const; double wpolymer() const; @@ -244,7 +228,7 @@ namespace Opm double well_efficiency_factor_; // cell index for each well perforation - std::vector well_cell_; + std::vector well_cells_; // saturation table nubmer for each well perforation std::vector saturation_table_number_; @@ -268,16 +252,9 @@ namespace Opm bool wellHasTHPConstraints() const; - // The index of the well in Wells struct - // It is used to locate the inforation in Wells and also WellState for now. - int indexOfWell() const; - // Component fractions for each phase for the well const std::vector& compFrac() const; - // Well productivity index for each perforation. - const std::vector& wellIndex() const; - double mostStrictBhpFromBhpLimits() const; // a tuple type for ratio limit check. diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 5f88a6aec..fce30952b 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -69,10 +69,10 @@ namespace Opm number_of_perforations_ = perf_index_end - perf_index_begin; first_perf_ = perf_index_begin; - well_cell_.resize(number_of_perforations_); + well_cells_.resize(number_of_perforations_); std::copy(wells->well_cells + perf_index_begin, wells->well_cells + perf_index_end, - well_cell_.begin() ); + well_cells_.begin() ); well_index_.resize(number_of_perforations_); std::copy(wells->WI + perf_index_begin, @@ -124,18 +124,6 @@ namespace Opm - template - int - WellInterface:: - indexOfWell() const - { - return index_of_well_; - } - - - - - template WellType WellInterface:: @@ -148,18 +136,6 @@ namespace Opm - template - const std::vector& - WellInterface:: - compFrac() const - { - return comp_frac_; - } - - - - - template WellControls* WellInterface:: @@ -172,54 +148,6 @@ namespace Opm - template - const std::vector& - WellInterface:: - saturationTableNumber() const - { - return saturation_table_number_; - } - - - - - - template - int - WellInterface:: - numberOfPerforations() const - { - return number_of_perforations_; - } - - - - - - template - const std::vector& - WellInterface:: - wellIndex() const - { - return well_index_; - } - - - - - - template - const std::vector& - WellInterface:: - wellCells() const - { - return well_cell_; - } - - - - - template const std::vector& WellInterface:: @@ -234,18 +162,6 @@ namespace Opm - template - bool - WellInterface:: - allowCrossFlow() const - { - return allow_cf_; - } - - - - - template void WellInterface:: @@ -322,25 +238,13 @@ namespace Opm - template - int - WellInterface:: - numPhases() const - { - return number_of_phases_; - } - - - - - template int WellInterface:: numComponents() const { // TODO: how about two phase polymer - if (numPhases() == 2) { + if (number_of_phases_ == 2) { return 2; } @@ -479,7 +383,7 @@ namespace Opm const WellState& well_state) const { const Opm::PhaseUsage& pu = *phase_usage_; - const int np = numPhases(); + const int np = number_of_phases_; if (econ_production_limits.onMinOilRate()) { assert(active()[Oil]); @@ -534,7 +438,7 @@ namespace Opm bool last_connection = false; double violation_extent = -1.0; - const int np = numPhases(); + const int np = number_of_phases_; const Opm::PhaseUsage& pu = *phase_usage_; const int well_number = index_of_well_; @@ -733,7 +637,7 @@ namespace Opm assert((worst_offending_connection >= 0) && (worst_offending_connection < number_of_perforations_)); - const int cell_worst_offending_connection = well_cell_[worst_offending_connection]; + const int cell_worst_offending_connection = well_cells_[worst_offending_connection]; list_econ_limited.addClosedConnectionsForWell(well_name, cell_worst_offending_connection); const std::string msg = std::string("Connection ") + std::to_string(worst_offending_connection) + std::string(" for well ") + well_name + std::string(" will be closed due to economic limit"); @@ -761,7 +665,7 @@ namespace Opm auto cell_to_faces = Opm::UgGridHelpers::cell2Faces(grid); auto begin_face_centroids = Opm::UgGridHelpers::beginFaceCentroids(grid); - const int nperf = numberOfPerforations(); + const int nperf = number_of_perforations_; perf_rep_radius_.clear(); perf_length_.clear(); From 6dcb0dfba1428f74d3daa02514290660dc8f35b5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 7 Aug 2017 14:50:03 +0200 Subject: [PATCH 078/104] fixing the vfp running with flow_ebos. --- opm/autodiff/StandardWell.hpp | 13 ++++++------- opm/autodiff/StandardWell_impl.hpp | 15 ++++++--------- opm/autodiff/StandardWellsDense.hpp | 2 -- opm/autodiff/StandardWellsDense_impl.hpp | 9 ++++++--- opm/autodiff/WellInterface.hpp | 3 ++- opm/autodiff/WellInterface_impl.hpp | 14 ++++++++++++-- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 7af9e27b1..b431ff2c5 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -99,7 +99,6 @@ namespace Opm virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, const std::vector& depth_arg, const double gravity_arg, const int num_cells); @@ -161,6 +160,7 @@ namespace Opm std::vector& well_potentials) const; protected: + // protected functions from the Base class using Base::phaseUsage; using Base::active; using Base::flowToEbosPvIdx; @@ -172,7 +172,7 @@ namespace Opm using Base::wellHasTHPConstraints; using Base::mostStrictBhpFromBhpLimits; - // TODO: decide wether to use member function to refer to private member later + // protected member variables from the Base class using Base::name_; using Base::vfp_properties_; using Base::gravity_; @@ -201,8 +201,6 @@ namespace Opm // pressure drop between different perforations std::vector perf_pressure_diffs_; - // TODO: probably, they should be moved to the WellInterface, then - // we decide the template paramters. // two off-diagonal matrices OffDiagMatWell duneB_; OffDiagMatWell duneC_; @@ -214,6 +212,7 @@ namespace Opm mutable BVectorWell invDrw_; mutable BVector scaleAddRes_; + // residuals of the well equations BVectorWell resWell_; std::vector well_variables_; @@ -235,7 +234,7 @@ namespace Opm EvalWell extendEval(const Eval& in) const; - // TODO: maybe this function can go to some helper file. + // TODO: maybe this type of function can go to some helper file. void localInvert(DiagMatWell& istlA) const; // xw = inv(D)*(rw - C*x) @@ -250,7 +249,7 @@ namespace Opm std::vector& rvmax_perf, std::vector& surf_dens_perf) const; - // TODO: not total sure whether it is a good idea to put here + // TODO: not total sure whether it is a good idea to put this function here // the major reason to put here is to avoid the usage of Wells struct void computeConnectionDensities(const std::vector& perfComponentRates, const std::vector& b_perf, @@ -270,7 +269,7 @@ namespace Opm const ModelParameters& param, WellState& well_state); - // TODO: maybe we should provide a light version of computeWellFlux, which does not include the + // TODO: maybe we should provide a light version of computePerfRate, which does not include the // calculation of the derivatives void computeWellRatesWithBhp(const Simulator& ebosSimulator, const EvalWell& bhp, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index d8c3f0dae..3f2a91ad1 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -45,14 +45,12 @@ namespace Opm StandardWell:: init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, const std::vector& depth_arg, const double gravity_arg, const int num_cells) { Base::init(phase_usage_arg, active_arg, - vfp_properties_arg, depth_arg, - gravity_arg, num_cells); + depth_arg, gravity_arg, num_cells); perf_depth_.resize(number_of_perforations_, 0.); for (int perf = 0; perf < number_of_perforations_; ++perf) { @@ -166,8 +164,7 @@ namespace Opm // pick the density in the top layer const double rho = perf_densities_[0]; - // TODO: not sure whether it is always correct - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = ref_depth_; const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); bhp -= dp; return bhp; @@ -989,7 +986,7 @@ namespace Opm const WellType& well_type = well_type_; // pick the density in the top layer const double rho = perf_densities_[0]; - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = ref_depth_; if (well_type == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); @@ -1082,7 +1079,7 @@ namespace Opm const WellType& well_type = well_type_; const double rho = perf_densities_[0]; - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = ref_depth_; if (well_type == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); @@ -1184,7 +1181,7 @@ namespace Opm // pick the density in the top layer const double rho = perf_densities_[0]; - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = ref_depth_; // TODO: make the following a function and we call it so many times. if (well_type_ == INJECTOR) { @@ -2027,7 +2024,7 @@ namespace Opm // Calculating the BHP value based on THP // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. const double rho = perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface - const double well_ref_depth = perf_depth_[0]; + const double well_ref_depth = ref_depth_; if (well_type_ == INJECTOR) { const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 025eedc84..a13cba195 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -258,8 +258,6 @@ namespace Opm { PhaseUsage phase_usage_; std::vector active_; - const VFPProperties* vfp_properties_; - double gravity_; const RateConverterType& rate_converter_; std::vector pv_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 2d2eeeb41..c21c7099a 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -56,7 +56,6 @@ namespace Opm { phase_usage_ = phase_usage_arg; active_ = active_arg; - gravity_ = gravity_arg; pv_ = pv_arg; calculateEfficiencyFactors(); @@ -84,7 +83,7 @@ namespace Opm { // TODO: to see whether we can postpone of the intialization of the well containers to // optimize the usage of the following several member variables for (auto& well : well_container_) { - well->init(&phase_usage_, &active_, vfp_properties_, depth_arg, gravity_, nc); + well->init(&phase_usage_, &active_, depth_arg, gravity_arg, nc); } } @@ -97,7 +96,9 @@ namespace Opm { StandardWellsDense:: setVFPProperties(const VFPProperties* vfp_properties_arg) { - vfp_properties_ = vfp_properties_arg; + for (auto& well : well_container_) { + well->setVFPProperties(vfp_properties_arg); + } } @@ -781,6 +782,8 @@ namespace Opm { WellControls* wc = well_container_[w]->wellControls(); const int control = well_controls_get_current(wc); well_state.currentControls()[w] = control; + // TODO: for VFP control, the perf_densities are still zero here, investigate better + // way to handle it later. well_container_[w]->updateWellStateWithTarget(control, well_state); // The wells are not considered to be newly added diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index d30eff06e..275ed51dd 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -96,9 +96,10 @@ namespace Opm /// Well controls WellControls* wellControls() const; + void setVFPProperties(const VFPProperties* vfp_properties_arg); + virtual void init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, const std::vector& depth_arg, const double gravity_arg, const int num_cells); diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index fce30952b..80cab156b 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -97,14 +97,12 @@ namespace Opm WellInterface:: init(const PhaseUsage* phase_usage_arg, const std::vector* active_arg, - const VFPProperties* vfp_properties_arg, const std::vector& /* depth_arg */, const double gravity_arg, const int /* num_cells */) { phase_usage_ = phase_usage_arg; active_ = active_arg; - vfp_properties_ = vfp_properties_arg; gravity_ = gravity_arg; } @@ -112,6 +110,18 @@ namespace Opm + template + void + WellInterface:: + setVFPProperties(const VFPProperties* vfp_properties_arg) + { + vfp_properties_ = vfp_properties_arg; + } + + + + + template const std::string& WellInterface:: From 8441eb77bdd953b1d78948277cd9359c0127db98 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 7 Aug 2017 16:45:16 +0200 Subject: [PATCH 079/104] not using wellSolutions() from WellState while not sure whether we can remove it totally because of the comments related to group control. --- opm/autodiff/StandardWell.hpp | 2 + opm/autodiff/StandardWell_impl.hpp | 69 +++++++++++------------- opm/autodiff/StandardWellsDense_impl.hpp | 1 + 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index b431ff2c5..7d74b025f 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -215,6 +215,8 @@ namespace Opm // residuals of the well equations BVectorWell resWell_; + mutable std::vector well_solutions_; + std::vector well_variables_; std::vector F0_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 3f2a91ad1..83292336e 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -28,6 +28,7 @@ namespace Opm : Base(well, time_step, wells) , perf_densities_(number_of_perforations_) , perf_pressure_diffs_(number_of_perforations_) + , well_solutions_(numWellEq, 0.0) , well_variables_(numWellEq) // the number of the primary variables , F0_(numWellEq) { @@ -107,12 +108,10 @@ namespace Opm // TODO: in theory, we should use numWellEq here. // for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { for (int eqIdx = 0; eqIdx < numComponents(); ++eqIdx) { - const unsigned int idx = nw * eqIdx + index_of_well_; - assert( eqIdx < well_variables_.size() ); - assert( idx < well_state.wellSolutions().size() ); + assert( eqIdx < well_solutions_.size() ); well_variables_[eqIdx] = 0.0; - well_variables_[eqIdx].setValue(well_state.wellSolutions()[idx]); + well_variables_[eqIdx].setValue(well_solutions_[eqIdx]); well_variables_[eqIdx].setDerivative(numEq + eqIdx, 1.0); } } @@ -815,48 +814,44 @@ namespace Opm const double dBHPLimit = param.dbhp_max_rel_; const double dFLimit = param.dwell_fraction_max_; - std::vector xvar_well_old(numWellEq); - // TODO: better way to handle this? - for (int i = 0; i < numWellEq; ++i) { - xvar_well_old[i] = well_state.wellSolutions()[i * nw + index_of_well_]; - } + const std::vector xvar_well_old = well_solutions_; // update the second and third well variable (The flux fractions) std::vector F(np,0.0); if (active()[ Water ]) { const int sign2 = dwells[0][WFrac] > 0 ? 1: -1; const double dx2_limited = sign2 * std::min(std::abs(dwells[0][WFrac]),dFLimit); - well_state.wellSolutions()[WFrac * nw + index_of_well_] = xvar_well_old[WFrac] - dx2_limited; + well_solutions_[WFrac] = xvar_well_old[WFrac] - dx2_limited; } if (active()[ Gas ]) { const int sign3 = dwells[0][GFrac] > 0 ? 1: -1; const double dx3_limited = sign3 * std::min(std::abs(dwells[0][GFrac]),dFLimit); - well_state.wellSolutions()[GFrac*nw + index_of_well_] = xvar_well_old[GFrac] - dx3_limited; + well_solutions_[GFrac] = xvar_well_old[GFrac] - dx3_limited; } if (has_solvent) { const int sign4 = dwells[0][SFrac] > 0 ? 1: -1; const double dx4_limited = sign4 * std::min(std::abs(dwells[0][SFrac]),dFLimit); - well_state.wellSolutions()[SFrac*nw + index_of_well_] = xvar_well_old[SFrac] - dx4_limited; + well_solutions_[SFrac] = xvar_well_old[SFrac] - dx4_limited; } assert(active()[ Oil ]); F[Oil] = 1.0; if (active()[ Water ]) { - F[Water] = well_state.wellSolutions()[WFrac*nw + index_of_well_]; + F[Water] = well_solutions_[WFrac]; F[Oil] -= F[Water]; } if (active()[ Gas ]) { - F[Gas] = well_state.wellSolutions()[GFrac*nw + index_of_well_]; + F[Gas] = well_solutions_[GFrac]; F[Oil] -= F[Gas]; } double F_solvent = 0.0; if (has_solvent) { - F_solvent = well_state.wellSolutions()[SFrac*nw + index_of_well_]; + F_solvent = well_solutions_[SFrac]; F[Oil] -= F_solvent; } @@ -900,13 +895,13 @@ namespace Opm } if (active()[ Water ]) { - well_state.wellSolutions()[WFrac*nw + index_of_well_] = F[Water]; + well_solutions_[WFrac] = F[Water]; } if (active()[ Gas ]) { - well_state.wellSolutions()[GFrac*nw + index_of_well_] = F[Gas]; + well_solutions_[GFrac] = F[Gas]; } if(has_solvent) { - well_state.wellSolutions()[SFrac*nw + index_of_well_] = F_solvent; + well_solutions_[SFrac] = F_solvent; } // F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent. @@ -943,18 +938,18 @@ namespace Opm case THP: // The BHP and THP both uses the total rate as first well variable. case BHP: { - well_state.wellSolutions()[nw*XvarWell + index_of_well_] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; + well_solutions_[XvarWell] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; switch (well_type_) { case INJECTOR: for (int p = 0; p < np; ++p) { const double comp_frac = comp_frac_[p]; - well_state.wellRates()[index_of_well_ * np + p] = comp_frac * well_state.wellSolutions()[nw*XvarWell + index_of_well_]; + well_state.wellRates()[index_of_well_ * np + p] = comp_frac * well_solutions_[XvarWell]; } break; case PRODUCER: for (int p = 0; p < np; ++p) { - well_state.wellRates()[index_of_well_ * np + p] = well_state.wellSolutions()[nw*XvarWell + index_of_well_] * F[p]; + well_state.wellRates()[index_of_well_ * np + p] = well_solutions_[XvarWell] * F[p]; } break; } @@ -1013,8 +1008,8 @@ namespace Opm { const int sign1 = dwells[0][XvarWell] > 0 ? 1: -1; const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[XvarWell])*dBHPLimit); - well_state.wellSolutions()[nw*XvarWell + index_of_well_] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); - well_state.bhp()[index_of_well_] = well_state.wellSolutions()[nw*XvarWell + index_of_well_]; + well_solutions_[XvarWell] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); + well_state.bhp()[index_of_well_] = well_solutions_[XvarWell]; if (well_controls_iget_type(wc, current) == SURFACE_RATE) { if (well_type_ == PRODUCER) { @@ -1279,21 +1274,21 @@ namespace Opm switch (well_controls_iget_type(wc, current)) { case THP: case BHP: { - xw.wellSolutions()[nw*XvarWell + well_index] = 0.0; + well_solutions_[XvarWell] = 0.0; if (well_type_ == INJECTOR) { for (int p = 0; p < np; ++p) { - xw.wellSolutions()[nw*XvarWell + well_index] += xw.wellRates()[np*well_index + p] * comp_frac_[p]; + well_solutions_[XvarWell] += xw.wellRates()[np*well_index + p] * comp_frac_[p]; } } else { for (int p = 0; p < np; ++p) { - xw.wellSolutions()[nw*XvarWell + well_index] += g[p] * xw.wellRates()[np*well_index + p]; + well_solutions_[XvarWell] += g[p] * xw.wellRates()[np*well_index + p]; } } break; } case RESERVOIR_RATE: // Intentional fall-through case SURFACE_RATE: - xw.wellSolutions()[nw*XvarWell + well_index] = xw.bhp()[well_index]; + well_solutions_[XvarWell] = xw.bhp()[well_index]; break; } // end of switch @@ -1303,33 +1298,33 @@ namespace Opm } if(std::abs(tot_well_rate) > 0) { if (active()[ Water ]) { - xw.wellSolutions()[WFrac*nw + well_index] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; + well_solutions_[WFrac] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; } if (active()[ Gas ]) { - xw.wellSolutions()[GFrac*nw + well_index] = g[Gas] * (xw.wellRates()[np*well_index + Gas] - xw.solventWellRate(well_index)) / tot_well_rate ; + well_solutions_[GFrac] = g[Gas] * (xw.wellRates()[np*well_index + Gas] - xw.solventWellRate(well_index)) / tot_well_rate ; } if (has_solvent) { - xw.wellSolutions()[SFrac*nw + well_index] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; + well_solutions_[SFrac] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; } } else { // tot_well_rate == 0 if (well_type_ == INJECTOR) { // only single phase injection handled if (active()[Water]) { if (distr[Water] > 0.0) { - xw.wellSolutions()[WFrac * nw + well_index] = 1.0; + well_solutions_[WFrac] = 1.0; } else { - xw.wellSolutions()[WFrac * nw + well_index] = 0.0; + well_solutions_[WFrac] = 0.0; } } if (active()[Gas]) { if (distr[Gas] > 0.0) { - xw.wellSolutions()[GFrac * nw + well_index] = 1.0 - wsolvent(); + well_solutions_[GFrac] = 1.0 - wsolvent(); if (has_solvent) { - xw.wellSolutions()[SFrac * nw + well_index] = wsolvent(); + well_solutions_[SFrac] = wsolvent(); } } else { - xw.wellSolutions()[GFrac * nw + well_index] = 0.0; + well_solutions_[GFrac] = 0.0; } } @@ -1339,10 +1334,10 @@ namespace Opm } else if (well_type_ == PRODUCER) { // producers // TODO: the following are not addressed for the solvent case yet if (active()[Water]) { - xw.wellSolutions()[WFrac * nw + well_index] = 1.0 / np; + well_solutions_[WFrac] = 1.0 / np; } if (active()[Gas]) { - xw.wellSolutions()[GFrac * nw + well_index] = 1.0 / np; + well_solutions_[GFrac] = 1.0 / np; } } else { OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index c21c7099a..42cec2b42 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -506,6 +506,7 @@ namespace Opm { if (!converged) { well_state = well_state0; // also recover the old well controls + // TODO: well_solutions_ for each well not recovered here. for (int w = 0; w < nw; ++w) { WellControls* wc = well_container_[w]->wellControls(); well_controls_set_current(wc, well_state.currentControls()[w]); From 5af15fa63f36ea1cd22d90ceed357b5bf09f4866 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 7 Aug 2017 17:49:35 +0200 Subject: [PATCH 080/104] removing well_soltutions_ from WellStateFullyImplicitBlackoilDense adding function setWellSolutions() to StandardWell. The class WellStateFullyImplicitBlackoilDense is ready to be removed now, while the only thing can go wrong compred with the original version is the group control, which is not tested yet. --- opm/autodiff/Compat.cpp | 3 - opm/autodiff/StandardWell.hpp | 5 +- opm/autodiff/StandardWell_impl.hpp | 185 ++++++++++-------- opm/autodiff/StandardWellsDense.hpp | 2 +- opm/autodiff/StandardWellsDense_impl.hpp | 14 +- opm/autodiff/WellInterface.hpp | 4 +- .../WellStateFullyImplicitBlackoilDense.hpp | 101 ---------- 7 files changed, 117 insertions(+), 197 deletions(-) diff --git a/opm/autodiff/Compat.cpp b/opm/autodiff/Compat.cpp index e346f4ad4..289255df4 100644 --- a/opm/autodiff/Compat.cpp +++ b/opm/autodiff/Compat.cpp @@ -294,9 +294,6 @@ void wellsToState( const data::Wells& wells, { // Set base class variables. wellsToState(wells, phases, static_cast(state)); - - // Set wellSolution() variable. - state.setWellSolutions(phases); } diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 7d74b025f..17062fd54 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -104,7 +104,7 @@ namespace Opm const int num_cells); - virtual void setWellVariables(const WellState& well_state); + virtual void setWellVariables(); // TODO: to check whether all the paramters are required void computePerfRate(const IntensiveQuantities& intQuants, @@ -158,6 +158,9 @@ namespace Opm virtual void computeWellPotentials(const Simulator& ebosSimulator, const WellState& well_state, std::vector& well_potentials) const; + + virtual void setWellSolutions(const WellState& well_state) const; + protected: // protected functions from the Base class diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 83292336e..2d9098dae 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -101,9 +101,8 @@ namespace Opm template void StandardWell:: - setWellVariables(const WellState& well_state) + setWellVariables() { - const int nw = well_state.bhp().size(); // TODO: using numComp here is only to make the 2p + dummy phase work // TODO: in theory, we should use numWellEq here. // for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { @@ -1260,89 +1259,7 @@ namespace Opm break; } // end of switch - - std::vector g = {1.0, 1.0, 0.01}; - if (well_controls_iget_type(wc, current) == RESERVOIR_RATE) { - for (int phase = 0; phase < np; ++phase) { - g[phase] = distr[phase]; - } - } - - // the number of wells - const int nw = xw.bhp().size(); - - switch (well_controls_iget_type(wc, current)) { - case THP: - case BHP: { - well_solutions_[XvarWell] = 0.0; - if (well_type_ == INJECTOR) { - for (int p = 0; p < np; ++p) { - well_solutions_[XvarWell] += xw.wellRates()[np*well_index + p] * comp_frac_[p]; - } - } else { - for (int p = 0; p < np; ++p) { - well_solutions_[XvarWell] += g[p] * xw.wellRates()[np*well_index + p]; - } - } - break; - } - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - well_solutions_[XvarWell] = xw.bhp()[well_index]; - break; - } // end of switch - - double tot_well_rate = 0.0; - for (int p = 0; p < np; ++p) { - tot_well_rate += g[p] * xw.wellRates()[np*well_index + p]; - } - if(std::abs(tot_well_rate) > 0) { - if (active()[ Water ]) { - well_solutions_[WFrac] = g[Water] * xw.wellRates()[np*well_index + Water] / tot_well_rate; - } - if (active()[ Gas ]) { - well_solutions_[GFrac] = g[Gas] * (xw.wellRates()[np*well_index + Gas] - xw.solventWellRate(well_index)) / tot_well_rate ; - } - if (has_solvent) { - well_solutions_[SFrac] = g[Gas] * xw.solventWellRate(well_index) / tot_well_rate ; - } - } else { // tot_well_rate == 0 - if (well_type_ == INJECTOR) { - // only single phase injection handled - if (active()[Water]) { - if (distr[Water] > 0.0) { - well_solutions_[WFrac] = 1.0; - } else { - well_solutions_[WFrac] = 0.0; - } - } - - if (active()[Gas]) { - if (distr[Gas] > 0.0) { - well_solutions_[GFrac] = 1.0 - wsolvent(); - if (has_solvent) { - well_solutions_[SFrac] = wsolvent(); - } - } else { - well_solutions_[GFrac] = 0.0; - } - } - - // TODO: it is possible to leave injector as a oil well, - // when F_w and F_g both equals to zero, not sure under what kind of circumstance - // this will happen. - } else if (well_type_ == PRODUCER) { // producers - // TODO: the following are not addressed for the solvent case yet - if (active()[Water]) { - well_solutions_[WFrac] = 1.0 / np; - } - if (active()[Gas]) { - well_solutions_[GFrac] = 1.0 / np; - } - } else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } - } + setWellSolutions(xw); } @@ -2127,4 +2044,102 @@ namespace Opm } } + + + + + template + void + StandardWell:: + setWellSolutions(const WellState& well_state) const + { + const int np = number_of_phases_; + const int well_index = index_of_well_; + const WellControls* wc = well_controls_; + const double* distr = well_controls_get_current_distr(wc); + + std::vector g = {1.0, 1.0, 0.01}; + if (well_controls_get_current_type(wc) == RESERVOIR_RATE) { + for (int phase = 0; phase < np; ++phase) { + g[phase] = distr[phase]; + } + } + + // the number of wells + const int nw = well_state.bhp().size(); + + switch (well_controls_get_current_type(wc)) { + case THP: + case BHP: { + well_solutions_[XvarWell] = 0.0; + if (well_type_ == INJECTOR) { + for (int p = 0; p < np; ++p) { + well_solutions_[XvarWell] += well_state.wellRates()[np*well_index + p] * comp_frac_[p]; + } + } else { + for (int p = 0; p < np; ++p) { + well_solutions_[XvarWell] += g[p] * well_state.wellRates()[np*well_index + p]; + } + } + break; + } + case RESERVOIR_RATE: // Intentional fall-through + case SURFACE_RATE: + well_solutions_[XvarWell] = well_state.bhp()[well_index]; + break; + } // end of switch + + double tot_well_rate = 0.0; + for (int p = 0; p < np; ++p) { + tot_well_rate += g[p] * well_state.wellRates()[np*well_index + p]; + } + if(std::abs(tot_well_rate) > 0) { + if (active()[ Water ]) { + well_solutions_[WFrac] = g[Water] * well_state.wellRates()[np*well_index + Water] / tot_well_rate; + } + if (active()[ Gas ]) { + well_solutions_[GFrac] = g[Gas] * (well_state.wellRates()[np*well_index + Gas] - well_state.solventWellRate(well_index)) / tot_well_rate ; + } + if (has_solvent) { + well_solutions_[SFrac] = g[Gas] * well_state.solventWellRate(well_index) / tot_well_rate ; + } + } else { // tot_well_rate == 0 + if (well_type_ == INJECTOR) { + // only single phase injection handled + if (active()[Water]) { + if (distr[Water] > 0.0) { + well_solutions_[WFrac] = 1.0; + } else { + well_solutions_[WFrac] = 0.0; + } + } + + if (active()[Gas]) { + if (distr[Gas] > 0.0) { + well_solutions_[GFrac] = 1.0 - wsolvent(); + if (has_solvent) { + well_solutions_[SFrac] = wsolvent(); + } + } else { + well_solutions_[GFrac] = 0.0; + } + } + + // TODO: it is possible to leave injector as a oil well, + // when F_w and F_g both equals to zero, not sure under what kind of circumstance + // this will happen. + } else if (well_type_ == PRODUCER) { // producers + // TODO: the following are not addressed for the solvent case yet + if (active()[Water]) { + well_solutions_[WFrac] = 1.0 / np; + } + if (active()[Gas]) { + well_solutions_[GFrac] = 1.0 / np; + } + } else { + OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + } + } + } + } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index a13cba195..8b8fcb67a 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -172,7 +172,7 @@ namespace Opm { /// return true if wells are available on this process bool localWellsActive() const; - void setWellVariables(const WellState& xw); + void setWellVariables(); void computeAccumWells(); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 42cec2b42..eb477dbc1 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -174,7 +174,7 @@ namespace Opm { updateWellControls(well_state); updateGroupControls(well_state); // Set the primary variables for the wells - setWellVariables(well_state); + setWellVariables(); if (iterationIdx == 0) { computeWellConnectionPressures(ebosSimulator, well_state); @@ -430,10 +430,10 @@ namespace Opm { template void StandardWellsDense:: - setWellVariables(const WellState& xw) + setWellVariables() { for (auto& well : well_container_) { - well->setWellVariables(xw); + well->setWellVariables(); } } @@ -499,7 +499,7 @@ namespace Opm { { updateWellControls(well_state); updateGroupControls(well_state); - setWellVariables(well_state); + setWellVariables(); } } while (it < 15); @@ -758,7 +758,11 @@ namespace Opm { if (well_collection_->requireWellPotentials()) { // calculate the well potentials - setWellVariables(well_state); + // TODO: for the purpose of group control, not tested yet + for (const auto& well : well_container_) { + well->setWellSolutions(well_state); + } + setWellVariables(); computeWellConnectionPressures(ebos_simulator, well_state); // To store well potentials for each well diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 275ed51dd..eb076cc37 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -104,7 +104,7 @@ namespace Opm const double gravity_arg, const int num_cells); - virtual void setWellVariables(const WellState& well_state) = 0; + virtual void setWellVariables() = 0; virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, @@ -153,6 +153,8 @@ namespace Opm virtual void updateWellControl(WellState& xw) const = 0; + virtual void setWellSolutions(const WellState& well_state) const = 0; + protected: const std::vector& active() const; diff --git a/opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp b/opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp index 48de2d124..d7b6d499f 100644 --- a/opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp +++ b/opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp @@ -108,101 +108,6 @@ namespace Opm } } } - - - // TODO: the reason to keep this is to avoid getting defaulted value BHP - // some facilities needed from opm-parser or opm-core - // It is a little tricky, since sometimes before applying group control, the only - // available constraints in the well_controls is the defaulted BHP value, and it - // is really not desirable to use this value to enter the Newton iterations. - setWellSolutions(pu); - } - - - - /// Set wellSolutions() based on the base class members. - void setWellSolutions(const PhaseUsage& pu) - { - // Set nw and np, or return if no wells. - if (wells_.get() == nullptr) { - return; - } - const int nw = wells_->number_of_wells; - if (nw == 0) { - return; - } - - - const int np = wells_->number_of_phases; - const int numComp = pu.has_solvent? np+1:np; - well_solutions_.clear(); - well_solutions_.resize(nw * numComp, 0.0); - std::vector g = {1.0,1.0,0.01}; - for (int w = 0; w < nw; ++w) { - 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 = currentControls()[w]; - well_controls_set_current( wc, current); - const WellType& well_type = wells_->type[w]; - - switch (well_controls_iget_type(wc, current)) { - case THP: // Intentional fall-through - case BHP: - if (well_type == INJECTOR) { - for (int p = 0; p < np; ++p) { - well_solutions_[w] += wellRates()[np*w + p] * wells_->comp_frac[np*w + p]; - } - } else { - for (int p = 0; p < np; ++p) { - well_solutions_[w] += g[p] * wellRates()[np*w + p]; - } - } - break; - case RESERVOIR_RATE: // Intentional fall-through - case SURFACE_RATE: - wellSolutions()[w] = bhp()[w]; - break; - } - - double total_rates = 0.0; - for (int p = 0; p < np; ++p) { - total_rates += g[p] * wellRates()[np*w + p]; - } - - const int waterpos = pu.phase_pos[Water]; - const int gaspos = pu.phase_pos[Gas]; - - assert(np > 2 || (np == 2 && !pu.phase_used[Gas])); - // assumes the gas fractions are stored after water fractions - if(std::abs(total_rates) > 0) { - if( pu.phase_used[Water] ) { - wellSolutions()[nw + w] = g[Water] * wellRates()[np*w + waterpos] / total_rates; - } - if( pu.phase_used[Gas] ) { - wellSolutions()[2*nw + w] = g[Gas] * (wellRates()[np*w + gaspos] - solventWellRate(w)) / total_rates ; - } - if( pu.has_solvent) { - wellSolutions()[3*nw + w] = g[Gas] * solventWellRate(w) / total_rates; - } - - - - } else { - if( pu.phase_used[Water] ) { - wellSolutions()[nw + w] = wells_->comp_frac[np*w + waterpos]; - } - if( pu.phase_used[Gas] ) { - wellSolutions()[2*nw + w] = wells_->comp_frac[np*w + gaspos]; - } - if (pu.has_solvent) { - wellSolutions()[3*nw + w] = 0; - } - - } - } } @@ -212,11 +117,6 @@ namespace Opm init(wells, state.pressure(), dummy_state, pu) ; } - - /// One rate per phase and well connection. - std::vector& wellSolutions() { return well_solutions_; } - const std::vector& wellSolutions() const { return well_solutions_; } - /// One rate pr well connection. std::vector& perfRateSolvent() { return perfRateSolvent_; } const std::vector& perfRateSolvent() const { return perfRateSolvent_; } @@ -252,7 +152,6 @@ namespace Opm private: - std::vector well_solutions_; std::vector perfRateSolvent_; }; From 5cb0a5109e7df03c41860c3f7653552ac254181c Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 8 Aug 2017 10:44:10 +0200 Subject: [PATCH 081/104] adding setWellSolutions to StandardWellsDense. the function will calculate the well_solutions_ based on well_state. It will be used when we recover the well_state from a failed iteration, then we should also update well_solutions_ for consistence. --- opm/autodiff/StandardWellsDense.hpp | 3 +++ opm/autodiff/StandardWellsDense_impl.hpp | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 8b8fcb67a..ed9f4fb87 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -225,6 +225,9 @@ namespace Opm { void updateGroupControls(WellState& well_state) const; + /// setting the well_solutions_ based on well_state. + void setWellSolutions(const WellState& well_state) const; + protected: bool wells_active_; const Wells* wells_; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index eb477dbc1..ee5124477 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -505,6 +505,7 @@ namespace Opm { if (!converged) { well_state = well_state0; + setWellSolutions(well_state); // also recover the old well controls // TODO: well_solutions_ for each well not recovered here. for (int w = 0; w < nw; ++w) { @@ -759,9 +760,7 @@ namespace Opm { // calculate the well potentials // TODO: for the purpose of group control, not tested yet - for (const auto& well : well_container_) { - well->setWellSolutions(well_state); - } + setWellSolutions(well_state); setWellVariables(); computeWellConnectionPressures(ebos_simulator, well_state); @@ -1042,4 +1041,17 @@ namespace Opm { } + + + + template + void + StandardWellsDense:: + setWellSolutions(const WellState& well_state) const + { + for (const auto& well : well_container_) { + well->setWellSolutions(well_state); + } + } + } // namespace Opm From 31ce880ba7c6e75c68580b6ae75e37d5d681975d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 8 Aug 2017 12:30:24 +0200 Subject: [PATCH 082/104] fixing the prallel running. It needs WellSwitchingLogger for all the processes even there are not wells in some of the processes. --- opm/autodiff/StandardWell.hpp | 3 ++- opm/autodiff/StandardWell_impl.hpp | 4 ++-- opm/autodiff/StandardWellsDense_impl.hpp | 4 +++- opm/autodiff/WellInterface.hpp | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 17062fd54..47cbe1805 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -131,7 +131,8 @@ namespace Opm // TODO: this should go to the WellInterface, while updateWellStateWithTarget // will need touch different types of well_state, we will see. - virtual void updateWellControl(WellState& xw) const; + virtual void updateWellControl(WellState& xw, + wellhelpers::WellSwitchingLogger& logger) const; /// check whether the well equations get converged for this well virtual bool getWellConvergence(Simulator& ebosSimulator, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 2d9098dae..8c48ecfeb 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1269,7 +1269,8 @@ namespace Opm template void StandardWell:: - updateWellControl(WellState& xw) const + updateWellControl(WellState& xw, + wellhelpers::WellSwitchingLogger& logger) const { const int np = number_of_phases_; const int nw = xw.bhp().size(); @@ -1329,7 +1330,6 @@ namespace Opm const int updated_control_index = xw.currentControls()[w]; // checking whether control changed - wellhelpers::WellSwitchingLogger logger; if (updated_control_index != old_control_index) { logger.wellSwitched(name_, well_controls_iget_type(wc, old_control_index), diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index ee5124477..7bc1cb11c 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -657,8 +657,10 @@ namespace Opm { // we simply return. if( !wellsActive() ) return ; + wellhelpers::WellSwitchingLogger logger; + for (const auto& well : well_container_) { - well->updateWellControl(xw); + well->updateWellControl(xw, logger); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index eb076cc37..68b2918ea 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -151,7 +151,8 @@ namespace Opm virtual void updateWellStateWithTarget(const int current, WellState& xw) const = 0; - virtual void updateWellControl(WellState& xw) const = 0; + virtual void updateWellControl(WellState& xw, + wellhelpers::WellSwitchingLogger& logger) const = 0; virtual void setWellSolutions(const WellState& well_state) const = 0; From af6155d15afc410a93dc4c6759aa468f79830a39 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Wed, 9 Aug 2017 16:36:16 +0200 Subject: [PATCH 083/104] updating the well group status before updating targets. This recovers the running with group control. --- opm/autodiff/StandardWellsDense_impl.hpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 7bc1cb11c..e7bf238b8 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -934,13 +934,29 @@ namespace Opm { StandardWellsDense:: updateGroupControls(WellState& well_state) const { - if (wellCollection()->groupControlActive()) { - applyVREPGroupControl(well_state); - wellCollection()->updateWellTargets(well_state.wellRates()); - // TODO: group control has to be applied in the level of the all wells + if (wellCollection()->groupControlActive()) { + for (int w = 0; w < number_of_wells_; ++w) { + // update whether well is under group control + // get well node in the well collection + WellNode& well_node = well_collection_->findWellNode(well_container_[w]->name()); + + // update whehter the well is under group control or individual control + const int current = well_state.currentControls()[w]; + if (well_node.groupControlIndex() >= 0 && current == well_node.groupControlIndex()) { + // under group control + well_node.setIndividualControl(false); + } else { + // individual control + well_node.setIndividualControl(true); + } + } + + applyVREPGroupControl(well_state); // upate the well targets following group controls // it will not change the control mode, only update the targets + wellCollection()->updateWellTargets(well_state.wellRates()); + for (int w = 0; w < number_of_wells_; ++w) { // TODO: check whether we need current argument in updateWellStateWithTarget // maybe there is some circumstances that the current is different from the one From 49df0c12f688a651a29ad4260b51632731210d56 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 10 Aug 2017 13:33:45 +0200 Subject: [PATCH 084/104] cleaing up and fixing the error from rebasing. --- opm/autodiff/StandardWell_impl.hpp | 27 ------------------------ opm/autodiff/StandardWellsDense_impl.hpp | 8 ++----- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 8c48ecfeb..71af53dbe 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -807,7 +807,6 @@ namespace Opm const BlackoilModelParameters& param, WellState& well_state) const { - // TODO: to check whether all the things from PR 1220 were incoporated. const int np = number_of_phases_; const int nw = well_state.bhp().size(); const double dBHPLimit = param.dbhp_max_rel_; @@ -1311,21 +1310,6 @@ namespace Opm well_controls_set_current( wc, current); } - // update whether well is under group control - /* if (wellCollection()->groupControlActive()) { - // get well node in the well collection - WellNode& well_node = well_collection_->findWellNode(std::string(wells().name[w])); - - // update whehter the well is under group control or individual control - if (well_node.groupControlIndex() >= 0 && current == well_node.groupControlIndex()) { - // under group control - well_node.setIndividualControl(false); - } else { - // individual control - well_node.setIndividualControl(true); - } - } */ - // the new well control indices after all the related updates, const int updated_control_index = xw.currentControls()[w]; @@ -1339,17 +1323,6 @@ namespace Opm if (updated_control_index != old_control_index) { // || well_collection_->groupControlActive()) { updateWellStateWithTarget(updated_control_index, xw); } - - // upate the well targets following group controls - // it will not change the control mode, only update the targets - /* if (wellCollection()->groupControlActive()) { - applyVREPGroupControl(xw); - wellCollection()->updateWellTargets(xw.wellRates()); - for (int w = 0; w < nw; ++w) { - const WellControls* wc = wells().ctrls[w]; - updateWellStateWithTarget(wc, updated_control_index[w], w, xw); - } - } */ } diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index e7bf238b8..4b262a06c 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -24,10 +24,6 @@ namespace Opm { , has_polymer_(GET_PROP_VALUE(TypeTag, EnablePolymer)) , current_timeIdx_(current_timeIdx) , rate_converter_(rate_converter) - , well_perforation_efficiency_factors_((wells_!=nullptr ? wells_->well_connpos[wells_->number_of_wells] : 0), 1.0) - , well_perforation_densities_( wells_ ? wells_arg->well_connpos[wells_arg->number_of_wells] : 0) - , well_perforation_pressure_diffs_( wells_ ? wells_arg->well_connpos[wells_arg->number_of_wells] : 0) - , wellVariables_( wells_ ? (wells_arg->number_of_wells * numWellEq) : 0) { createWellContainer(wells_arg); } @@ -507,7 +503,6 @@ namespace Opm { well_state = well_state0; setWellSolutions(well_state); // also recover the old well controls - // TODO: well_solutions_ for each well not recovered here. for (int w = 0; w < nw; ++w) { WellControls* wc = well_container_[w]->wellControls(); well_controls_set_current(wc, well_state.currentControls()[w]); @@ -586,6 +581,8 @@ namespace Opm { } } + // TODO: there should be a better way to do the following, while I did not find + // a direct way to handle boolean variables there. { const auto& grid = ebosSimulator.gridManager().grid(); int value = 0; @@ -995,7 +992,6 @@ namespace Opm { StandardWellsDense:: computeRepRadiusPerfLength(const Grid& grid) { - // TODO, the function does not work for parallel running // to be fixed later. int number_of_cells = Opm::UgGridHelpers::numCells(grid); From f1c0e848782a1e5517988f8b5adf9a843874cfdf Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 10 Aug 2017 15:27:05 +0200 Subject: [PATCH 085/104] cleaning up for StandardWellsDense. --- opm/autodiff/BlackoilModelEbos.hpp | 5 +- opm/autodiff/StandardWellsDense.hpp | 81 +++++++++++------------- opm/autodiff/StandardWellsDense_impl.hpp | 53 ++++++---------- 3 files changed, 58 insertions(+), 81 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 2fb37cf81..9022b9677 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -1539,13 +1539,10 @@ namespace Opm { const StandardWellsDense& wellModel() const { return well_model_; } - /// return the Well struct in the StandardWells - const Wells& wells() const { return well_model_.wells(); } - /// return true if wells are available in the reservoir bool wellsActive() const { return well_model_.wellsActive(); } - int numWells() const { return wellsActive() ? wells().number_of_wells : 0; } + int numWells() const { return well_model_.numWells(); } /// return true if wells are available on this process bool localWellsActive() const { return well_model_.localWellsActive(); } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index ed9f4fb87..48880dcad 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -135,7 +135,7 @@ namespace Opm { void assembleWellEq(Simulator& ebosSimulator, const double dt, WellState& well_state, - bool only_wells); + bool only_wells) const; // substract Binv(D)rw from r; void apply( BVector& r) const; @@ -158,9 +158,9 @@ namespace Opm { int numCells() const; - void resetWellControlFromState(const WellState& xw) const; + int numWells() const; - const Wells& wells() const; + void resetWellControlFromState(const WellState& xw) const; const Wells* wellsPointer() const; @@ -172,26 +172,11 @@ namespace Opm { /// return true if wells are available on this process bool localWellsActive() const; - void setWellVariables(); - - void computeAccumWells(); - - SimulatorReport solveWellEq(Simulator& ebosSimulator, - const double dt, - WellState& well_state); - - void printIf(const int c, const double x, const double y, const double eps, const std::string type) const; - std::vector residual() const; bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg) const; - void computeWellConnectionPressures(const Simulator& ebosSimulator, - const WellState& xw); - - void updateWellControls(WellState& xw) const; - /// upate the dynamic lists related to economic limits void updateListEconLimited(const Schedule& schedule, const int current_step, @@ -199,12 +184,6 @@ namespace Opm { const WellState& well_state, DynamicListEconLimited& list_econ_limited) const; - // Calculating well potentials for each well - // TODO: getBhp() will be refactored to reduce the duplication of the code calculating the bhp from THP. - void computeWellPotentials(const Simulator& ebosSimulator, - const WellState& well_state, - std::vector& well_potentials) const; - // TODO: some preparation work, mostly related to group control and RESV, // at the beginning of each time step (Not report step) void prepareTimeStep(const Simulator& ebos_simulator, @@ -212,21 +191,6 @@ namespace Opm { WellCollection* wellCollection() const; - const std::vector& - wellPerfEfficiencyFactors() const; - - void calculateEfficiencyFactors(); - - void computeWellVoidageRates(const WellState& well_state, - std::vector& well_voidage_rates, - std::vector& voidage_conversion_coeffs) const; - - void applyVREPGroupControl(WellState& well_state) const; - - void updateGroupControls(WellState& well_state) const; - - /// setting the well_solutions_ based on well_state. - void setWellSolutions(const WellState& well_state) const; protected: bool wells_active_; @@ -265,14 +229,17 @@ namespace Opm { std::vector pv_; - std::vector wells_rep_radius_; - std::vector wells_perf_length_; - std::vector wells_bore_diameter_; - long int global_nc_; mutable BVector scaleAddRes_; + void updateWellControls(WellState& xw) const; + + void updateGroupControls(WellState& well_state) const; + + // setting the well_solutions_ based on well_state. + void setWellSolutions(const WellState& well_state) const; + void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; void computeRepRadiusPerfLength(const Grid& grid); @@ -280,6 +247,34 @@ namespace Opm { void computeAverageFormationFactor(Simulator& ebosSimulator, std::vector& B_avg) const; + + void applyVREPGroupControl(WellState& well_state) const; + + void computeWellVoidageRates(const WellState& well_state, + std::vector& well_voidage_rates, + std::vector& voidage_conversion_coeffs) const; + + // Calculating well potentials for each well + // TODO: getBhp() will be refactored to reduce the duplication of the code calculating the bhp from THP. + void computeWellPotentials(const Simulator& ebosSimulator, + const WellState& well_state, + std::vector& well_potentials) const; + + const std::vector& wellPerfEfficiencyFactors() const; + + void calculateEfficiencyFactors(); + + void computeWellConnectionPressures(const Simulator& ebosSimulator, + const WellState& xw) const; + + SimulatorReport solveWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state) const; + + void computeAccumWells() const; + + void setWellVariables() const; + }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 4b262a06c..87093bd19 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -102,6 +102,18 @@ namespace Opm { + template + int + StandardWellsDense:: + numWells() const + { + return number_of_wells_; + } + + + + + template void StandardWellsDense:: @@ -168,7 +180,6 @@ namespace Opm { } updateWellControls(well_state); - updateGroupControls(well_state); // Set the primary variables for the wells setWellVariables(); @@ -197,7 +208,7 @@ namespace Opm { assembleWellEq(Simulator& ebosSimulator, const double dt, WellState& well_state, - bool only_wells) + bool only_wells) const { for (int w = 0; w < number_of_wells_; ++w) { well_container_[w]->assembleWellEq(ebosSimulator, dt, well_state, only_wells); @@ -362,19 +373,6 @@ namespace Opm { - template - const Wells& - StandardWellsDense:: - wells() const - { - assert(wells_ != 0); - return *(wells_); - } - - - - - template const Wells* StandardWellsDense:: @@ -426,7 +424,7 @@ namespace Opm { template void StandardWellsDense:: - setWellVariables() + setWellVariables() const { for (auto& well : well_container_) { well->setWellVariables(); @@ -440,7 +438,7 @@ namespace Opm { template void StandardWellsDense:: - computeAccumWells() + computeAccumWells() const { for (auto& well : well_container_) { well->computeAccumWell(); @@ -456,7 +454,7 @@ namespace Opm { StandardWellsDense:: solveWellEq(Simulator& ebosSimulator, const double dt, - WellState& well_state) + WellState& well_state) const { const int nw = number_of_wells_; WellState well_state0 = well_state; @@ -494,7 +492,6 @@ namespace Opm { if( wellsActive() ) { updateWellControls(well_state); - updateGroupControls(well_state); setWellVariables(); } } while (it < 15); @@ -519,20 +516,6 @@ namespace Opm { - template - void - StandardWellsDense:: - printIf(const int c, const double x, const double y, const double eps, const std::string type) const - { - if (std::abs(x-y) > eps) { - std::cout << type << " " << c << ": "< std::vector StandardWellsDense:: @@ -630,7 +613,7 @@ namespace Opm { void StandardWellsDense:: computeWellConnectionPressures(const Simulator& ebosSimulator, - const WellState& xw) + const WellState& xw) const { if( ! localWellsActive() ) return ; @@ -659,6 +642,8 @@ namespace Opm { for (const auto& well : well_container_) { well->updateWellControl(xw, logger); } + + updateGroupControls(xw); } From 66b5226039eff6bc135779602618ea43f6e4157f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 10 Aug 2017 16:00:27 +0200 Subject: [PATCH 086/104] removing some unused variables to reduce warnings. --- opm/autodiff/BlackoilModelEbos.hpp | 1 - opm/autodiff/StandardWell_impl.hpp | 3 --- opm/autodiff/StandardWellsDense.hpp | 2 -- opm/autodiff/StandardWellsDense_impl.hpp | 27 ++---------------------- 4 files changed, 2 insertions(+), 31 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 9022b9677..90138d279 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -824,7 +824,6 @@ namespace Opm { const double dt = timer.currentStepLength(); const double tol_mb = param_.tolerance_mb_; const double tol_cnv = param_.tolerance_cnv_; - const double tol_wells = param_.tolerance_wells_; const int np = numPhases(); const int numComp = numComponents(); diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 71af53dbe..f88dd773b 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -2038,9 +2038,6 @@ namespace Opm } } - // the number of wells - const int nw = well_state.bhp().size(); - switch (well_controls_get_current_type(wc)) { case THP: case BHP: { diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 48880dcad..1865a9c88 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -150,8 +150,6 @@ namespace Opm { // xw to update Well State void applySolutionWellState(const BVector& x, WellState& well_state) const; - int flowToEbosPvIdx( const int flowPv ) const; - int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; int numPhases() const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 87093bd19..10103986d 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -56,7 +56,6 @@ namespace Opm { calculateEfficiencyFactors(); - const int nw = number_of_wells_; const int nc = numCells(); #ifndef NDEBUG @@ -298,27 +297,6 @@ namespace Opm { - template - int - StandardWellsDense:: - flowToEbosPvIdx( const int flowPv ) const - { - const int flowToEbos[ 3 ] = { - BlackoilIndices::pressureSwitchIdx, - BlackoilIndices::waterSaturationIdx, - BlackoilIndices::compositionSwitchIdx - }; - - if (flowPv > 2 ) - return flowPv; - - return flowToEbos[ flowPv ]; - } - - - - - template int StandardWellsDense:: @@ -838,8 +816,8 @@ namespace Opm { // We only store the conversion coefficients for all the injection wells. // Later, more delicate model will be implemented here. // And for the moment, group control can only work for serial running. - const int nw = well_state.numWells(); - const int np = well_state.numPhases(); + const int nw = numWells(); + const int np = numPhases(); // we calculate the voidage rate for each well, that means the sum of all the phases. well_voidage_rates.resize(nw, 0); @@ -1002,7 +980,6 @@ namespace Opm { std::vector& B_avg) const { const int np = numPhases(); - const int numComp = numComponents(); const auto& grid = ebosSimulator.gridManager().grid(); const auto& gridView = grid.leafGridView(); From 8a12ec677fbaeb847133b9d0616030b17ceafcb0 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 10 Aug 2017 16:56:03 +0200 Subject: [PATCH 087/104] more cleaning up not funtional change. --- opm/autodiff/BlackoilModelEbos.hpp | 3 -- opm/autodiff/StandardWell_impl.hpp | 24 --------- opm/autodiff/StandardWellsDense.hpp | 4 -- opm/autodiff/StandardWellsDense_impl.hpp | 69 ++---------------------- 4 files changed, 3 insertions(+), 97 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 90138d279..1db3bbd2d 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -1538,9 +1538,6 @@ namespace Opm { const StandardWellsDense& wellModel() const { return well_model_; } - /// return true if wells are available in the reservoir - bool wellsActive() const { return well_model_.wellsActive(); } - int numWells() const { return well_model_.numWells(); } /// return true if wells are available on this process diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index f88dd773b..68f93e496 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1626,30 +1626,6 @@ namespace Opm } } - /* if ( terminal_output_ ) - { - // Only rank 0 does print to std::cout - if (iteration == 0) { - std::string msg; - msg = "Iter"; - for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { - const std::string& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); - msg += " W-FLUX(" + phaseName + ")"; - } - OpmLog::note(msg); - } - - std::ostringstream ss; - const std::streamsize oprec = ss.precision(3); - const std::ios::fmtflags oflags = ss.setf(std::ios::scientific); - ss << std::setw(4) << iteration; - for (int compIdx = 0; compIdx < numComp; ++compIdx) { - ss << std::setw(11) << well_flux_residual[compIdx]; - } - ss.precision(oprec); - ss.flags(oflags); - OpmLog::note(ss.str()); - } */ return converged_Well; } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 1865a9c88..4c0256e17 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -39,8 +39,6 @@ #include #include #include -#include -#include #include #include #include @@ -170,8 +168,6 @@ namespace Opm { /// return true if wells are available on this process bool localWellsActive() const; - std::vector residual() const; - bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 10103986d..2296c3c35 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -494,37 +494,6 @@ namespace Opm { - template - std::vector - StandardWellsDense:: - residual() const - { - // TODO: to decide later whether to output this - // Even yes, we do not need resWell_. We will use the values - // from each individual well. - /* if( ! wellsActive() ) - { - return std::vector(); - } - - const int nw = number_of_wells_; - const int numComp = numComponents(); - std::vector res(numEq*nw, 0.0); - for( int compIdx = 0; compIdx < numComp; ++compIdx) { - - for (int wellIdx = 0; wellIdx < nw; ++wellIdx) { - int idx = wellIdx + nw*compIdx; - res[idx] = resWell_[ wellIdx ][ compIdx ]; - } - } - return res; */ - return std::vector(1, 0.0); // to disable warning, unusable - } - - - - - template bool StandardWellsDense:: @@ -533,53 +502,21 @@ namespace Opm { { bool converged_well = true; - // TODO: to check the strategy here // currently, if there is any well not converged, we consider the well eqautions do not get converged for (const auto& well : well_container_) { if ( !well->getWellConvergence(ebosSimulator, B_avg, param_) ) { converged_well = false; - // break; // TODO: no need to check other wells? } } - // TODO: there should be a better way to do the following, while I did not find - // a direct way to handle boolean variables there. { const auto& grid = ebosSimulator.gridManager().grid(); - int value = 0; - if (converged_well) { - value = 1; - } - if (grid.comm().min(value) < 1) { - converged_well = false; - } + int value = converged_well ? 1 : 0; + + converged_well = grid.comm().min(value); } - // TODO: to think about the output here. - /* if ( terminal_output_ ) - { - // Only rank 0 does print to std::cout - if (iteration == 0) { - std::string msg; - msg = "Iter"; - for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { - const std::string& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); - msg += " W-FLUX(" + phaseName + ")"; - } - OpmLog::note(msg); - } - std::ostringstream ss; - const std::streamsize oprec = ss.precision(3); - const std::ios::fmtflags oflags = ss.setf(std::ios::scientific); - ss << std::setw(4) << iteration; - for (int compIdx = 0; compIdx < numComp; ++compIdx) { - ss << std::setw(11) << well_flux_residual[compIdx]; - } - ss.precision(oprec); - ss.flags(oflags); - OpmLog::note(ss.str()); - } */ return converged_well; } From fe3d2f91e00fdb1806624df634bd4e024dbfb997 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 11 Aug 2017 11:10:59 +0200 Subject: [PATCH 088/104] adding functions to calcuate between thp and bhp to reduce some code duplication in StandardWell --- opm/autodiff/StandardWell.hpp | 5 + opm/autodiff/StandardWell_impl.hpp | 272 +++++++++++++---------------- 2 files changed, 128 insertions(+), 149 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 47cbe1805..af7b8fda4 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -285,6 +285,11 @@ namespace Opm const double initial_bhp, // bhp from BHP constraints const std::vector& initial_potential) const; + template + ValueType calculateBhpFromThp(const std::vector& rates, const int control_index) const; + + double calculateThpFromBhp(const std::vector& rates, const int control_index, const double bhp) const; + // get the mobility for specific perforation void getMobility(const Simulator& ebosSimulator, const int perf, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 68f93e496..5da0e7644 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -132,40 +132,19 @@ namespace Opm return bhp; } else if (well_controls_get_current_type(wc) == THP) { const int control = well_controls_get_current(wc); - const double thp = well_controls_get_current_target(wc); - const double alq = well_controls_iget_alq(wc, control); - const int table_id = well_controls_iget_vfp(wc, control); - EvalWell aqua = 0.0; - EvalWell liquid = 0.0; - EvalWell vapour = 0.0; - EvalWell bhp = 0.0; - double vfp_ref_depth = 0.0; const Opm::PhaseUsage& pu = phaseUsage(); - + std::vector rates(3, 0.0); if (active()[ Water ]) { - aqua = getQs(pu.phase_pos[ Water]); + rates[ Water ]= getQs(pu.phase_pos[ Water]); } if (active()[ Oil ]) { - liquid = getQs(pu.phase_pos[ Oil ]); + rates[ Oil ] = getQs(pu.phase_pos[ Oil ]); } if (active()[ Gas ]) { - vapour = getQs(pu.phase_pos[ Gas ]); + rates[ Gas ] = getQs(pu.phase_pos[ Gas ]); } - if (well_type_ == INJECTOR) { - bhp = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp); - vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); - } else { - bhp = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq); - vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); - } - - // pick the density in the top layer - const double rho = perf_densities_[0]; - const double well_ref_depth = ref_depth_; - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - bhp -= dp; - return bhp; + return calculateBhpFromThp(rates, control); } return well_variables_[XvarWell]; @@ -955,49 +934,20 @@ namespace Opm if (well_controls_iget_type(wc, current) == THP) { // Calculate bhp from thp control and well rates - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; + std::vector rates(3, 0.0); // the vfp related only supports three phases for the moment const Opm::PhaseUsage& pu = phaseUsage(); - if (active()[ Water ]) { - aqua = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Water ] ]; + rates[ Water ] = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Oil ] ]; + rates[ Oil ]= well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Gas ] ]; + rates[ Gas ]= well_state.wellRates()[index_of_well_ * np + pu.phase_pos[ Gas ] ]; } - const int vfp = well_controls_iget_vfp(wc, current); - const double& thp = well_controls_iget_target(wc, current); - const double& alq = well_controls_iget_alq(wc, current); - - // Set *BHP* target by calculating bhp from THP - const WellType& well_type = well_type_; - // pick the density in the top layer - const double rho = perf_densities_[0]; - const double well_ref_depth = ref_depth_; - - if (well_type == INJECTOR) { - const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - well_state.bhp()[index_of_well_] = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; - } - else if (well_type == PRODUCER) { - const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - well_state.bhp()[index_of_well_] = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; - } - else { - OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); - } + well_state.bhp()[index_of_well_] = calculateBhpFromThp(rates, current); } } break; @@ -1056,42 +1006,21 @@ namespace Opm double vapour = 0.0; const Opm::PhaseUsage& pu = phaseUsage(); + std::vector rates(3, 0.0); if (active()[ Water ]) { - aqua = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Water ] ]; + rates[ Water ] = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Oil ] ]; + rates[ Oil ] = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Gas ] ]; + rates[ Gas ] = well_state.wellRates()[index_of_well_*np + pu.phase_pos[ Gas ] ]; } - const double alq = well_controls_iget_alq(wc, ctrl_index); - const int table_id = well_controls_iget_vfp(wc, ctrl_index); + const double bhp = well_state.bhp()[index_of_well_]; - const WellType& well_type = well_type_; - const double rho = perf_densities_[0]; - const double well_ref_depth = ref_depth_; - if (well_type == INJECTOR) { - const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - const double bhp = well_state.bhp()[index_of_well_]; - - well_state.thp()[index_of_well_] = vfp_properties_->getInj()->thp(table_id, aqua, liquid, vapour, bhp + dp); - } else if (well_type == PRODUCER) { - const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - const double bhp = well_state.bhp()[index_of_well_]; - - well_state.thp()[index_of_well_] = vfp_properties_->getProd()->thp(table_id, aqua, liquid, vapour, bhp + dp, alq); - } else { - OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); - } + well_state.thp()[index_of_well_] = calculateThpFromBhp(rates, ctrl_index, bhp); } // the THP control is found, we leave the loop now @@ -1150,51 +1079,23 @@ namespace Opm case THP: { xw.thp()[well_index] = target; - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - - const Opm::PhaseUsage& pu = *phase_usage_; - + const Opm::PhaseUsage& pu = phaseUsage(); + std::vector rates(3, 0.0); if (active()[ Water ]) { - aqua = xw.wellRates()[well_index*np + pu.phase_pos[ Water ] ]; + rates[ Water ] = xw.wellRates()[well_index*np + pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = xw.wellRates()[well_index*np + pu.phase_pos[ Oil ] ]; + rates[ Oil ] = xw.wellRates()[well_index*np + pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = xw.wellRates()[well_index*np + pu.phase_pos[ Gas ] ]; + rates[ Gas ] = xw.wellRates()[well_index*np + pu.phase_pos[ Gas ] ]; } const int table_id = well_controls_iget_vfp(wc, current); const double& thp = well_controls_iget_target(wc, current); const double& alq = well_controls_iget_alq(wc, current); - //Set *BHP* target by calculating bhp from THP - - // pick the density in the top layer - const double rho = perf_densities_[0]; - const double well_ref_depth = ref_depth_; - - // TODO: make the following a function and we call it so many times. - if (well_type_ == INJECTOR) { - - const double vfp_ref_depth = vfp_properties_->getInj()->getTable(table_id)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - xw.bhp()[well_index] = vfp_properties_->getInj()->bhp(table_id, aqua, liquid, vapour, thp) - dp; - } - else if (well_type_ == PRODUCER) { - const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); - - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - - xw.bhp()[well_index] = vfp_properties_->getProd()->bhp(table_id, aqua, liquid, vapour, thp, alq) - dp; - } - else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); - } + xw.bhp()[well_index] = calculateBhpFromThp(rates, current); break; } @@ -1868,43 +1769,25 @@ namespace Opm const Opm::PhaseUsage& pu = *phase_usage_; + std::vector rates(3, 0.0); if (active()[ Water ]) { - aqua = potentials[pu.phase_pos[ Water ] ]; + rates[ Water ] = potentials[pu.phase_pos[ Water ] ]; } if (active()[ Oil ]) { - liquid = potentials[pu.phase_pos[ Oil ] ]; + rates[ Oil ] = potentials[pu.phase_pos[ Oil ] ]; } if (active()[ Gas ]) { - vapour = potentials[pu.phase_pos[ Gas ] ]; + rates[ Gas ] = potentials[pu.phase_pos[ Gas ] ]; } - const int vfp = well_controls_iget_vfp(well_controls_, ctrl_index); - const double thp = well_controls_iget_target(well_controls_, ctrl_index); - const double alq = well_controls_iget_alq(well_controls_, ctrl_index); + const double bhp_calculated = calculateBhpFromThp(rates, ctrl_index); - // Calculating the BHP value based on THP - // TODO: check whether it is always correct to do calculation based on the depth of the first perforation. - const double rho = perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface - const double well_ref_depth = ref_depth_; - if (well_type_ == INJECTOR) { - const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - const double bhp_calculated = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; - // apply the strictest of the bhp controlls i.e. smallest bhp for injectors - if (bhp_calculated < bhp) { - bhp = bhp_calculated; - } + if (well_type_ == INJECTOR && bhp_calculated < bhp ) { + bhp = bhp_calculated; } - else if (well_type_ == PRODUCER) { - const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); - const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_); - const double bhp_calculated = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; - // apply the strictest of the bhp controlls i.e. largest bhp for producers - if (bhp_calculated > bhp) { - bhp = bhp_calculated; - } - } else { - OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); + + if (well_type_ == PRODUCER && bhp_calculated > bhp) { + bhp = bhp_calculated; } } } @@ -2088,4 +1971,95 @@ namespace Opm } } + + + + + template + template + ValueType + StandardWell:: + calculateBhpFromThp(const std::vector& rates, + const int control_index) const + { + assert(int(rates.size()) == 3); // the vfp related only supports three phases now. + + const ValueType aqua = rates[Water]; + const ValueType liquid = rates[Oil]; + const ValueType vapour = rates[Gas]; + + const int vfp = well_controls_iget_vfp(well_controls_, control_index); + const double& thp = well_controls_iget_target(well_controls_, control_index); + const double& alq = well_controls_iget_alq(well_controls_, control_index); + + // pick the density in the top layer + const double rho = perf_densities_[0]; + + ValueType bhp = 0.; + if (well_type_ == INJECTOR) { + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(ref_depth_, vfp_ref_depth, rho, gravity_); + + bhp = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp; + } + else if (well_type_ == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(ref_depth_, vfp_ref_depth, rho, gravity_); + + bhp = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp; + } + else { + OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); + } + + return bhp; + } + + + + + + template + double + StandardWell:: + calculateThpFromBhp(const std::vector& rates, + const int control_index, + const double bhp) const + { + assert(int(rates.size()) == 3); // the vfp related only supports three phases now. + + const double aqua = rates[Water]; + const double liquid = rates[Oil]; + const double vapour = rates[Gas]; + + const int vfp = well_controls_iget_vfp(well_controls_, control_index); + const double& alq = well_controls_iget_alq(well_controls_, control_index); + + // pick the density in the top layer + const double rho = perf_densities_[0]; + + double thp = 0.0; + if (well_type_ == INJECTOR) { + const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(ref_depth_, vfp_ref_depth, rho, gravity_); + + thp = vfp_properties_->getInj()->thp(vfp, aqua, liquid, vapour, bhp + dp); + } + else if (well_type_ == PRODUCER) { + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth(); + + const double dp = wellhelpers::computeHydrostaticCorrection(ref_depth_, vfp_ref_depth, rho, gravity_); + + thp = vfp_properties_->getProd()->thp(vfp, aqua, liquid, vapour, bhp + dp, alq); + } + else { + OPM_THROW(std::logic_error, "Expected INJECTOR or PRODUCER well"); + } + + return thp; + } + } From 68edfd723524a8f36139227abe36a2b0cc0896ee Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 11 Aug 2017 13:33:56 +0200 Subject: [PATCH 089/104] removing several unused variables. --- opm/autodiff/StandardWell_impl.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 5da0e7644..d164a8717 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1001,10 +1001,6 @@ namespace Opm const double thp_target = well_controls_iget_target(wc, current); well_state.thp()[index_of_well_] = thp_target; } else { // otherwise we calculate the thp from the bhp value - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - const Opm::PhaseUsage& pu = phaseUsage(); std::vector rates(3, 0.0); @@ -1763,10 +1759,6 @@ namespace Opm for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { if (well_controls_iget_type(well_controls_, ctrl_index) == THP) { - double aqua = 0.0; - double liquid = 0.0; - double vapour = 0.0; - const Opm::PhaseUsage& pu = *phase_usage_; std::vector rates(3, 0.0); From 7a9fc2132e7f43ab52200769381796a8106332cb Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 11 Aug 2017 14:51:29 +0200 Subject: [PATCH 090/104] more cleaning up of StandardWellsDense. not functional change. --- .../SimulatorFullyImplicitBlackoilEbos.hpp | 19 ------- opm/autodiff/StandardWellsDense.hpp | 55 +++++++++---------- opm/autodiff/StandardWellsDense_impl.hpp | 24 ++------ 3 files changed, 31 insertions(+), 67 deletions(-) diff --git a/opm/autodiff/SimulatorFullyImplicitBlackoilEbos.hpp b/opm/autodiff/SimulatorFullyImplicitBlackoilEbos.hpp index cc7b4bc6a..5085427fa 100644 --- a/opm/autodiff/SimulatorFullyImplicitBlackoilEbos.hpp +++ b/opm/autodiff/SimulatorFullyImplicitBlackoilEbos.hpp @@ -141,7 +141,6 @@ public: ExtraData extra; failureReport_ = SimulatorReport(); - extractLegacyPoreVolume_(); extractLegacyDepth_(); // communicate the initial solution to ebos @@ -480,7 +479,6 @@ protected: activePhases, gravity, legacyDepth_, - legacyPoreVolume_, globalNumCells, grid()); auto model = std::unique_ptr(new Model(ebosSimulator_, @@ -867,22 +865,6 @@ protected: } } - void extractLegacyPoreVolume_() - { - const auto& grid = ebosSimulator_.gridManager().grid(); - const unsigned numCells = grid.size(/*codim=*/0); - const auto& ebosProblem = ebosSimulator_.problem(); - const auto& ebosModel = ebosSimulator_.model(); - - legacyPoreVolume_.resize(numCells); - for (unsigned cellIdx = 0; cellIdx < numCells; ++cellIdx) { - // todo (?): respect rock compressibility - legacyPoreVolume_[cellIdx] = - ebosModel.dofTotalVolume(cellIdx) - *ebosProblem.porosity(cellIdx); - } - } - void extractLegacyDepth_() { const auto& grid = ebosSimulator_.gridManager().grid(); @@ -1009,7 +991,6 @@ protected: Simulator& ebosSimulator_; std::vector legacyCellPvtRegionIdx_; - std::vector legacyPoreVolume_; std::vector legacyDepth_; typedef typename Solver::SolverParameters SolverParameters; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 4c0256e17..ed577c8bb 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -104,37 +104,17 @@ namespace Opm { const std::vector& active_arg, const double gravity_arg, const std::vector& depth_arg, - const std::vector& pv_arg, long int global_nc, const Grid& grid); void setVFPProperties(const VFPProperties* vfp_properties_arg); - /// The number of components in the model. - int numComponents() const - { - if (numPhases() == 2) { - return 2; - } - int numComp = FluidSystem::numComponents; - if (has_solvent_) { - numComp ++; - } - - return numComp; - } - SimulatorReport assemble(Simulator& ebosSimulator, const int iterationIdx, const double dt, WellState& well_state); - void assembleWellEq(Simulator& ebosSimulator, - const double dt, - WellState& well_state, - bool only_wells) const; - // substract Binv(D)rw from r; void apply( BVector& r) const; @@ -148,16 +128,8 @@ namespace Opm { // xw to update Well State void applySolutionWellState(const BVector& x, WellState& well_state) const; - int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; - - int numPhases() const; - - int numCells() const; - int numWells() const; - void resetWellControlFromState(const WellState& xw) const; - const Wells* wellsPointer() const; /// return true if wells are available in the reservoir @@ -221,7 +193,8 @@ namespace Opm { std::vector active_; const RateConverterType& rate_converter_; - std::vector pv_; + // the number of the cells in the local grid + int number_of_cells_; long int global_nc_; @@ -269,6 +242,30 @@ namespace Opm { void setWellVariables() const; + // The number of components in the model. + int numComponents() const + { + if (numPhases() == 2) { + return 2; + } + int numComp = FluidSystem::numComponents; + if (has_solvent_) { + numComp ++; + } + + return numComp; + } + + int numPhases() const; + + int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; + + void resetWellControlFromState(const WellState& xw) const; + + void assembleWellEq(Simulator& ebosSimulator, + const double dt, + WellState& well_state, + bool only_wells) const; }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 2296c3c35..42748cc0c 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -39,7 +39,6 @@ namespace Opm { const std::vector& active_arg, const double gravity_arg, const std::vector& depth_arg, - const std::vector& pv_arg, long int global_nc, const Grid& grid) { @@ -52,12 +51,9 @@ namespace Opm { phase_usage_ = phase_usage_arg; active_ = active_arg; - pv_ = pv_arg; calculateEfficiencyFactors(); - const int nc = numCells(); - #ifndef NDEBUG const auto& pu = phase_usage_; const int np = pu.num_phases; @@ -74,11 +70,12 @@ namespace Opm { } } + number_of_cells_ = Opm::UgGridHelpers::numCells(grid); // do the initialization for all the wells // TODO: to see whether we can postpone of the intialization of the well containers to // optimize the usage of the following several member variables for (auto& well : well_container_) { - well->init(&phase_usage_, &active_, depth_arg, gravity_arg, nc); + well->init(&phase_usage_, &active_, depth_arg, gravity_arg, number_of_cells_); } } @@ -323,18 +320,6 @@ namespace Opm { - template - int - StandardWellsDense:: - numCells() const - { - return pv_.size(); - } - - - - - template void StandardWellsDense:: @@ -668,6 +653,8 @@ namespace Opm { computeWellPotentials(ebos_simulator, well_state, well_potentials); // update/setup guide rates for each well based on the well_potentials + // TODO: this is one of two places that still need Wells struct. In this function, only the well names + // well types are used, probably the order of the wells to locate the correct values in well_potentials. well_collection_->setGuideRatesWithPotentials(wellsPointer(), phase_usage_, well_potentials); } @@ -894,11 +881,10 @@ namespace Opm { { // TODO, the function does not work for parallel running // to be fixed later. - int number_of_cells = Opm::UgGridHelpers::numCells(grid); const int* global_cell = Opm::UgGridHelpers::globalCell(grid); std::map cartesian_to_compressed; - setupCompressedToCartesian(global_cell, number_of_cells, + setupCompressedToCartesian(global_cell, number_of_cells_, cartesian_to_compressed); for (const auto& well : well_container_) { From 277d26df8a3c91169fc9e5c836b889005df1984b Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 14 Aug 2017 13:36:13 +0200 Subject: [PATCH 091/104] cleaning up well residual related in BlackoilModelEbos TODO: how to output the information for debugging well iteration process. --- opm/autodiff/BlackoilModelEbos.hpp | 37 +++++++++--------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index 1db3bbd2d..b22739ee7 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -762,8 +762,7 @@ namespace Opm { const double pvSumLocal, std::vector< Scalar >& R_sum, std::vector< Scalar >& maxCoeff, - std::vector< Scalar >& B_avg, - std::vector< Scalar >& maxNormWell) + std::vector< Scalar >& B_avg) { // Compute total pore volume (use only owned entries) double pvSum = pvSumLocal; @@ -775,13 +774,12 @@ namespace Opm { std::vector< Scalar > maxBuffer; const int numComp = B_avg.size(); sumBuffer.reserve( 2*numComp + 1 ); // +1 for pvSum - maxBuffer.reserve( 2*numComp ); + maxBuffer.reserve( numComp ); for( int compIdx = 0; compIdx < numComp; ++compIdx ) { sumBuffer.push_back( B_avg[ compIdx ] ); sumBuffer.push_back( R_sum[ compIdx ] ); maxBuffer.push_back( maxCoeff[ compIdx ] ); - maxBuffer.push_back( maxNormWell[ compIdx ] ); } // Compute total pore volume @@ -797,11 +795,14 @@ namespace Opm { for( int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx ) { B_avg[ compIdx ] = sumBuffer[ buffIdx ]; - maxCoeff[ compIdx ] = maxBuffer[ buffIdx ]; ++buffIdx; R_sum[ compIdx ] = sumBuffer[ buffIdx ]; - maxNormWell[ compIdx ] = maxBuffer[ buffIdx ]; + } + + for( int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx ) + { + maxCoeff[ compIdx ] = maxBuffer[ buffIdx ]; } // restore global pore volume @@ -831,7 +832,6 @@ namespace Opm { Vector R_sum(numComp, 0.0 ); Vector B_avg(numComp, 0.0 ); Vector maxCoeff(numComp, std::numeric_limits< Scalar >::lowest() ); - Vector maxNormWell(numComp, 0.0 ); const auto& ebosModel = ebosSimulator_.model(); const auto& ebosProblem = ebosSimulator_.problem(); @@ -891,20 +891,13 @@ namespace Opm { B_avg[ i ] /= Scalar( global_nc_ ); } - // compute maximum of local well residuals - // const Vector& wellResidual = wellModel().residual(); - // const int nw = wellResidual.size() / numComp; - // assert(nw * numComp == int(wellResidual.size())); - /* for( int compIdx = 0; compIdx < numComp; ++compIdx ) - { - for ( int w = 0; w < nw; ++w ) { - maxNormWell[compIdx] = std::max(maxNormWell[compIdx], std::abs(wellResidual[nw*compIdx + w])); - } - } */ + // TODO: we remove the maxNormWell for now because the convergence of wells are on a individual well basis. + // Anyway, we need to provide some infromation to help debug the well iteration process. + // compute global sum and max of quantities const double pvSum = convergenceReduction(grid_.comm(), pvSumLocal, - R_sum, maxCoeff, B_avg, maxNormWell ); + R_sum, maxCoeff, B_avg); Vector CNV(numComp); Vector mass_balance_residual(numComp); @@ -958,9 +951,6 @@ namespace Opm { for (int compIdx = 0; compIdx < numComp; ++compIdx) { msg += " CNV(" + key[ compIdx ] + ") "; } - /* for (int compIdx = 0; compIdx < numComp; ++compIdx) { - msg += " W-FLUX(" + key[ compIdx ] + ")"; - } */ OpmLog::note(msg); } std::ostringstream ss; @@ -973,9 +963,6 @@ namespace Opm { for (int compIdx = 0; compIdx < numComp; ++compIdx) { ss << std::setw(11) << CNV[compIdx]; } - // for (int compIdx = 0; compIdx < numComp; ++compIdx) { - // ss << std::setw(11) << well_flux_residual[compIdx]; - // } ss.precision(oprec); ss.flags(oflags); OpmLog::note(ss.str()); @@ -986,12 +973,10 @@ namespace Opm { if (std::isnan(mass_balance_residual[phaseIdx]) || std::isnan(CNV[phaseIdx])) { - // || (phaseIdx < numPhases() && std::isnan(well_flux_residual[phaseIdx]))) { OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName); } if (mass_balance_residual[phaseIdx] > maxResidualAllowed() || CNV[phaseIdx] > maxResidualAllowed()) { - // || (phaseIdx < numPhases() && well_flux_residual[phaseIdx] > maxResidualAllowed())) { OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName); } } From a29be796e7d600a6872f966c208208ea660525f6 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 14 Aug 2017 14:11:58 +0200 Subject: [PATCH 092/104] adding comments about function calculateBhpFromThp. --- opm/autodiff/StandardWell_impl.hpp | 6 ++++++ opm/autodiff/StandardWellsDense.hpp | 10 +++++----- opm/autodiff/StandardWellsDense_impl.hpp | 1 - 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index d164a8717..65a81fcd3 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1974,6 +1974,12 @@ namespace Opm calculateBhpFromThp(const std::vector& rates, const int control_index) const { + // TODO: when well is under THP control, the BHP is dependent on the rates, + // the well rates is also dependent on the BHP, so it might need to do some iteration. + // However, when group control is involved, change of the rates might impacts other wells + // so iterations on a higher level will be required. Some investigation might be needed when + // we face problems under THP control. + assert(int(rates.size()) == 3); // the vfp related only supports three phases now. const ValueType aqua = rates[Water]; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index ed577c8bb..e7d1529ea 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -150,11 +150,6 @@ namespace Opm { const WellState& well_state, DynamicListEconLimited& list_econ_limited) const; - // TODO: some preparation work, mostly related to group control and RESV, - // at the beginning of each time step (Not report step) - void prepareTimeStep(const Simulator& ebos_simulator, - WellState& well_state); - WellCollection* wellCollection() const; @@ -266,6 +261,11 @@ namespace Opm { const double dt, WellState& well_state, bool only_wells) const; + + // some preparation work, mostly related to group control and RESV, + // at the beginning of each time step (Not report step) + void prepareTimeStep(const Simulator& ebos_simulator, + WellState& well_state); }; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 42748cc0c..8c17ebfbc 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -643,7 +643,6 @@ namespace Opm { if (well_collection_->requireWellPotentials()) { // calculate the well potentials - // TODO: for the purpose of group control, not tested yet setWellSolutions(well_state); setWellVariables(); computeWellConnectionPressures(ebos_simulator, well_state); From 52af70ad0e1ac442552c98e58291573501d3da08 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 15 Aug 2017 09:51:49 +0200 Subject: [PATCH 093/104] adding debugging output for solveWellEq to tell if the solveWellEq is successful. --- opm/autodiff/StandardWellsDense_impl.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 8c17ebfbc..eadfc0d56 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -459,7 +459,11 @@ namespace Opm { } } while (it < 15); - if (!converged) { + if (converged) { + OpmLog::debug("Well equation solution gets converged with " + std::to_string(it) + " iterations"); + } else { + OpmLog::debug("Well equation solution failed in getting converged with " + std::to_string(it) + " iterations"); + well_state = well_state0; setWellSolutions(well_state); // also recover the old well controls From a04d1a9393d07ecf4bbdddfb758c16b627a27ac2 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 17 Aug 2017 15:29:56 +0200 Subject: [PATCH 094/104] correcting a typo in CMakeLists_files.cmake. --- CMakeLists_files.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index ccdb2f455..799cbab21 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -240,7 +240,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/autodiff/StandardWells.hpp opm/autodiff/StandardWells_impl.hpp opm/autodiff/WellInterface.hpp - opm/autodiff/StandardWell.cpp + opm/autodiff/StandardWell.hpp opm/autodiff/StandardWellsDense.hpp opm/autodiff/StandardWellsSolvent.hpp opm/autodiff/StandardWellsSolvent_impl.hpp From f9b7094075d65d4d979300f737ddcfb98ac90b4f Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 18 Aug 2017 16:25:05 +0200 Subject: [PATCH 095/104] fixing of some comments no functional change. --- CMakeLists_files.cmake | 2 ++ opm/autodiff/BlackoilModelEbos.hpp | 8 ++++---- opm/autodiff/StandardWell.hpp | 3 --- opm/autodiff/StandardWell_impl.hpp | 19 +------------------ opm/autodiff/StandardWellsDense.hpp | 7 +------ opm/autodiff/StandardWellsDense_impl.hpp | 17 ++--------------- 6 files changed, 10 insertions(+), 46 deletions(-) diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 799cbab21..3e81d026f 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -240,7 +240,9 @@ list (APPEND PUBLIC_HEADER_FILES opm/autodiff/StandardWells.hpp opm/autodiff/StandardWells_impl.hpp opm/autodiff/WellInterface.hpp + opm/autodiff/WellInterface_impl.hpp opm/autodiff/StandardWell.hpp + opm/autodiff/StandardWell_impl.hpp opm/autodiff/StandardWellsDense.hpp opm/autodiff/StandardWellsSolvent.hpp opm/autodiff/StandardWellsSolvent_impl.hpp diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index b22739ee7..dcfa15aee 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -154,7 +154,7 @@ namespace Opm { /// \param[in] terminal_output request output to cout/cerr BlackoilModelEbos(Simulator& ebosSimulator, const ModelParameters& param, - const StandardWellsDense& well_model, + StandardWellsDense& well_model, RateConverterType& rate_converter, const NewtonIterationBlackoilInterface& linsolver, const bool terminal_output @@ -800,9 +800,9 @@ namespace Opm { R_sum[ compIdx ] = sumBuffer[ buffIdx ]; } - for( int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx ) + for( int compIdx = 0; compIdx < numComp; ++compIdx ) { - maxCoeff[ compIdx ] = maxBuffer[ buffIdx ]; + maxCoeff[ compIdx ] = maxBuffer[ compIdx ]; } // restore global pore volume @@ -1501,7 +1501,7 @@ namespace Opm { SimulatorReport failureReport_; // Well Model - StandardWellsDense well_model_; + StandardWellsDense& well_model_; /// \brief Whether we print something to std::cout bool terminal_output_; diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index af7b8fda4..df634a830 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -240,9 +240,6 @@ namespace Opm EvalWell extendEval(const Eval& in) const; - // TODO: maybe this type of function can go to some helper file. - void localInvert(DiagMatWell& istlA) const; - // xw = inv(D)*(rw - C*x) void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 65a81fcd3..669444caa 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -636,7 +636,7 @@ namespace Opm } // do the local inversion of D. - localInvert( invDuneD_ ); + invDuneD_[0][0].invert(); } @@ -1034,23 +1034,6 @@ namespace Opm - template - void - StandardWell:: - localInvert(DiagMatWell& istlA) const - { - for (auto row = istlA.begin(), rowend = istlA.end(); row != rowend; ++row ) { - for (auto col = row->begin(), colend = row->end(); col != colend; ++col ) { - //std::cout << (*col) << std::endl; - (*col).invert(); - } - } - } - - - - - template void StandardWell:: diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index e7d1529ea..ba4adf2a2 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -57,8 +57,6 @@ #include -#include - namespace Opm { @@ -130,8 +128,6 @@ namespace Opm { int numWells() const; - const Wells* wellsPointer() const; - /// return true if wells are available in the reservoir bool wellsActive() const; @@ -169,8 +165,7 @@ namespace Opm { // eventually, the wells_ above should be gone. // the name is just temporary // later, might make share_ptr const later. - // TODO: forget why make it share_ptr instead of unique_ptr - std::vector > > well_container_; + std::vector > > well_container_; // TODO: forgot why returning a vector here void createWellContainer(const Wells* wells_arg); diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index eadfc0d56..088eefe40 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -147,8 +147,7 @@ namespace Opm { } // Basically, we are handling all the wells as StandardWell for the moment - // TODO: to be changed when we begin introducing MultisegmentWell - well_container_.push_back(std::make_shared >(well_ecl, current_timeIdx_, wells_arg) ); + well_container_.emplace_back(new StandardWell(well_ecl, current_timeIdx_, wells_arg) ); } } } @@ -336,18 +335,6 @@ namespace Opm { - template - const Wells* - StandardWellsDense:: - wellsPointer() const - { - return wells_; - } - - - - - template bool StandardWellsDense:: @@ -658,7 +645,7 @@ namespace Opm { // update/setup guide rates for each well based on the well_potentials // TODO: this is one of two places that still need Wells struct. In this function, only the well names // well types are used, probably the order of the wells to locate the correct values in well_potentials. - well_collection_->setGuideRatesWithPotentials(wellsPointer(), phase_usage_, well_potentials); + well_collection_->setGuideRatesWithPotentials(wells_, phase_usage_, well_potentials); } applyVREPGroupControl(well_state); From a908bd7cf11db3ae69cf68f71c62ebcf36d96079 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 10:23:42 +0200 Subject: [PATCH 096/104] makding createWellContainer a static function. --- opm/autodiff/StandardWell.hpp | 7 +++--- opm/autodiff/StandardWellsDense.hpp | 10 +++++--- opm/autodiff/StandardWellsDense_impl.hpp | 31 +++++++++++++----------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index df634a830..98df23ff4 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -205,6 +205,9 @@ namespace Opm // pressure drop between different perforations std::vector perf_pressure_diffs_; + // residuals of the well equations + BVectorWell resWell_; + // two off-diagonal matrices OffDiagMatWell duneB_; OffDiagMatWell duneC_; @@ -214,10 +217,6 @@ namespace Opm // several vector used in the matrix calculation mutable BVectorWell Bx_; mutable BVectorWell invDrw_; - mutable BVector scaleAddRes_; - - // residuals of the well equations - BVectorWell resWell_; mutable std::vector well_solutions_; diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index ba4adf2a2..614719d4d 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -161,14 +161,17 @@ namespace Opm { const int number_of_phases_; + using WellInterfacePtr = std::unique_ptr >; // a vector of all the wells. // eventually, the wells_ above should be gone. // the name is just temporary // later, might make share_ptr const later. - std::vector > > well_container_; + std::vector well_container_; - // TODO: forgot why returning a vector here - void createWellContainer(const Wells* wells_arg); + // create the well container + static std::vector createWellContainer(const Wells* wells, + const std::vector& wells_ecl, + const int time_step); // Well collection is used to enforce the group control WellCollection* well_collection_; @@ -188,6 +191,7 @@ namespace Opm { long int global_nc_; + // used to better efficiency of calcuation mutable BVector scaleAddRes_; void updateWellControls(WellState& xw) const; diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 088eefe40..3ce6641c7 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -17,6 +17,7 @@ namespace Opm { , wells_ecl_(wells_ecl) , number_of_wells_(wells_arg ? (wells_arg->number_of_wells) : 0) , number_of_phases_(wells_arg ? (wells_arg->number_of_phases) : 0) // TODO: not sure if it is proper for this way + , well_container_(createWellContainer(wells_arg, wells_ecl, current_timeIdx) ) , well_collection_(well_collection) , param_(param) , terminal_output_(terminal_output) @@ -25,7 +26,6 @@ namespace Opm { , current_timeIdx_(current_timeIdx) , rate_converter_(rate_converter) { - createWellContainer(wells_arg); } @@ -111,27 +111,29 @@ namespace Opm { template - void + std::vector::WellInterfacePtr > StandardWellsDense:: - createWellContainer(const Wells* wells_arg) + createWellContainer(const Wells* wells, + const std::vector< const Well* >& wells_ecl, + const int time_step) { - well_container_.clear(); - // There might be no wells in the process - if (localWellsActive()) { - const int nw = number_of_wells_; + std::vector well_container; - well_container_.reserve(nw); + const int nw = wells ? (wells->number_of_wells) : 0; + + if (nw > 0) { + well_container.reserve(nw); // With the following way, it will have the same order with wells struct // Hopefully, it can generate the same residual history with master branch for (int w = 0; w < nw; ++w) { - const std::string well_name = std::string(wells_arg->name[w]); + const std::string well_name = std::string(wells->name[w]); // finding the location of the well in wells_ecl - const int nw_wells_ecl = wells_ecl_.size(); + const int nw_wells_ecl = wells_ecl.size(); int index_well = 0; for (; index_well < nw_wells_ecl; ++index_well) { - if (well_name == wells_ecl_[index_well]->name()) { + if (well_name == wells_ecl[index_well]->name()) { break; } } @@ -141,15 +143,16 @@ namespace Opm { OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl "); } - const Well* well_ecl = wells_ecl_[index_well]; - if (well_ecl->isMultiSegment(current_timeIdx_)) { + const Well* well_ecl = wells_ecl[index_well]; + if (well_ecl->isMultiSegment(time_step)) { OPM_THROW(Opm::NumericalProblem, "Not handling Multisegment Wells for now"); } // Basically, we are handling all the wells as StandardWell for the moment - well_container_.emplace_back(new StandardWell(well_ecl, current_timeIdx_, wells_arg) ); + well_container.emplace_back(new StandardWell(well_ecl, time_step, wells) ); } } + return well_container; } From 9a4a9a2bcc6149c6dc9913b210bec688f493095b Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 11:26:21 +0200 Subject: [PATCH 097/104] using well name and allow_cf form well_ecl. --- opm/autodiff/StandardWell.hpp | 4 +-- opm/autodiff/StandardWell_impl.hpp | 18 +++++----- opm/autodiff/WellInterface.hpp | 52 +++++++++++++---------------- opm/autodiff/WellInterface_impl.hpp | 18 +++++++--- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 98df23ff4..df3ffd013 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -62,6 +62,7 @@ namespace Opm using Base::has_solvent; using Base::has_polymer; + using Base::name; // TODO: with flow_ebos,for a 2P deck, // TODO: for the 2p deck, numEq will be 3, a dummy phase is already added from the reservoir side. // it will cause problem here without processing the dummy phase. @@ -165,6 +166,7 @@ namespace Opm protected: // protected functions from the Base class + using Base::getAllowCrossFlow; using Base::phaseUsage; using Base::active; using Base::flowToEbosPvIdx; @@ -177,7 +179,6 @@ namespace Opm using Base::mostStrictBhpFromBhpLimits; // protected member variables from the Base class - using Base::name_; using Base::vfp_properties_; using Base::gravity_; using Base::well_efficiency_factor_; @@ -185,7 +186,6 @@ namespace Opm using Base::first_perf_; using Base::ref_depth_; using Base::perf_depth_; - using Base::allow_cf_; using Base::well_cells_; using Base::number_of_perforations_; using Base::number_of_phases_; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 669444caa..2054068e2 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -282,7 +282,7 @@ namespace Opm // ReservoirRate return target_rate * wellVolumeFractionScaled(comp_idx); } else { - OPM_THROW(std::logic_error, "Unknown control type for well " << name_); + OPM_THROW(std::logic_error, "Unknown control type for well " << name()); } // avoid warning of condition reaches end of non-void function @@ -488,7 +488,7 @@ namespace Opm const EvalWell d = 1.0 - rv * rs; if (d.value() == 0.0) { - OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << name_ << " during flux calcuation" + OPM_THROW(Opm::NumericalProblem, "Zero d value obtained for well " << name() << " during flux calcuation" << " with rs " << rs << " and rv " << rv); } @@ -648,7 +648,7 @@ namespace Opm StandardWell:: crossFlowAllowed(const Simulator& ebosSimulator) const { - if (allow_cf_) { + if (getAllowCrossFlow()) { return true; } @@ -1195,7 +1195,7 @@ namespace Opm // checking whether control changed if (updated_control_index != old_control_index) { - logger.wellSwitched(name_, + logger.wellSwitched(name(), well_controls_iget_type(wc, old_control_index), well_controls_iget_type(wc, updated_control_index)); } @@ -1498,11 +1498,11 @@ namespace Opm const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); if (std::isnan(well_flux_residual[phaseIdx])) { - OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name_); + OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name()); } if (well_flux_residual[phaseIdx] > maxResidualAllowed) { - OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name_); + OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name()); } } @@ -1769,7 +1769,7 @@ namespace Opm // there should be always some available bhp/thp constraints there if (std::isinf(bhp) || std::isnan(bhp)) { - OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name_); + OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name()); } converged = std::abs(old_bhp - bhp) < bhp_tolerance; @@ -1779,7 +1779,7 @@ namespace Opm // checking whether the potentials have valid values for (const double value : potentials) { if (std::isinf(value) || std::isnan(value)) { - OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name_); + OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name()); } } @@ -1798,7 +1798,7 @@ namespace Opm } if (!converged) { - OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name_); + OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name()); } return potentials; diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 68b2918ea..097fa7695 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -157,27 +157,6 @@ namespace Opm virtual void setWellSolutions(const WellState& well_state) const = 0; protected: - const std::vector& active() const; - - const PhaseUsage& phaseUsage() const; - - int flowPhaseToEbosCompIdx( const int phaseIdx ) const; - - int flowToEbosPvIdx( const int flowPv ) const; - - int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; - - // TODO: it is dumplicated with StandardWellsDense - int numComponents() const; - - virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const = 0; - - double wsolvent() const; - - double wpolymer() const; - - bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, - const WellState& well_state) const; // to indicate a invalid connection static const int INVALIDCONNECTION = -100000; @@ -186,10 +165,6 @@ namespace Opm const int current_step_; - // TODO: some variables shared by all the wells should be made static - // well name - std::string name_; - // the index of well in Wells struct int index_of_well_; @@ -197,9 +172,6 @@ namespace Opm // INJECTOR or PRODUCER enum WellType well_type_; - // whether the well allows crossflow - bool allow_cf_; - // number of phases int number_of_phases_; @@ -248,12 +220,36 @@ namespace Opm const PhaseUsage* phase_usage_; + bool getAllowCrossFlow() const; + const std::vector* active_; const VFPProperties* vfp_properties_; double gravity_; + const std::vector& active() const; + + const PhaseUsage& phaseUsage() const; + + int flowPhaseToEbosCompIdx( const int phaseIdx ) const; + + int flowToEbosPvIdx( const int flowPv ) const; + + int flowPhaseToEbosPhaseIdx( const int phaseIdx ) const; + + // TODO: it is dumplicated with StandardWellsDense + int numComponents() const; + + virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const = 0; + + double wsolvent() const; + + double wpolymer() const; + + bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits, + const WellState& well_state) const; + bool wellHasTHPConstraints() const; // Component fractions for each phase for the well diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 80cab156b..0dcee05d6 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -44,10 +44,8 @@ namespace Opm // here, just another assertion. assert(index_well != wells->number_of_wells); - name_ = well_name; index_of_well_ = index_well; well_type_ = wells->type[index_well]; - allow_cf_ = wells->allow_cf[index_well]; number_of_phases_ = wells->number_of_phases; // copying the comp_frac @@ -127,7 +125,7 @@ namespace Opm WellInterface:: name() const { - return name_; + return well_ecl_->name(); } @@ -158,6 +156,18 @@ namespace Opm + template + bool + WellInterface:: + getAllowCrossFlow() const + { + return well_ecl_->getAllowCrossFlow(); + } + + + + + template const std::vector& WellInterface:: @@ -593,7 +603,7 @@ namespace Opm return; } - const std::string well_name = name_; + const std::string well_name = name(); // for the moment, we only handle rate limits, not handling potential limits // the potential limits should not be difficult to add From 25869026e5a9f180a75c358ade9976a80b3924f8 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 14:16:24 +0200 Subject: [PATCH 098/104] renaming a few functions to reflect the functions better. No functional change. --- opm/autodiff/BlackoilModelEbos.hpp | 2 +- opm/autodiff/StandardWell.hpp | 21 ++++++++++----------- opm/autodiff/StandardWell_impl.hpp | 13 ++++++------- opm/autodiff/StandardWellsDense.hpp | 4 ++-- opm/autodiff/StandardWellsDense_impl.hpp | 21 +++++++++++---------- opm/autodiff/WellInterface.hpp | 11 +++++------ 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp index dcfa15aee..e2bbfdb78 100644 --- a/opm/autodiff/BlackoilModelEbos.hpp +++ b/opm/autodiff/BlackoilModelEbos.hpp @@ -324,7 +324,7 @@ namespace Opm { if( nw > 0 ) { - wellModel().applySolutionWellState(x, well_state); + wellModel().recoverWellSolutionAndUpdateWellState(x, well_state); } report.update_time += perfTimer.stop(); } diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index df3ffd013..ea79b4bbc 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -105,7 +105,7 @@ namespace Opm const int num_cells); - virtual void setWellVariables(); + virtual void setWellPrimaryVariables(); // TODO: to check whether all the paramters are required void computePerfRate(const IntensiveQuantities& intQuants, @@ -120,11 +120,6 @@ namespace Opm virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const; - /// updating the well_state based on well solution dwells - void updateWellState(const BVectorWell& dwells, - const BlackoilModelParameters& param, - WellState& well_state) const; - /// updating the well state based the control mode specified with current // TODO: later will check wheter we need current virtual void updateWellStateWithTarget(const int current, @@ -153,8 +148,8 @@ namespace Opm /// using the solution x to recover the solution xw for wells and applying /// xw to update Well State - virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, - WellState& well_state) const; + virtual void recoverWellSolutionAndUpdateWellState(const BVector& x, const ModelParameters& param, + WellState& well_state) const; /// computing the well potentials for group control virtual void computeWellPotentials(const Simulator& ebosSimulator, @@ -242,6 +237,11 @@ namespace Opm // xw = inv(D)*(rw - C*x) void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; + // updating the well_state based on well solution dwells + void updateWellState(const BVectorWell& dwells, + const BlackoilModelParameters& param, + WellState& well_state) const; + // calculate the properties for the well connections // to calulate the pressure difference between well connections. void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator, @@ -267,9 +267,8 @@ namespace Opm const std::vector& rvmax_perf, const std::vector& surf_dens_perf); - virtual void wellEqIteration(Simulator& ebosSimulator, - const ModelParameters& param, - WellState& well_state); + virtual void solveEqAndUpdateWellState(const ModelParameters& param, + WellState& well_state); // TODO: maybe we should provide a light version of computePerfRate, which does not include the // calculation of the derivatives diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 2054068e2..a02a6a5c4 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -101,7 +101,7 @@ namespace Opm template void StandardWell:: - setWellVariables() + setWellPrimaryVariables() { // TODO: using numComp here is only to make the 2p + dummy phase work // TODO: in theory, we should use numWellEq here. @@ -1571,9 +1571,8 @@ namespace Opm template void StandardWell:: - wellEqIteration(Simulator& ebosSimulator, - const ModelParameters& param, - WellState& well_state) + solveEqAndUpdateWellState(const ModelParameters& param, + WellState& well_state) { // We assemble the well equations, then we check the convergence, // which is why we do not put the assembleWellEq here. @@ -1660,9 +1659,9 @@ namespace Opm template void StandardWell:: - applySolutionWellState(const BVector& x, - const ModelParameters& param, - WellState& well_state) const + recoverWellSolutionAndUpdateWellState(const BVector& x, + const ModelParameters& param, + WellState& well_state) const { BVectorWell xw(1); recoverSolutionWell(x, xw); diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 614719d4d..b8c7e5f0b 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -124,7 +124,7 @@ namespace Opm { // using the solution x to recover the solution xw for wells and applying // xw to update Well State - void applySolutionWellState(const BVector& x, WellState& well_state) const; + void recoverWellSolutionAndUpdateWellState(const BVector& x, WellState& well_state) const; int numWells() const; @@ -234,7 +234,7 @@ namespace Opm { void computeAccumWells() const; - void setWellVariables() const; + void setWellPrimaryVariables() const; // The number of components in the model. int numComponents() const diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 3ce6641c7..90294ecf8 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -178,8 +178,8 @@ namespace Opm { } updateWellControls(well_state); - // Set the primary variables for the wells - setWellVariables(); + // Set the well primary variables + setWellPrimaryVariables(); if (iterationIdx == 0) { computeWellConnectionPressures(ebosSimulator, well_state); @@ -285,10 +285,10 @@ namespace Opm { template void StandardWellsDense:: - applySolutionWellState(const BVector& x, WellState& well_state) const + recoverWellSolutionAndUpdateWellState(const BVector& x, WellState& well_state) const { for (auto& well : well_container_) { - well->applySolutionWellState(x, param_, well_state); + well->recoverWellSolutionAndUpdateWellState(x, param_, well_state); } } @@ -377,10 +377,10 @@ namespace Opm { template void StandardWellsDense:: - setWellVariables() const + setWellPrimaryVariables() const { for (auto& well : well_container_) { - well->setWellVariables(); + well->setWellPrimaryVariables(); } } @@ -436,7 +436,7 @@ namespace Opm { if( localWellsActive() ) { for (auto& well : well_container_) { - well->wellEqIteration(ebosSimulator, param_, well_state); + well->solveEqAndUpdateWellState(param_, well_state); } } // updateWellControls uses communication @@ -445,7 +445,7 @@ namespace Opm { if( wellsActive() ) { updateWellControls(well_state); - setWellVariables(); + setWellPrimaryVariables(); } } while (it < 15); @@ -638,10 +638,11 @@ namespace Opm { // calculate the well potentials setWellSolutions(well_state); - setWellVariables(); computeWellConnectionPressures(ebos_simulator, well_state); - // To store well potentials for each well + // set the well primary variables, which is used in computePerfRate for computeWellPotentials + // TODO: for computeWellPotentials, no derivative is required actually + setWellPrimaryVariables(); std::vector well_potentials; computeWellPotentials(ebos_simulator, well_state, well_potentials); diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 097fa7695..89d15586e 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -104,15 +104,14 @@ namespace Opm const double gravity_arg, const int num_cells); - virtual void setWellVariables() = 0; + virtual void setWellPrimaryVariables() = 0; virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, const ModelParameters& param) const = 0; - virtual void wellEqIteration(Simulator& ebosSimulator, - const ModelParameters& param, - WellState& well_state) = 0; + virtual void solveEqAndUpdateWellState(const ModelParameters& param, + WellState& well_state) = 0; virtual void assembleWellEq(Simulator& ebosSimulator, const double dt, @@ -128,8 +127,8 @@ namespace Opm // using the solution x to recover the solution xw for wells and applying // xw to update Well State - virtual void applySolutionWellState(const BVector& x, const ModelParameters& param, - WellState& well_state) const = 0; + virtual void recoverWellSolutionAndUpdateWellState(const BVector& x, const ModelParameters& param, + WellState& well_state) const = 0; // Ax = Ax - C D^-1 B x virtual void apply(const BVector& x, BVector& Ax) const = 0; From bc785536863894ff8f43bbe3d3226ec193d3d704 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 15:41:25 +0200 Subject: [PATCH 099/104] renaming well_solutions_ and well_variables to give slightly easier understanding. --- opm/autodiff/StandardWell.hpp | 13 +++- opm/autodiff/StandardWell_impl.hpp | 96 ++++++++++++------------ opm/autodiff/StandardWellsDense.hpp | 4 +- opm/autodiff/StandardWellsDense_impl.hpp | 29 ++++--- opm/autodiff/WellInterface.hpp | 4 +- 5 files changed, 75 insertions(+), 71 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index ea79b4bbc..49b2f5cd7 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -105,7 +105,7 @@ namespace Opm const int num_cells); - virtual void setWellPrimaryVariables(); + virtual void initPrimaryVariablesEvaluation() const; // TODO: to check whether all the paramters are required void computePerfRate(const IntensiveQuantities& intQuants, @@ -156,7 +156,7 @@ namespace Opm const WellState& well_state, std::vector& well_potentials) const; - virtual void setWellSolutions(const WellState& well_state) const; + virtual void updatePrimaryVariables(const WellState& well_state) const; protected: @@ -213,9 +213,14 @@ namespace Opm mutable BVectorWell Bx_; mutable BVectorWell invDrw_; - mutable std::vector well_solutions_; + // the values for the primary varibles + // based on different solutioin strategies, the wells can have different primary variables + mutable std::vector primary_variables_; - std::vector well_variables_; + // the Evaluation for the well primary variables, which contain derivativles and are used in AD calculation + mutable std::vector primary_variables_evaluation_; + + // the saturations in the well bore under surface conditions at the beginning of the time step std::vector F0_; // TODO: this function should be moved to the base class. diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index a02a6a5c4..9aa7956f8 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -28,8 +28,8 @@ namespace Opm : Base(well, time_step, wells) , perf_densities_(number_of_perforations_) , perf_pressure_diffs_(number_of_perforations_) - , well_solutions_(numWellEq, 0.0) - , well_variables_(numWellEq) // the number of the primary variables + , primary_variables_(numWellEq, 0.0) + , primary_variables_evaluation_(numWellEq) // the number of the primary variables , F0_(numWellEq) { duneB_.setBuildMode( OffDiagMatWell::row_wise ); @@ -101,17 +101,17 @@ namespace Opm template void StandardWell:: - setWellPrimaryVariables() + initPrimaryVariablesEvaluation() const { // TODO: using numComp here is only to make the 2p + dummy phase work // TODO: in theory, we should use numWellEq here. // for (int eqIdx = 0; eqIdx < numWellEq; ++eqIdx) { for (int eqIdx = 0; eqIdx < numComponents(); ++eqIdx) { - assert( eqIdx < well_solutions_.size() ); + assert( eqIdx < primary_variables_.size() ); - well_variables_[eqIdx] = 0.0; - well_variables_[eqIdx].setValue(well_solutions_[eqIdx]); - well_variables_[eqIdx].setDerivative(numEq + eqIdx, 1.0); + primary_variables_evaluation_[eqIdx] = 0.0; + primary_variables_evaluation_[eqIdx].setValue(primary_variables_[eqIdx]); + primary_variables_evaluation_[eqIdx].setDerivative(numEq + eqIdx, 1.0); } } @@ -147,7 +147,7 @@ namespace Opm return calculateBhpFromThp(rates, control); } - return well_variables_[XvarWell]; + return primary_variables_evaluation_[XvarWell]; } @@ -187,7 +187,7 @@ namespace Opm } if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return comp_frac * well_variables_[XvarWell]; + return comp_frac * primary_variables_evaluation_[XvarWell]; } qs.setValue(comp_frac * target_rate); @@ -200,7 +200,7 @@ namespace Opm } if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP) { - return well_variables_[XvarWell]; + return primary_variables_evaluation_[XvarWell]; } qs.setValue(target_rate); return qs; @@ -208,7 +208,7 @@ namespace Opm // Producers if (well_controls_get_current_type(wc) == BHP || well_controls_get_current_type(wc) == THP ) { - return well_variables_[XvarWell] * wellVolumeFractionScaled(comp_idx); + return primary_variables_evaluation_[XvarWell] * wellVolumeFractionScaled(comp_idx); } if (well_controls_get_current_type(wc) == SURFACE_RATE) { @@ -332,28 +332,28 @@ namespace Opm wellVolumeFraction(const int compIdx) const { if (compIdx == Water) { - return well_variables_[WFrac]; + return primary_variables_evaluation_[WFrac]; } if (compIdx == Gas) { - return well_variables_[GFrac]; + return primary_variables_evaluation_[GFrac]; } if (has_solvent && compIdx == contiSolventEqIdx) { - return well_variables_[SFrac]; + return primary_variables_evaluation_[SFrac]; } // Oil fraction EvalWell well_fraction = 1.0; if (active()[Water]) { - well_fraction -= well_variables_[WFrac]; + well_fraction -= primary_variables_evaluation_[WFrac]; } if (active()[Gas]) { - well_fraction -= well_variables_[GFrac]; + well_fraction -= primary_variables_evaluation_[GFrac]; } if (has_solvent) { - well_fraction -= well_variables_[SFrac]; + well_fraction -= primary_variables_evaluation_[SFrac]; } return well_fraction; } @@ -791,44 +791,44 @@ namespace Opm const double dBHPLimit = param.dbhp_max_rel_; const double dFLimit = param.dwell_fraction_max_; - const std::vector xvar_well_old = well_solutions_; + const std::vector xvar_well_old = primary_variables_; // update the second and third well variable (The flux fractions) std::vector F(np,0.0); if (active()[ Water ]) { const int sign2 = dwells[0][WFrac] > 0 ? 1: -1; const double dx2_limited = sign2 * std::min(std::abs(dwells[0][WFrac]),dFLimit); - well_solutions_[WFrac] = xvar_well_old[WFrac] - dx2_limited; + primary_variables_[WFrac] = xvar_well_old[WFrac] - dx2_limited; } if (active()[ Gas ]) { const int sign3 = dwells[0][GFrac] > 0 ? 1: -1; const double dx3_limited = sign3 * std::min(std::abs(dwells[0][GFrac]),dFLimit); - well_solutions_[GFrac] = xvar_well_old[GFrac] - dx3_limited; + primary_variables_[GFrac] = xvar_well_old[GFrac] - dx3_limited; } if (has_solvent) { const int sign4 = dwells[0][SFrac] > 0 ? 1: -1; const double dx4_limited = sign4 * std::min(std::abs(dwells[0][SFrac]),dFLimit); - well_solutions_[SFrac] = xvar_well_old[SFrac] - dx4_limited; + primary_variables_[SFrac] = xvar_well_old[SFrac] - dx4_limited; } assert(active()[ Oil ]); F[Oil] = 1.0; if (active()[ Water ]) { - F[Water] = well_solutions_[WFrac]; + F[Water] = primary_variables_[WFrac]; F[Oil] -= F[Water]; } if (active()[ Gas ]) { - F[Gas] = well_solutions_[GFrac]; + F[Gas] = primary_variables_[GFrac]; F[Oil] -= F[Gas]; } double F_solvent = 0.0; if (has_solvent) { - F_solvent = well_solutions_[SFrac]; + F_solvent = primary_variables_[SFrac]; F[Oil] -= F_solvent; } @@ -872,13 +872,13 @@ namespace Opm } if (active()[ Water ]) { - well_solutions_[WFrac] = F[Water]; + primary_variables_[WFrac] = F[Water]; } if (active()[ Gas ]) { - well_solutions_[GFrac] = F[Gas]; + primary_variables_[GFrac] = F[Gas]; } if(has_solvent) { - well_solutions_[SFrac] = F_solvent; + primary_variables_[SFrac] = F_solvent; } // F_solvent is added to F_gas. This means that well_rate[Gas] also contains solvent. @@ -915,18 +915,18 @@ namespace Opm case THP: // The BHP and THP both uses the total rate as first well variable. case BHP: { - well_solutions_[XvarWell] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; + primary_variables_[XvarWell] = xvar_well_old[XvarWell] - dwells[0][XvarWell]; switch (well_type_) { case INJECTOR: for (int p = 0; p < np; ++p) { const double comp_frac = comp_frac_[p]; - well_state.wellRates()[index_of_well_ * np + p] = comp_frac * well_solutions_[XvarWell]; + well_state.wellRates()[index_of_well_ * np + p] = comp_frac * primary_variables_[XvarWell]; } break; case PRODUCER: for (int p = 0; p < np; ++p) { - well_state.wellRates()[index_of_well_ * np + p] = well_solutions_[XvarWell] * F[p]; + well_state.wellRates()[index_of_well_ * np + p] = primary_variables_[XvarWell] * F[p]; } break; } @@ -956,8 +956,8 @@ namespace Opm { const int sign1 = dwells[0][XvarWell] > 0 ? 1: -1; const double dx1_limited = sign1 * std::min(std::abs(dwells[0][XvarWell]),std::abs(xvar_well_old[XvarWell])*dBHPLimit); - well_solutions_[XvarWell] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); - well_state.bhp()[index_of_well_] = well_solutions_[XvarWell]; + primary_variables_[XvarWell] = std::max(xvar_well_old[XvarWell] - dx1_limited,1e5); + well_state.bhp()[index_of_well_] = primary_variables_[XvarWell]; if (well_controls_iget_type(wc, current) == SURFACE_RATE) { if (well_type_ == PRODUCER) { @@ -1138,7 +1138,7 @@ namespace Opm break; } // end of switch - setWellSolutions(xw); + updatePrimaryVariables(xw); } @@ -1857,7 +1857,7 @@ namespace Opm template void StandardWell:: - setWellSolutions(const WellState& well_state) const + updatePrimaryVariables(const WellState& well_state) const { const int np = number_of_phases_; const int well_index = index_of_well_; @@ -1874,21 +1874,21 @@ namespace Opm switch (well_controls_get_current_type(wc)) { case THP: case BHP: { - well_solutions_[XvarWell] = 0.0; + primary_variables_[XvarWell] = 0.0; if (well_type_ == INJECTOR) { for (int p = 0; p < np; ++p) { - well_solutions_[XvarWell] += well_state.wellRates()[np*well_index + p] * comp_frac_[p]; + primary_variables_[XvarWell] += well_state.wellRates()[np*well_index + p] * comp_frac_[p]; } } else { for (int p = 0; p < np; ++p) { - well_solutions_[XvarWell] += g[p] * well_state.wellRates()[np*well_index + p]; + primary_variables_[XvarWell] += g[p] * well_state.wellRates()[np*well_index + p]; } } break; } case RESERVOIR_RATE: // Intentional fall-through case SURFACE_RATE: - well_solutions_[XvarWell] = well_state.bhp()[well_index]; + primary_variables_[XvarWell] = well_state.bhp()[well_index]; break; } // end of switch @@ -1898,33 +1898,33 @@ namespace Opm } if(std::abs(tot_well_rate) > 0) { if (active()[ Water ]) { - well_solutions_[WFrac] = g[Water] * well_state.wellRates()[np*well_index + Water] / tot_well_rate; + primary_variables_[WFrac] = g[Water] * well_state.wellRates()[np*well_index + Water] / tot_well_rate; } if (active()[ Gas ]) { - well_solutions_[GFrac] = g[Gas] * (well_state.wellRates()[np*well_index + Gas] - well_state.solventWellRate(well_index)) / tot_well_rate ; + primary_variables_[GFrac] = g[Gas] * (well_state.wellRates()[np*well_index + Gas] - well_state.solventWellRate(well_index)) / tot_well_rate ; } if (has_solvent) { - well_solutions_[SFrac] = g[Gas] * well_state.solventWellRate(well_index) / tot_well_rate ; + primary_variables_[SFrac] = g[Gas] * well_state.solventWellRate(well_index) / tot_well_rate ; } } else { // tot_well_rate == 0 if (well_type_ == INJECTOR) { // only single phase injection handled if (active()[Water]) { if (distr[Water] > 0.0) { - well_solutions_[WFrac] = 1.0; + primary_variables_[WFrac] = 1.0; } else { - well_solutions_[WFrac] = 0.0; + primary_variables_[WFrac] = 0.0; } } if (active()[Gas]) { if (distr[Gas] > 0.0) { - well_solutions_[GFrac] = 1.0 - wsolvent(); + primary_variables_[GFrac] = 1.0 - wsolvent(); if (has_solvent) { - well_solutions_[SFrac] = wsolvent(); + primary_variables_[SFrac] = wsolvent(); } } else { - well_solutions_[GFrac] = 0.0; + primary_variables_[GFrac] = 0.0; } } @@ -1934,10 +1934,10 @@ namespace Opm } else if (well_type_ == PRODUCER) { // producers // TODO: the following are not addressed for the solvent case yet if (active()[Water]) { - well_solutions_[WFrac] = 1.0 / np; + primary_variables_[WFrac] = 1.0 / np; } if (active()[Gas]) { - well_solutions_[GFrac] = 1.0 / np; + primary_variables_[GFrac] = 1.0 / np; } } else { OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well"); diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index b8c7e5f0b..2441d1ea2 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -199,7 +199,7 @@ namespace Opm { void updateGroupControls(WellState& well_state) const; // setting the well_solutions_ based on well_state. - void setWellSolutions(const WellState& well_state) const; + void updatePrimaryVariables(const WellState& well_state) const; void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map& cartesian_to_compressed ) const; @@ -234,7 +234,7 @@ namespace Opm { void computeAccumWells() const; - void setWellPrimaryVariables() const; + void initPrimaryVariablesEvaluation() const; // The number of components in the model. int numComponents() const diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index 90294ecf8..f343693db 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -178,8 +178,8 @@ namespace Opm { } updateWellControls(well_state); - // Set the well primary variables - setWellPrimaryVariables(); + // Set the well primary variables based on the value of well solutions + initPrimaryVariablesEvaluation(); if (iterationIdx == 0) { computeWellConnectionPressures(ebosSimulator, well_state); @@ -377,10 +377,10 @@ namespace Opm { template void StandardWellsDense:: - setWellPrimaryVariables() const + initPrimaryVariablesEvaluation() const { for (auto& well : well_container_) { - well->setWellPrimaryVariables(); + well->initPrimaryVariablesEvaluation(); } } @@ -445,7 +445,7 @@ namespace Opm { if( wellsActive() ) { updateWellControls(well_state); - setWellPrimaryVariables(); + initPrimaryVariablesEvaluation(); } } while (it < 15); @@ -455,7 +455,7 @@ namespace Opm { OpmLog::debug("Well equation solution failed in getting converged with " + std::to_string(it) + " iterations"); well_state = well_state0; - setWellSolutions(well_state); + updatePrimaryVariables(well_state); // also recover the old well controls for (int w = 0; w < nw; ++w) { WellControls* wc = well_container_[w]->wellControls(); @@ -569,11 +569,16 @@ namespace Opm { const WellState& well_state, std::vector& well_potentials) const { + updatePrimaryVariables(well_state); + computeWellConnectionPressures(ebosSimulator, well_state); + + // initialize the primary variables in Evaluation, which is used in computePerfRate for computeWellPotentials + // TODO: for computeWellPotentials, no derivative is required actually + initPrimaryVariablesEvaluation(); // number of wells and phases const int nw = number_of_wells_; const int np = number_of_phases_; - well_potentials.resize(nw * np, 0.0); for (int w = 0; w < nw; ++w) { @@ -637,12 +642,6 @@ namespace Opm { if (well_collection_->requireWellPotentials()) { // calculate the well potentials - setWellSolutions(well_state); - computeWellConnectionPressures(ebos_simulator, well_state); - - // set the well primary variables, which is used in computePerfRate for computeWellPotentials - // TODO: for computeWellPotentials, no derivative is required actually - setWellPrimaryVariables(); std::vector well_potentials; computeWellPotentials(ebos_simulator, well_state, well_potentials); @@ -940,10 +939,10 @@ namespace Opm { template void StandardWellsDense:: - setWellSolutions(const WellState& well_state) const + updatePrimaryVariables(const WellState& well_state) const { for (const auto& well : well_container_) { - well->setWellSolutions(well_state); + well->updatePrimaryVariables(well_state); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 89d15586e..8a3a9bd44 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -104,7 +104,7 @@ namespace Opm const double gravity_arg, const int num_cells); - virtual void setWellPrimaryVariables() = 0; + virtual void initPrimaryVariablesEvaluation() const = 0; virtual bool getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, @@ -153,7 +153,7 @@ namespace Opm virtual void updateWellControl(WellState& xw, wellhelpers::WellSwitchingLogger& logger) const = 0; - virtual void setWellSolutions(const WellState& well_state) const = 0; + virtual void updatePrimaryVariables(const WellState& well_state) const = 0; protected: From 96340a23ff23c6e5cdcc39ff9753d92800a31442 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 16:40:10 +0200 Subject: [PATCH 100/104] computePerfRate and crossFlowAllowed protected in StandardWell --- opm/autodiff/StandardWell.hpp | 16 ++++++++-------- opm/autodiff/WellInterface.hpp | 2 -- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 49b2f5cd7..42ba78262 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -107,19 +107,11 @@ namespace Opm virtual void initPrimaryVariablesEvaluation() const; - // TODO: to check whether all the paramters are required - void computePerfRate(const IntensiveQuantities& intQuants, - const std::vector& mob_perfcells_dense, - const double Tw, const EvalWell& bhp, const double& cdp, - const bool& allow_cf, std::vector& cq_s) const; - virtual void assembleWellEq(Simulator& ebosSimulator, const double dt, WellState& well_state, bool only_wells); - virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const; - /// updating the well state based the control mode specified with current // TODO: later will check wheter we need current virtual void updateWellStateWithTarget(const int current, @@ -239,6 +231,8 @@ namespace Opm EvalWell extendEval(const Eval& in) const; + bool crossFlowAllowed(const Simulator& ebosSimulator) const; + // xw = inv(D)*(rw - C*x) void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; @@ -275,6 +269,12 @@ namespace Opm virtual void solveEqAndUpdateWellState(const ModelParameters& param, WellState& well_state); + // TODO: to check whether all the paramters are required + void computePerfRate(const IntensiveQuantities& intQuants, + const std::vector& mob_perfcells_dense, + const double Tw, const EvalWell& bhp, const double& cdp, + const bool& allow_cf, std::vector& cq_s) const; + // TODO: maybe we should provide a light version of computePerfRate, which does not include the // calculation of the derivatives void computeWellRatesWithBhp(const Simulator& ebosSimulator, diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 8a3a9bd44..68a381fac 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -240,8 +240,6 @@ namespace Opm // TODO: it is dumplicated with StandardWellsDense int numComponents() const; - virtual bool crossFlowAllowed(const Simulator& ebosSimulator) const = 0; - double wsolvent() const; double wpolymer() const; From a60a969257d8d6984e13858125bab8e8de157c06 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 21 Aug 2017 17:05:26 +0200 Subject: [PATCH 101/104] fixing one memory-leaking for flow_ebos running 2p case. --- opm/autodiff/StandardWell_impl.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 9aa7956f8..c86e60b94 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1476,8 +1476,10 @@ namespace Opm const double tol_wells = param.tolerance_wells_; const double maxResidualAllowed = param.max_residual_allowed_; + // TODO: it should be the number of numWellEq + // using numComp here for flow_ebos running 2p case. std::vector res(numComp); - for (int comp = 0; comp < numWellEq; ++comp) { + for (int comp = 0; comp < numComp; ++comp) { // magnitude of the residual matters res[comp] = std::abs(resWell_[0][comp]); } From 8abe48a69380fca0a47b18506e11c522fe1f11ad Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 22 Aug 2017 09:43:26 +0200 Subject: [PATCH 102/104] making destructor of WellInterface virtual to avoid memory leaking. --- opm/autodiff/WellInterface.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 68a381fac..aa698a582 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -87,6 +87,9 @@ namespace Opm /// Constructor WellInterface(const Well* well, const int time_step, const Wells* wells); + /// Virutal destructor + virtual ~WellInterface() {} + /// Well name. const std::string& name() const; From 355be6c26c56289a220def2708c5ae180a884979 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 22 Aug 2017 14:49:30 +0200 Subject: [PATCH 103/104] collecting the NaN and too large well residuals make sure all the processes will throw if there is any of the processes found abnormal residual values. --- opm/autodiff/StandardWell.hpp | 8 +++-- opm/autodiff/StandardWell_impl.hpp | 33 +++++++++++------ opm/autodiff/StandardWellsDense.hpp | 2 ++ opm/autodiff/StandardWellsDense_impl.hpp | 39 +++++++++++++++++--- opm/autodiff/WellInterface.hpp | 45 ++++++++++++++++++++---- 5 files changed, 102 insertions(+), 25 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 42ba78262..d1c4c0a67 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -58,6 +58,8 @@ namespace Opm }; using typename Base::Scalar; + using typename Base::ConvergenceReport; + using Base::numEq; using Base::has_solvent; @@ -123,9 +125,9 @@ namespace Opm wellhelpers::WellSwitchingLogger& logger) const; /// check whether the well equations get converged for this well - virtual bool getWellConvergence(Simulator& ebosSimulator, - const std::vector& B_avg, - const ModelParameters& param) const; + virtual ConvergenceReport getWellConvergence(Simulator& ebosSimulator, + const std::vector& B_avg, + const ModelParameters& param) const; /// computing the accumulation term for later use in well mass equations virtual void computeAccumWell(); diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index c86e60b94..70f8782fa 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1457,7 +1457,7 @@ namespace Opm template - bool + typename StandardWell::ConvergenceReport StandardWell:: getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg, @@ -1485,30 +1485,43 @@ namespace Opm } Vector well_flux_residual(numComp); - bool converged_Well = true; // Finish computation for ( int compIdx = 0; compIdx < numComp; ++compIdx ) { well_flux_residual[compIdx] = B_avg[compIdx] * res[compIdx]; - converged_Well = converged_Well && (well_flux_residual[compIdx] < tol_wells); } - // if one of the residuals is NaN, throw exception, so that the solver can be restarted + ConvergenceReport report; + // checking if any NaN or too large residuals found // TODO: not understand why phase here while component in other places. for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) { const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx)); if (std::isnan(well_flux_residual[phaseIdx])) { - OPM_THROW(Opm::NumericalProblem, "NaN residual for phase " << phaseName << " for well " << name()); - } - - if (well_flux_residual[phaseIdx] > maxResidualAllowed) { - OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName << " for well " << name()); + report.nan_residual_found = true; + const typename ConvergenceReport::ProblemWell problem_well = {name(), phaseName}; + report.nan_residual_wells.push_back(problem_well); + } else { + if (well_flux_residual[phaseIdx] > maxResidualAllowed) { + report.too_large_residual_found = true; + const typename ConvergenceReport::ProblemWell problem_well = {name(), phaseName}; + report.too_large_residual_wells.push_back(problem_well); + } } } - return converged_Well; + if ( !(report.nan_residual_found || report.too_large_residual_found) ) { // no abnormal residual value found + // check convergence + for ( int compIdx = 0; compIdx < numComp; ++compIdx ) + { + report.converged = report.converged && (well_flux_residual[compIdx] < tol_wells); + } + } else { // abnormal values found and no need to check the convergence + report.converged = false; + } + + return report; } diff --git a/opm/autodiff/StandardWellsDense.hpp b/opm/autodiff/StandardWellsDense.hpp index 2441d1ea2..48114afa5 100644 --- a/opm/autodiff/StandardWellsDense.hpp +++ b/opm/autodiff/StandardWellsDense.hpp @@ -168,6 +168,8 @@ namespace Opm { // later, might make share_ptr const later. std::vector well_container_; + using ConvergenceReport = typename WellInterface::ConvergenceReport; + // create the well container static std::vector createWellContainer(const Wells* wells, const std::vector& wells_ecl, diff --git a/opm/autodiff/StandardWellsDense_impl.hpp b/opm/autodiff/StandardWellsDense_impl.hpp index f343693db..ba841d2a5 100644 --- a/opm/autodiff/StandardWellsDense_impl.hpp +++ b/opm/autodiff/StandardWellsDense_impl.hpp @@ -479,22 +479,51 @@ namespace Opm { getWellConvergence(Simulator& ebosSimulator, const std::vector& B_avg) const { - bool converged_well = true; + ConvergenceReport report; - // currently, if there is any well not converged, we consider the well eqautions do not get converged for (const auto& well : well_container_) { - if ( !well->getWellConvergence(ebosSimulator, B_avg, param_) ) { - converged_well = false; + report += well->getWellConvergence(ebosSimulator, B_avg, param_); + } + + // checking NaN residuals + { + bool nan_residual_found = report.nan_residual_found; + const auto& grid = ebosSimulator.gridManager().grid(); + int value = nan_residual_found ? 1 : 0; + + nan_residual_found = grid.comm().max(value); + + if (nan_residual_found) { + for (const auto& well : report.nan_residual_wells) { + OpmLog::debug("NaN residual found with phase " + well.phase_name + " for well " + well.well_name); + } + OPM_THROW(Opm::NumericalProblem, "NaN residual found!"); } } + // checking too large residuals + { + bool too_large_residual_found = report.too_large_residual_found; + const auto& grid = ebosSimulator.gridManager().grid(); + int value = too_large_residual_found ? 1 : 0; + + too_large_residual_found = grid.comm().max(value); + if (too_large_residual_found) { + for (const auto& well : report.too_large_residual_wells) { + OpmLog::debug("Too large residual found with phase " + well.phase_name + " fow well " + well.well_name); + } + OPM_THROW(Opm::NumericalProblem, "Too large residual found!"); + } + } + + // checking convergence + bool converged_well = report.converged; { const auto& grid = ebosSimulator.gridManager().grid(); int value = converged_well ? 1 : 0; converged_well = grid.comm().min(value); } - // TODO: to think about the output here. return converged_well; } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index aa698a582..6d6c0de8b 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -109,9 +109,40 @@ namespace Opm virtual void initPrimaryVariablesEvaluation() const = 0; - virtual bool getWellConvergence(Simulator& ebosSimulator, - const std::vector& B_avg, - const ModelParameters& param) const = 0; + /// a struct to collect information about the convergence checking + struct ConvergenceReport { + struct ProblemWell { + std::string well_name; + std::string phase_name; + }; + bool converged = true; + bool nan_residual_found = false; + std::vector nan_residual_wells; + // We consider Inf is large residual here + bool too_large_residual_found = false; + std::vector too_large_residual_wells; + + ConvergenceReport& operator+=(const ConvergenceReport& rhs) { + converged = converged && rhs.converged; + nan_residual_found = nan_residual_found || rhs.nan_residual_found; + if (rhs.nan_residual_found) { + for (const ProblemWell& well : rhs.nan_residual_wells) { + nan_residual_wells.push_back(well); + } + } + too_large_residual_found = too_large_residual_found || rhs.too_large_residual_found; + if (rhs.too_large_residual_found) { + for (const ProblemWell& well : rhs.too_large_residual_wells) { + too_large_residual_wells.push_back(well); + } + } + return *this; + } + }; + + virtual ConvergenceReport getWellConvergence(Simulator& ebosSimulator, + const std::vector& B_avg, + const ModelParameters& param) const = 0; virtual void solveEqAndUpdateWellState(const ModelParameters& param, WellState& well_state) = 0; @@ -128,15 +159,15 @@ namespace Opm void computeRepRadiusPerfLength(const Grid& grid, const std::map& cartesian_to_compressed); - // using the solution x to recover the solution xw for wells and applying - // xw to update Well State + /// using the solution x to recover the solution xw for wells and applying + /// xw to update Well State virtual void recoverWellSolutionAndUpdateWellState(const BVector& x, const ModelParameters& param, WellState& well_state) const = 0; - // Ax = Ax - C D^-1 B x + /// Ax = Ax - C D^-1 B x virtual void apply(const BVector& x, BVector& Ax) const = 0; - // r = r - C D^-1 Rw + /// r = r - C D^-1 Rw virtual void apply(BVector& r) const = 0; virtual void computeWellPotentials(const Simulator& ebosSimulator, From 9accb56c86f2062639c7a269c0b69f09934f9bd7 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 22 Aug 2017 17:14:52 +0200 Subject: [PATCH 104/104] adding a test for well model. It is just test for StandardWell. --- CMakeLists_files.cmake | 2 + opm/autodiff/StandardWell.hpp | 4 +- opm/autodiff/WellInterface.hpp | 1 + opm/autodiff/WellInterface_impl.hpp | 11 ++ tests/TESTWELLMODEL.DATA | 58 +++++++++ tests/test_multisegmentwells.cpp | 11 +- tests/test_wellmodel.cpp | 194 ++++++++++++++++++++++++++++ 7 files changed, 269 insertions(+), 12 deletions(-) create mode 100644 tests/TESTWELLMODEL.DATA create mode 100644 tests/test_wellmodel.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 3e81d026f..6cfb634cb 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -91,6 +91,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_solventprops_ad.cpp tests/test_multisegmentwells.cpp tests/test_multiphaseupwind.cpp + tests/test_wellmodel.cpp # tests/test_thresholdpressure.cpp tests/test_wellswitchlogger.cpp tests/test_timer.cpp @@ -102,6 +103,7 @@ list (APPEND TEST_DATA_FILES tests/VFPPROD2 tests/msw.data tests/TESTTIMER.DATA + tests/TESTWELLMODEL.DATA ) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index d1c4c0a67..036ba51c1 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -135,9 +135,9 @@ namespace Opm virtual void computeWellConnectionPressures(const Simulator& ebosSimulator, const WellState& xw); - // Ax = Ax - C D^-1 B x + /// Ax = Ax - C D^-1 B x virtual void apply(const BVector& x, BVector& Ax) const; - // r = r - C D^-1 Rw + /// r = r - C D^-1 Rw virtual void apply(BVector& r) const; /// using the solution x to recover the solution xw for wells and applying diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 6d6c0de8b..950fca584 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 0dcee05d6..6133291e0 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -29,6 +29,17 @@ namespace Opm : well_ecl_(well) , current_step_(time_step) { + if (!well) { + OPM_THROW(std::invalid_argument, "Null pointer of Well is used to construct WellInterface"); + } + + if (time_step < 0) { + OPM_THROW(std::invalid_argument, "Negtive time step is used to construct WellInterface"); + } + + if (!wells) { + OPM_THROW(std::invalid_argument, "Null pointer of Wells is used to construct WellInterface"); + } const std::string& well_name = well->name(); diff --git a/tests/TESTWELLMODEL.DATA b/tests/TESTWELLMODEL.DATA new file mode 100644 index 000000000..796b4ff68 --- /dev/null +++ b/tests/TESTWELLMODEL.DATA @@ -0,0 +1,58 @@ +RUNSPEC + +DIMENS + 5 5 4 / +OIL +GAS +WATER + +GRID + +DX + 100*100. / + +DY + 100*50. / + +DZ + 100*10. / + +TOPS + 25*2500 / + +PORO + 100*0.3 / + +PERMX + 100*10. / + +PERMY + 100*20. / + +PERMZ + 100*1. / + +SCHEDULE + +WELSPECS + 'PROD1' 'P' 5 5 2525 'OIL' / + 'INJE1' 'I' 1 1 2505 'GAS' / +/ + +COMPDAT + 'PROD1' 5 5 3 4 'OPEN' 2* 0.15 / + 'INJE1' 1 1 1 4 'OPEN' 2* 0.15 / +/ + +WCONINJE + 'INJE1' 'WATER' 'OPEN' 'RATE' 1000. / +/ + +WCONPROD + 'PROD1' 'OPEN' 'GRAT' 2* 50000. / +/ + +TSTEP +10 / + +END diff --git a/tests/test_multisegmentwells.cpp b/tests/test_multisegmentwells.cpp index 82a00c39b..403aad311 100644 --- a/tests/test_multisegmentwells.cpp +++ b/tests/test_multisegmentwells.cpp @@ -83,15 +83,6 @@ struct SetupMSW { std::unique_ptr grid_init(new GridInit(ecl_state, porv)); const Grid& grid = grid_init->grid(); - // Create material law manager. - std::vector compressed_to_cartesianIdx; - Opm::createGlobalCellArray(grid, compressed_to_cartesianIdx); - - std::shared_ptr material_law_manager(new MaterialLawManager()); - material_law_manager->initFromDeck(deck, ecl_state, compressed_to_cartesianIdx); - - std::unique_ptr fluidprops(new FluidProps(deck, ecl_state, material_law_manager, grid)); - const size_t current_timestep = 0; // dummy_dynamic_list_econ_lmited @@ -115,7 +106,7 @@ struct SetupMSW { std::unordered_set()); const Wells* wells = wells_manager.c_wells(); - const auto wells_ecl = ecl_state.getSchedule().getWells(current_timestep); + const auto& wells_ecl = ecl_state.getSchedule().getWells(current_timestep); ms_wells.reset(new Opm::MultisegmentWells(wells, &(wells_manager.wellCollection()), wells_ecl, current_timestep)); }; diff --git a/tests/test_wellmodel.cpp b/tests/test_wellmodel.cpp new file mode 100644 index 000000000..c954e8e0a --- /dev/null +++ b/tests/test_wellmodel.cpp @@ -0,0 +1,194 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + Copyright 2017 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 . +*/ + + +#include + +#if HAVE_DYNAMIC_BOOST_TEST +#define BOOST_TEST_DYN_LINK +#endif + +#define BOOST_TEST_MODULE WellModelTest + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +// maybe should just include BlackoilModelEbos.hpp +namespace Ewoms { + namespace Properties { + NEW_TYPE_TAG(EclFlowProblem, INHERITS_FROM(BlackOilModel, EclBaseProblem)); + } +} + +using StandardWell = Opm::StandardWell; + +struct SetupTest { + + using Grid = UnstructuredGrid; + using GridInit = Opm::GridInit; + + SetupTest () + { + Opm::ParseContext parse_context; + Opm::Parser parser; + auto deck = parser.parseFile("TESTWELLMODEL.DATA", parse_context); + ecl_state = std::make_unique(deck , parse_context); + + // Create grid. + const std::vector& porv = + ecl_state->get3DProperties().getDoubleGridProperty("PORV").getData(); + + std::unique_ptr grid_init(new GridInit(*ecl_state, porv)); + const Grid& grid = grid_init->grid(); + + // Create material law manager. + std::vector compressed_to_cartesianIdx; + Opm::createGlobalCellArray(grid, compressed_to_cartesianIdx); + + // dummy_dynamic_list_econ_lmited + const Opm::DynamicListEconLimited dummy_dynamic_list; + + current_timestep = 0; + + // Create wells. + wells_manager = std::make_unique (*ecl_state, + current_timestep, + Opm::UgGridHelpers::numCells(grid), + Opm::UgGridHelpers::globalCell(grid), + Opm::UgGridHelpers::cartDims(grid), + Opm::UgGridHelpers::dimensions(grid), + Opm::UgGridHelpers::cell2Faces(grid), + Opm::UgGridHelpers::beginFaceCentroids(grid), + dummy_dynamic_list, + false, + std::unordered_set()); + + }; + + std::unique_ptr wells_manager; + std::unique_ptr ecl_state; + int current_timestep; +}; + + +BOOST_AUTO_TEST_CASE(TestStandardWellInput) { + SetupTest setup_test; + const Wells* wells = setup_test.wells_manager->c_wells(); + const auto& wells_ecl = setup_test.ecl_state->getSchedule().getWells(setup_test.current_timestep); + BOOST_CHECK_EQUAL( wells_ecl.size(), 2); + const Opm::Well* well = wells_ecl[1]; + BOOST_CHECK_THROW( StandardWell( well, -1, wells), std::invalid_argument); + BOOST_CHECK_THROW( StandardWell( nullptr, 4, wells), std::invalid_argument); + BOOST_CHECK_THROW( StandardWell( well, 4, nullptr), std::invalid_argument); +} + + +BOOST_AUTO_TEST_CASE(TestBehavoir) { + SetupTest setup_test; + const Wells* wells_struct = setup_test.wells_manager->c_wells(); + const auto& wells_ecl = setup_test.ecl_state->getSchedule().getWells(setup_test.current_timestep); + const int current_timestep = setup_test.current_timestep; + std::vector > wells; + + { + const int nw = wells_struct ? (wells_struct->number_of_wells) : 0; + + for (int w = 0; w < nw; ++w) { + const std::string well_name(wells_struct->name[w]); + + size_t index_well = 0; + for (; index_well < wells_ecl.size(); ++index_well) { + if (well_name == wells_ecl[index_well]->name()) { + break; + } + } + // we should always be able to find the well in wells_ecl + BOOST_CHECK(index_well != wells_ecl.size()); + + wells.emplace_back(new StandardWell(wells_ecl[index_well], current_timestep, wells_struct) ); + } + } + + // first well, it is a production well from the deck + { + const auto& well = wells[0]; + BOOST_CHECK_EQUAL(well->name(), "PROD1"); + BOOST_CHECK(well->wellType() == PRODUCER); + BOOST_CHECK(well->numEq == 3); + BOOST_CHECK(well->numWellEq == 3); + const auto& wc = well->wellControls(); + const int ctrl_num = well_controls_get_num(wc); + BOOST_CHECK(ctrl_num > 0); + const auto& control = well_controls_get_current(wc); + BOOST_CHECK(control >= 0); + // GAS RATE CONTROL + const auto& distr = well_controls_iget_distr(wc, control); + BOOST_CHECK(distr[0] == 0.); + BOOST_CHECK(distr[1] == 0.); + BOOST_CHECK(distr[2] == 1.); + } + + // second well, it is the injection well from the deck + { + const auto& well = wells[1]; + BOOST_CHECK_EQUAL(well->name(), "INJE1"); + BOOST_CHECK(well->wellType() == INJECTOR); + BOOST_CHECK(well->numEq == 3); + BOOST_CHECK(well->numWellEq == 3); + const auto& wc = well->wellControls(); + const int ctrl_num = well_controls_get_num(wc); + BOOST_CHECK(ctrl_num > 0); + const auto& control = well_controls_get_current(wc); + BOOST_CHECK(control >= 0); + // WATER RATE CONTROL + const auto& distr = well_controls_iget_distr(wc, control); + BOOST_CHECK(distr[0] == 1.); + BOOST_CHECK(distr[1] == 0.); + BOOST_CHECK(distr[2] == 0.); + } +}