diff --git a/CMakeLists.txt b/CMakeLists.txt
index 88c2acc1c..d30161752 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -81,6 +81,7 @@ include (CMakeLists_files.cmake)
macro (config_hook)
opm_need_version_of ("dune-common")
opm_need_version_of ("dune-istl")
+ opm_need_version_of ("ewoms")
endmacro (config_hook)
macro (prereqs_hook)
@@ -148,10 +149,7 @@ if (HAVE_OPM_DATA)
endif()
# create a symbolic link from flow to flow_legacy
+message("create symlink")
ADD_CUSTOM_TARGET(flow ALL
- COMMAND ${CMAKE_COMMAND} -E create_symlink "flow_legacy" "${CMAKE_BINARY_DIR}/bin/flow")
-install(
- FILES "${CMAKE_BINARY_DIR}/bin/flow"
- DESTINATION "${CMAKE_INSTALL_PREFIX}/bin"
- )
+ COMMAND ${CMAKE_COMMAND} -E create_symlink "flow_legacy" "${CMAKE_BINARY_DIR}/bin/flow")
diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index 9d719ea0b..74fba857f 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -36,6 +36,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/autodiff/ImpesTPFAAD.cpp
opm/autodiff/moduleVersion.cpp
opm/autodiff/SimulatorFullyImplicitBlackoilOutput.cpp
+ opm/autodiff/SimulatorFullyImplicitBlackoilOutputEbos.cpp
opm/autodiff/SimulatorIncompTwophaseAd.cpp
opm/autodiff/TransportSolverTwophaseAd.cpp
opm/autodiff/BlackoilPropsAdFromDeck.cpp
@@ -49,7 +50,6 @@ list (APPEND MAIN_SOURCE_FILES
opm/autodiff/WellMultiSegment.cpp
opm/autodiff/MultisegmentWells.cpp
opm/autodiff/BlackoilSolventState.cpp
- opm/autodiff/ThreadHandle.hpp
opm/autodiff/MissingFeatures.cpp
opm/polymer/PolymerState.cpp
opm/polymer/PolymerBlackoilState.cpp
@@ -105,6 +105,7 @@ list (APPEND EXAMPLE_SOURCE_FILES
examples/find_zero.cpp
examples/flow_legacy.cpp
examples/flow_sequential.cpp
+ examples/flow_ebos.cpp
examples/flow_multisegment.cpp
examples/flow_solvent.cpp
examples/sim_2p_incomp.cpp
@@ -124,6 +125,7 @@ list (APPEND PROGRAM_SOURCE_FILES
examples/sim_2p_incomp.cpp
examples/sim_2p_incomp_ad.cpp
examples/sim_2p_comp_reorder.cpp
+ examples/flow_ebos.cpp
examples/flow_legacy.cpp
examples/flow_sequential.cpp
examples/flow_solvent.cpp
@@ -143,6 +145,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/AutoDiffMatrix.hpp
opm/autodiff/AutoDiff.hpp
opm/autodiff/BackupRestore.hpp
+ opm/autodiff/BlackoilDetails.hpp
opm/autodiff/BlackoilModel.hpp
opm/autodiff/BlackoilModelBase.hpp
opm/autodiff/BlackoilModelBase_impl.hpp
@@ -155,6 +158,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/Compat.hpp
opm/autodiff/CPRPreconditioner.hpp
opm/autodiff/createGlobalCellArray.hpp
+ opm/autodiff/DefaultBlackoilSolutionState.hpp
opm/autodiff/BlackoilSequentialModel.hpp
opm/autodiff/BlackoilSolventModel.hpp
opm/autodiff/BlackoilSolventModel_impl.hpp
@@ -166,6 +170,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/DuneMatrix.hpp
opm/autodiff/ExtractParallelGridInformationToISTL.hpp
opm/autodiff/FlowMain.hpp
+ opm/autodiff/FlowMainEbos.hpp
opm/autodiff/FlowMainPolymer.hpp
opm/autodiff/FlowMainSequential.hpp
opm/autodiff/FlowMainSolvent.hpp
@@ -173,6 +178,8 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/GridHelpers.hpp
opm/autodiff/GridInit.hpp
opm/autodiff/ImpesTPFAAD.hpp
+ opm/autodiff/ISTLSolver.hpp
+ opm/autodiff/IterationReport.hpp
opm/autodiff/moduleVersion.hpp
opm/autodiff/NewtonIterationBlackoilCPR.hpp
opm/autodiff/NewtonIterationBlackoilInterface.hpp
@@ -187,18 +194,22 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/ParallelRestrictedAdditiveSchwarz.hpp
opm/autodiff/RateConverter.hpp
opm/autodiff/RedistributeDataHandles.hpp
+ opm/autodiff/SimFIBODetails.hpp
opm/autodiff/SimulatorBase.hpp
opm/autodiff/SimulatorBase_impl.hpp
+ opm/autodiff/SimulatorFullyImplicitBlackoilEbos.hpp
opm/autodiff/SimulatorFullyImplicitBlackoil.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilSolvent.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilSolvent_impl.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilMultiSegment.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilMultiSegment_impl.hpp
+ opm/autodiff/SimulatorFullyImplicitBlackoilOutputEbos.hpp
opm/autodiff/SimulatorIncompTwophaseAd.hpp
opm/autodiff/SimulatorSequentialBlackoil.hpp
opm/autodiff/TransportSolverTwophaseAd.hpp
opm/autodiff/WellDensitySegmented.hpp
opm/autodiff/WellStateFullyImplicitBlackoil.hpp
+ opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp
opm/autodiff/WellStateFullyImplicitBlackoilSolvent.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilOutput.hpp
opm/autodiff/VFPProperties.hpp
@@ -212,9 +223,11 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/WellHelpers.hpp
opm/autodiff/StandardWells.hpp
opm/autodiff/StandardWells_impl.hpp
+ opm/autodiff/StandardWellsDense.hpp
opm/autodiff/StandardWellsSolvent.hpp
opm/autodiff/StandardWellsSolvent_impl.hpp
opm/autodiff/MissingFeatures.hpp
+ opm/autodiff/ThreadHandle.hpp
opm/polymer/CompressibleTpfaPolymer.hpp
opm/polymer/GravityColumnSolverPolymer.hpp
opm/polymer/GravityColumnSolverPolymer_impl.hpp
@@ -243,6 +256,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/ParallelFileMerger.hpp
opm/simulators/SimulatorCompressibleTwophase.hpp
opm/simulators/SimulatorIncompTwophase.hpp
+ opm/simulators/thresholdPressures.hpp
opm/simulators/WellSwitchingLogger.hpp
)
diff --git a/dune.module b/dune.module
index 9b6ca05d6..026125417 100644
--- a/dune.module
+++ b/dune.module
@@ -10,4 +10,4 @@ Label: 2017.04-pre
Maintainer: atgeirr@sintef.no
MaintainerName: Atgeirr F. Rasmussen
Url: http://opm-project.org
-Depends: opm-common opm-parser opm-output opm-material opm-core opm-grid dune-istl (>=2.2)
+Depends: opm-common opm-parser opm-output opm-material opm-core opm-grid dune-istl (>=2.2) ewoms
diff --git a/examples/flow_ebos.cpp b/examples/flow_ebos.cpp
new file mode 100644
index 000000000..9ff12a482
--- /dev/null
+++ b/examples/flow_ebos.cpp
@@ -0,0 +1,39 @@
+/*
+ Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
+ Copyright 2015 IRIS AS
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include
+#include
+#include
+#include
+#include
+
+
+// ----------------- Main program -----------------
+int main(int argc, char** argv)
+{
+ Opm::FlowMainEbos mainfunc;
+ return mainfunc.execute(argc, argv);
+}
diff --git a/jenkins/build.sh b/jenkins/build.sh
index f785f1c8d..04158ebc5 100755
--- a/jenkins/build.sh
+++ b/jenkins/build.sh
@@ -7,7 +7,8 @@ upstreams=(opm-common
opm-output
opm-material
opm-core
- opm-grid)
+ opm-grid
+ ewoms)
declare -A upstreamRev
upstreamRev[opm-common]=master
@@ -17,6 +18,7 @@ upstreamRev[opm-material]=master
upstreamRev[opm-core]=master
upstreamRev[opm-grid]=master
upstreamRev[opm-output]=master
+upstreamRev[ewoms]=master
if grep -q "opm-common=" <<< $ghprbCommentBody
then
diff --git a/jenkins/run-norne.sh b/jenkins/run-norne.sh
index cac96c625..ce12723a7 100755
--- a/jenkins/run-norne.sh
+++ b/jenkins/run-norne.sh
@@ -2,10 +2,11 @@
pushd .
cd deps/opm-data
+test -z $SIM && SIM=flow
# Run the norne case
cd norne
-$WORKSPACE/$configuration/build-opm-simulators/bin/flow deck_filename=NORNE_ATW2013.DATA output_dir=OPM
+$WORKSPACE/$configuration/build-opm-simulators/bin/$SIM deck_filename=NORNE_ATW2013.DATA output_dir=OPM
test $? -eq 0 || exit 1
PATH=$WORKSPACE/$configuration/install/bin:$PATH ./plotwells.sh
diff --git a/opm/autodiff/BlackoilDetails.hpp b/opm/autodiff/BlackoilDetails.hpp
new file mode 100644
index 000000000..7422c2b73
--- /dev/null
+++ b/opm/autodiff/BlackoilDetails.hpp
@@ -0,0 +1,363 @@
+/*
+ Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
+ Copyright 2014, 2015 Dr. Blatt - HPC-Simulation-Software & Services
+ Copyright 2014, 2015 Statoil ASA.
+ Copyright 2015 NTNU
+ Copyright 2015 IRIS AS
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#ifndef OPM_BLACKOILDETAILS_HEADER_INCLUDED
+#define OPM_BLACKOILDETAILS_HEADER_INCLUDED
+
+#include
+
+#include
+#include
+
+namespace Opm {
+namespace detail {
+
+
+ inline
+ std::vector
+ buildAllCells(const int nc)
+ {
+ std::vector all_cells(nc);
+
+ for (int c = 0; c < nc; ++c) { all_cells[c] = c; }
+
+ return all_cells;
+ }
+
+
+
+ template
+ std::vector
+ activePhases(const PU& pu)
+ {
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ std::vector active(maxnp, false);
+
+ for (int p = 0; p < pu.MaxNumPhases; ++p) {
+ active[ p ] = pu.phase_used[ p ] != 0;
+ }
+
+ return active;
+ }
+
+
+
+ template
+ std::vector
+ active2Canonical(const PU& pu)
+ {
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+ std::vector act2can(maxnp, -1);
+
+ for (int phase = 0; phase < maxnp; ++phase) {
+ if (pu.phase_used[ phase ]) {
+ act2can[ pu.phase_pos[ phase ] ] = phase;
+ }
+ }
+
+ return act2can;
+ }
+
+
+
+ inline
+ double getGravity(const double* g, const int dim) {
+ double grav = 0.0;
+ if (g) {
+ // Guard against gravity in anything but last dimension.
+ for (int dd = 0; dd < dim - 1; ++dd) {
+ assert(g[dd] == 0.0);
+ }
+ grav = g[dim - 1];
+ }
+ return grav;
+ }
+
+ /// \brief Compute the L-infinity norm of a vector
+ /// \warn This function is not suitable to compute on the well equations.
+ /// \param a The container to compute the infinity norm on.
+ /// It has to have one entry for each cell.
+ /// \param info In a parallel this holds the information about the data distribution.
+ template
+ inline
+ double infinityNorm( const ADB& a, const boost::any& pinfo = boost::any() )
+ {
+ static_cast(pinfo); // Suppress warning in non-MPI case.
+#if HAVE_MPI
+ if ( pinfo.type() == typeid(ParallelISTLInformation) )
+ {
+ const ParallelISTLInformation& real_info =
+ boost::any_cast(pinfo);
+ double result=0;
+ real_info.computeReduction(a.value(), Reduction::makeLInfinityNormFunctor(), result);
+ return result;
+ }
+ else
+#endif
+ {
+ if( a.value().size() > 0 ) {
+ return a.value().matrix().template lpNorm ();
+ }
+ else { // this situation can occur when no wells are present
+ return 0.0;
+ }
+ }
+ }
+
+ /// \brief Compute the Euclidian norm of a vector
+ /// \warning In the case that num_components is greater than 1
+ /// an interleaved ordering is assumed. E.g. for each cell
+ /// all phases of that cell are stored consecutively. First
+ /// the ones for cell 0, then the ones for cell 1, ... .
+ /// \param it begin iterator for the given vector
+ /// \param end end iterator for the given vector
+ /// \param num_components number of components (i.e. phases) in the vector
+ /// \param pinfo In a parallel this holds the information about the data distribution.
+ template
+ inline
+ double euclidianNormSquared( Iterator it, const Iterator end, int num_components, const boost::any& pinfo = boost::any() )
+ {
+ static_cast(num_components); // Suppress warning in the serial case.
+ static_cast(pinfo); // Suppress warning in non-MPI case.
+#if HAVE_MPI
+ if ( pinfo.type() == typeid(ParallelISTLInformation) )
+ {
+ const ParallelISTLInformation& info =
+ boost::any_cast(pinfo);
+ typedef typename Iterator::value_type Scalar;
+ Scalar product = 0.0;
+ int size_per_component = (end - it);
+ size_per_component /= num_components; // two lines to supresse unused warning.
+ assert((end - it) == num_components * size_per_component);
+
+ if( num_components == 1 )
+ {
+ auto component_container =
+ boost::make_iterator_range(it, end);
+ info.computeReduction(component_container,
+ Opm::Reduction::makeInnerProductFunctor(),
+ product);
+ }
+ else
+ {
+ auto& maskContainer = info.getOwnerMask();
+ auto mask = maskContainer.begin();
+ assert(static_cast(maskContainer.size()) == size_per_component);
+
+ for(int cell = 0; cell < size_per_component; ++cell, ++mask)
+ {
+ Scalar cell_product = (*it) * (*it);
+ ++it;
+ for(int component=1; component < num_components;
+ ++component, ++it)
+ {
+ cell_product += (*it) * (*it);
+ }
+ product += cell_product * (*mask);
+ }
+ }
+ return info.communicator().sum(product);
+ }
+ else
+#endif
+ {
+ double product = 0.0 ;
+ for( ; it != end; ++it ) {
+ product += ( *it * *it );
+ }
+ return product;
+ }
+ }
+ /// \brief Compute the reduction within the convergence check.
+ /// \param[in] B A matrix with MaxNumPhases columns and the same number rows
+ /// as the number of cells of the grid. B.col(i) contains the values
+ /// for phase i.
+ /// \param[in] tempV A matrix with MaxNumPhases columns and the same number rows
+ /// as the number of cells of the grid. tempV.col(i) contains the
+ /// values
+ /// for phase i.
+ /// \param[in] R A matrix with MaxNumPhases columns and the same number rows
+ /// as the number of cells of the grid. B.col(i) contains the values
+ /// for phase i.
+ /// \param[out] R_sum An array of size MaxNumPhases where entry i contains the sum
+ /// of R for the phase i.
+ /// \param[out] maxCoeff An array of size MaxNumPhases where entry i contains the
+ /// maximum of tempV for the phase i.
+ /// \param[out] B_avg An array of size MaxNumPhases where entry i contains the average
+ /// of B for the phase i.
+ /// \param[out] maxNormWell The maximum of the well flux equations for each phase.
+ /// \param[in] nc The number of cells of the local grid.
+ /// \return The total pore volume over all cells.
+ inline
+ double
+ convergenceReduction(const Eigen::Array& B,
+ const Eigen::Array& tempV,
+ const Eigen::Array& R,
+ std::vector& R_sum,
+ std::vector& maxCoeff,
+ std::vector& B_avg,
+ std::vector& maxNormWell,
+ int nc,
+ int np,
+ const std::vector pv,
+ std::vector residual_well)
+ {
+ const int nw = residual_well.size() / np;
+ assert(nw * np == int(residual_well.size()));
+
+ // Do the global reductions
+#if 0 // HAVE_MPI
+ if ( linsolver_.parallelInformation().type() == typeid(ParallelISTLInformation) )
+ {
+ const ParallelISTLInformation& info =
+ boost::any_cast(linsolver_.parallelInformation());
+
+ // Compute the global number of cells and porevolume
+ std::vector v(nc, 1);
+ auto nc_and_pv = std::tuple(0, 0.0);
+ auto nc_and_pv_operators = std::make_tuple(Opm::Reduction::makeGlobalSumFunctor(),
+ Opm::Reduction::makeGlobalSumFunctor());
+ auto nc_and_pv_containers = std::make_tuple(v, pv);
+ info.computeReduction(nc_and_pv_containers, nc_and_pv_operators, nc_and_pv);
+
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ auto values = std::tuple(0.0 ,0.0 ,0.0);
+ auto containers = std::make_tuple(B.col(idx),
+ tempV.col(idx),
+ R.col(idx));
+ auto operators = std::make_tuple(Opm::Reduction::makeGlobalSumFunctor(),
+ Opm::Reduction::makeGlobalMaxFunctor(),
+ Opm::Reduction::makeGlobalSumFunctor());
+ info.computeReduction(containers, operators, values);
+ B_avg[idx] = std::get<0>(values)/std::get<0>(nc_and_pv);
+ maxCoeff[idx] = std::get<1>(values);
+ R_sum[idx] = std::get<2>(values);
+ assert(np >= np);
+ if (idx < np) {
+ maxNormWell[idx] = 0.0;
+ for ( int w = 0; w < nw; ++w ) {
+ maxNormWell[idx] = std::max(maxNormWell[idx], std::abs(residual_well[nw*idx + w]));
+ }
+ }
+ }
+ info.communicator().max(maxNormWell.data(), np);
+ // Compute pore volume
+ return std::get<1>(nc_and_pv);
+ }
+ else
+#endif
+ {
+ B_avg.resize(np);
+ maxCoeff.resize(np);
+ R_sum.resize(np);
+ maxNormWell.resize(np);
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ B_avg[idx] = B.col(idx).sum()/nc;
+ maxCoeff[idx] = tempV.col(idx).maxCoeff();
+ R_sum[idx] = R.col(idx).sum();
+
+ assert(np >= np);
+ if (idx < np) {
+ maxNormWell[idx] = 0.0;
+ for ( int w = 0; w < nw; ++w ) {
+ maxNormWell[idx] = std::max(maxNormWell[idx], std::abs(residual_well[nw*idx + w]));
+ }
+ }
+ }
+ // Compute total pore volume
+ return std::accumulate(pv.begin(), pv.end(), 0.0);
+ }
+ }
+
+
+ template
+ inline
+ double
+ convergenceReduction(const std::vector< std::vector< Scalar > >& B,
+ const std::vector< std::vector< Scalar > >& tempV,
+ const std::vector< std::vector< Scalar > >& R,
+ std::vector< Scalar >& R_sum,
+ std::vector< Scalar >& maxCoeff,
+ std::vector< Scalar >& B_avg,
+ std::vector< Scalar >& maxNormWell,
+ const int nc,
+ const int np,
+ const std::vector< Scalar >& pv,
+ const std::vector< Scalar >& residual_well)
+ {
+ const int nw = residual_well.size() / np;
+ assert(nw * np == int(residual_well.size()));
+
+ // Do the global reductions
+ {
+ B_avg.resize(np);
+ maxCoeff.resize(np);
+ R_sum.resize(np);
+ maxNormWell.resize(np);
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ B_avg[idx] = std::accumulate( B[ idx ].begin(), B[ idx ].end(), 0.0 ) / nc;
+ R_sum[idx] = std::accumulate( R[ idx ].begin(), R[ idx ].end(), 0.0 );
+ maxCoeff[idx] = *(std::max_element( tempV[ idx ].begin(), tempV[ idx ].end() ));
+
+ assert(np >= np);
+ if (idx < np) {
+ maxNormWell[idx] = 0.0;
+ for ( int w = 0; w < nw; ++w ) {
+ maxNormWell[idx] = std::max(maxNormWell[idx], std::abs(residual_well[nw*idx + w]));
+ }
+ }
+ }
+ // Compute total pore volume
+ return std::accumulate(pv.begin(), pv.end(), 0.0);
+ }
+ }
+
+ /// \brief Compute the L-infinity norm of a vector representing a well equation.
+ /// \param a The container to compute the infinity norm on.
+ /// \param info In a parallel this holds the information about the data distribution.
+ template
+ inline
+ double infinityNormWell( const ADB& a, const boost::any& pinfo )
+ {
+ static_cast(pinfo); // Suppress warning in non-MPI case.
+ double result=0;
+ if( a.value().size() > 0 ) {
+ result = a.value().matrix().template lpNorm ();
+ }
+#if HAVE_MPI
+ if ( pinfo.type() == typeid(ParallelISTLInformation) )
+ {
+ const ParallelISTLInformation& real_info =
+ boost::any_cast(pinfo);
+ result = real_info.communicator().max(result);
+ }
+#endif
+ return result;
+ }
+ } // namespace detail
+} // namespace Opm
+
+#endif // OPM_BLACKOILDETAILS_HEADER_INCLUDED
diff --git a/opm/autodiff/BlackoilModelBase.hpp b/opm/autodiff/BlackoilModelBase.hpp
index 87a96e696..3f4e01e66 100644
--- a/opm/autodiff/BlackoilModelBase.hpp
+++ b/opm/autodiff/BlackoilModelBase.hpp
@@ -33,6 +33,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -49,47 +51,6 @@ namespace Opm {
class VFPProperties;
class SimulationDataContainer;
- /// Struct for containing iteration variables.
- struct DefaultBlackoilSolutionState
- {
- typedef AutoDiffBlock ADB;
- explicit DefaultBlackoilSolutionState(const int np)
- : pressure ( ADB::null())
- , temperature( ADB::null())
- , saturation(np, ADB::null())
- , rs ( ADB::null())
- , rv ( ADB::null())
- , qs ( ADB::null())
- , bhp ( ADB::null())
- , canonical_phase_pressures(3, ADB::null())
- {
- }
- ADB pressure;
- ADB temperature;
- std::vector saturation;
- ADB rs;
- ADB rv;
- ADB qs;
- ADB bhp;
- // Below are quantities stored in the state for optimization purposes.
- std::vector canonical_phase_pressures; // Always has 3 elements, even if only 2 phases active.
- };
-
-
-
-
- /// Class used for reporting the outcome of a nonlinearIteration() call.
- struct IterationReport
- {
- bool failed;
- bool converged;
- int linear_iterations;
- int well_iterations;
- };
-
-
-
-
/// Traits to encapsulate the types used by classes using or
/// extending this model. Forward declared here, must be
/// specialised for each concrete model class.
diff --git a/opm/autodiff/BlackoilModelBase_impl.hpp b/opm/autodiff/BlackoilModelBase_impl.hpp
index dbbcc7322..cd989f39b 100644
--- a/opm/autodiff/BlackoilModelBase_impl.hpp
+++ b/opm/autodiff/BlackoilModelBase_impl.hpp
@@ -25,6 +25,7 @@
#define OPM_BLACKOILMODELBASE_IMPL_HEADER_INCLUDED
#include
+#include
#include
#include
@@ -93,73 +94,6 @@ typedef Eigen::Array DataBlock;
-
-namespace detail {
-
-
- inline
- std::vector
- buildAllCells(const int nc)
- {
- std::vector all_cells(nc);
-
- for (int c = 0; c < nc; ++c) { all_cells[c] = c; }
-
- return all_cells;
- }
-
-
-
- template
- std::vector
- activePhases(const PU& pu)
- {
- const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
- std::vector active(maxnp, false);
-
- for (int p = 0; p < pu.MaxNumPhases; ++p) {
- active[ p ] = pu.phase_used[ p ] != 0;
- }
-
- return active;
- }
-
-
-
- template
- std::vector
- active2Canonical(const PU& pu)
- {
- const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
- std::vector act2can(maxnp, -1);
-
- for (int phase = 0; phase < maxnp; ++phase) {
- if (pu.phase_used[ phase ]) {
- act2can[ pu.phase_pos[ phase ] ] = phase;
- }
- }
-
- return act2can;
- }
-
-
-
- inline
- double getGravity(const double* g, const int dim) {
- double grav = 0.0;
- if (g) {
- // Guard against gravity in anything but last dimension.
- for (int dd = 0; dd < dim - 1; ++dd) {
- assert(g[dd] == 0.0);
- }
- grav = g[dim - 1];
- }
- return grav;
- }
-
-} // namespace detail
-
-
template
BlackoilModelBase::
BlackoilModelBase(const ModelParameters& param,
@@ -1155,135 +1089,6 @@ namespace detail {
return linsolver_.computeNewtonIncrement(residual_);
}
-
-
-
-
- namespace detail
- {
- /// \brief Compute the L-infinity norm of a vector
- /// \warn This function is not suitable to compute on the well equations.
- /// \param a The container to compute the infinity norm on.
- /// It has to have one entry for each cell.
- /// \param info In a parallel this holds the information about the data distribution.
- inline
- double infinityNorm( const ADB& a, const boost::any& pinfo = boost::any() )
- {
- static_cast(pinfo); // Suppress warning in non-MPI case.
-#if HAVE_MPI
- if ( pinfo.type() == typeid(ParallelISTLInformation) )
- {
- const ParallelISTLInformation& real_info =
- boost::any_cast(pinfo);
- double result=0;
- real_info.computeReduction(a.value(), Reduction::makeLInfinityNormFunctor(), result);
- return result;
- }
- else
-#endif
- {
- if( a.value().size() > 0 ) {
- return a.value().matrix().lpNorm ();
- }
- else { // this situation can occur when no wells are present
- return 0.0;
- }
- }
- }
-
- /// \brief Compute the Euclidian norm of a vector
- /// \warning In the case that num_components is greater than 1
- /// an interleaved ordering is assumed. E.g. for each cell
- /// all phases of that cell are stored consecutively. First
- /// the ones for cell 0, then the ones for cell 1, ... .
- /// \param it begin iterator for the given vector
- /// \param end end iterator for the given vector
- /// \param num_components number of components (i.e. phases) in the vector
- /// \param pinfo In a parallel this holds the information about the data distribution.
- template
- inline
- double euclidianNormSquared( Iterator it, const Iterator end, int num_components, const boost::any& pinfo = boost::any() )
- {
- static_cast(num_components); // Suppress warning in the serial case.
- static_cast(pinfo); // Suppress warning in non-MPI case.
-#if HAVE_MPI
- if ( pinfo.type() == typeid(ParallelISTLInformation) )
- {
- const ParallelISTLInformation& info =
- boost::any_cast(pinfo);
- typedef typename Iterator::value_type Scalar;
- Scalar product = 0.0;
- int size_per_component = (end - it);
- size_per_component /= num_components; // two lines to supresse unused warning.
- assert((end - it) == num_components * size_per_component);
-
- if( num_components == 1 )
- {
- auto component_container =
- boost::make_iterator_range(it, end);
- info.computeReduction(component_container,
- Opm::Reduction::makeInnerProductFunctor(),
- product);
- }
- else
- {
- auto& maskContainer = info.getOwnerMask();
- auto mask = maskContainer.begin();
- assert(static_cast(maskContainer.size()) == size_per_component);
-
- for(int cell = 0; cell < size_per_component; ++cell, ++mask)
- {
- Scalar cell_product = (*it) * (*it);
- ++it;
- for(int component=1; component < num_components;
- ++component, ++it)
- {
- cell_product += (*it) * (*it);
- }
- product += cell_product * (*mask);
- }
- }
- return info.communicator().sum(product);
- }
- else
-#endif
- {
- double product = 0.0 ;
- for( ; it != end; ++it ) {
- product += ( *it * *it );
- }
- return product;
- }
- }
-
- /// \brief Compute the L-infinity norm of a vector representing a well equation.
- /// \param a The container to compute the infinity norm on.
- /// \param info In a parallel this holds the information about the data distribution.
- inline
- double infinityNormWell( const ADB& a, const boost::any& pinfo )
- {
- static_cast(pinfo); // Suppress warning in non-MPI case.
- double result=0;
- if( a.value().size() > 0 ) {
- result = a.value().matrix().lpNorm ();
- }
-#if HAVE_MPI
- if ( pinfo.type() == typeid(ParallelISTLInformation) )
- {
- const ParallelISTLInformation& real_info =
- boost::any_cast(pinfo);
- result = real_info.communicator().max(result);
- }
-#endif
- return result;
- }
-
- } // namespace detail
-
-
-
-
-
template
void
BlackoilModelBase::
diff --git a/opm/autodiff/BlackoilModelEbos.hpp b/opm/autodiff/BlackoilModelEbos.hpp
new file mode 100644
index 000000000..b3b34f6b3
--- /dev/null
+++ b/opm/autodiff/BlackoilModelEbos.hpp
@@ -0,0 +1,1459 @@
+/*
+ Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
+ Copyright 2014, 2015 Dr. Blatt - HPC-Simulation-Software & Services
+ Copyright 2014, 2015 Statoil ASA.
+ Copyright 2015 NTNU
+ Copyright 2015 IRIS AS
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#ifndef OPM_BLACKOILMODELEBOS_HEADER_INCLUDED
+#define OPM_BLACKOILMODELEBOS_HEADER_INCLUDED
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+
+
+
+namespace Ewoms {
+namespace Properties {
+NEW_TYPE_TAG(EclFlowProblem, INHERITS_FROM(BlackOilModel, EclBaseProblem));
+SET_BOOL_PROP(EclFlowProblem, DisableWells, true);
+SET_BOOL_PROP(EclFlowProblem, EnableDebuggingChecks, false);
+}}
+
+namespace Opm {
+
+
+ namespace parameter { class ParameterGroup; }
+ class DerivedGeology;
+ class RockCompressibility;
+ class NewtonIterationBlackoilInterface;
+ class VFPProperties;
+ class SimulationDataContainer;
+
+
+
+
+ /// A model implementation for three-phase black oil.
+ ///
+ /// The simulator is capable of handling three-phase problems
+ /// where gas can be dissolved in oil and vice versa. It
+ /// uses an industry-standard TPFA discretization with per-phase
+ /// upwind weighting of mobilities.
+ class BlackoilModelEbos
+ {
+ typedef BlackoilModelEbos ThisType;
+ public:
+ // --------- Types and enums ---------
+ typedef BlackoilState ReservoirState;
+ typedef WellStateFullyImplicitBlackoilDense WellState;
+ typedef BlackoilModelParameters ModelParameters;
+ typedef DefaultBlackoilSolutionState SolutionState;
+
+ typedef typename TTAG(EclFlowProblem) TypeTag;
+ typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator ;
+ typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
+ typedef typename GET_PROP_TYPE(TypeTag, SolutionVector) SolutionVector ;
+ typedef typename GET_PROP_TYPE(TypeTag, PrimaryVariables) PrimaryVariables ;
+ typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
+ typedef typename GET_PROP_TYPE(TypeTag, Indices) BlackoilIndices;
+ typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw;
+ typedef typename GET_PROP_TYPE(TypeTag, MaterialLawParams) MaterialLawParams;
+
+ typedef double Scalar;
+ typedef Dune::FieldVector VectorBlockType;
+ typedef Dune::FieldMatrix MatrixBlockType;
+ typedef Dune::BCRSMatrix Mat;
+ typedef Dune::BlockVector BVector;
+
+ typedef ISTLSolver< MatrixBlockType, VectorBlockType > ISTLSolverType;
+ //typedef typename SolutionVector :: value_type PrimaryVariables ;
+
+ struct FIPData {
+ enum FipId {
+ FIP_AQUA = Opm::Water,
+ FIP_LIQUID = Opm::Oil,
+ FIP_VAPOUR = Opm::Gas,
+ FIP_DISSOLVED_GAS = 3,
+ FIP_VAPORIZED_OIL = 4,
+ FIP_PV = 5, //< Pore volume
+ FIP_WEIGHTED_PRESSURE = 6
+ };
+ std::array, 7> fip;
+ };
+
+ // --------- Public methods ---------
+
+ /// Construct the model. It will retain references to the
+ /// arguments of this functions, and they are expected to
+ /// remain in scope for the lifetime of the solver.
+ /// \param[in] param parameters
+ /// \param[in] grid grid data structure
+ /// \param[in] fluid fluid properties
+ /// \param[in] geo rock properties
+ /// \param[in] rock_comp_props if non-null, rock compressibility properties
+ /// \param[in] wells well structure
+ /// \param[in] vfp_properties Vertical flow performance tables
+ /// \param[in] linsolver linear solver
+ /// \param[in] eclState eclipse state
+ /// \param[in] terminal_output request output to cout/cerr
+ BlackoilModelEbos(Simulator& ebosSimulator,
+ const ModelParameters& param,
+ const BlackoilPropsAdInterface& fluid,
+ const DerivedGeology& geo ,
+ const RockCompressibility* rock_comp_props,
+ const StandardWellsDense& well_model,
+ const NewtonIterationBlackoilInterface& linsolver,
+ const bool terminal_output)
+ : ebosSimulator_(ebosSimulator)
+ , grid_(ebosSimulator_.gridManager().grid())
+ , istlSolver_( dynamic_cast< const ISTLSolverType* > (&linsolver) )
+ , fluid_ (fluid)
+ , geo_ (geo)
+ , vfp_properties_(
+ eclState().getTableManager().getVFPInjTables(),
+ eclState().getTableManager().getVFPProdTables())
+ , active_(detail::activePhases(fluid.phaseUsage()))
+ , has_disgas_(FluidSystem::enableDissolvedGas())
+ , has_vapoil_(FluidSystem::enableVaporizedOil())
+ , param_( param )
+ , well_model_ (well_model)
+ , terminal_output_ (terminal_output)
+ , current_relaxation_(1.0)
+ , dx_old_(AutoDiffGrid::numCells(grid_))
+ , isBeginReportStep_(false)
+ , invalidateIntensiveQuantitiesCache_(true)
+ {
+ const double gravity = detail::getGravity(geo_.gravity(), UgGridHelpers::dimensions(grid_));
+ const std::vector pv(geo_.poreVolume().data(), geo_.poreVolume().data() + geo_.poreVolume().size());
+ const std::vector depth(geo_.z().data(), geo_.z().data() + geo_.z().size());
+ well_model_.init(&fluid_, &active_, &vfp_properties_, gravity, depth, pv);
+ wellModel().setWellsActive( localWellsActive() );
+ global_nc_ = Opm::AutoDiffGrid::numCells(grid_);
+ // compute global sum of number of cells
+ global_nc_ = grid_.comm().sum( global_nc_ );
+
+ if (!istlSolver_)
+ {
+ OPM_THROW(std::logic_error,"solver down cast to ISTLSolver failed");
+ }
+ }
+
+ bool
+ isParallel() const
+ {
+ #if HAVE_MPI
+ if ( istlSolver().parallelInformation().type() !=
+ typeid(ParallelISTLInformation) )
+ {
+ return false;
+ }
+ else
+ {
+ const auto& comm =boost::any_cast(istlSolver().parallelInformation()).communicator();
+ return comm.size() > 1;
+ }
+ #else
+ return false;
+ #endif
+ }
+
+
+ const EclipseState& eclState() const
+ { return *ebosSimulator_.gridManager().eclState(); }
+
+ /// Called once before each time step.
+ /// \param[in] timer simulation timer
+ /// \param[in, out] reservoir_state reservoir state variables
+ /// \param[in, out] well_state well state variables
+ void prepareStep(const SimulatorTimerInterface& /*timer*/,
+ const ReservoirState& /*reservoir_state*/,
+ const WellState& /* well_state */)
+ {
+ }
+
+
+ /// Called once per nonlinear iteration.
+ /// This model will perform a Newton-Raphson update, changing reservoir_state
+ /// and well_state. It will also use the nonlinear_solver to do relaxation of
+ /// updates if necessary.
+ /// \param[in] iteration should be 0 for the first call of a new timestep
+ /// \param[in] timer simulation timer
+ /// \param[in] nonlinear_solver nonlinear solver used (for oscillation/relaxation control)
+ /// \param[in, out] reservoir_state reservoir state variables
+ /// \param[in, out] well_state well state variables
+ template
+ IterationReport nonlinearIteration(const int iteration,
+ const SimulatorTimerInterface& timer,
+ NonlinearSolverType& nonlinear_solver,
+ ReservoirState& reservoir_state,
+ WellState& well_state)
+ {
+ if (iteration == 0) {
+ // For each iteration we store in a vector the norms of the residual of
+ // the mass balance for each active phase, the well flux and the well equations.
+ residual_norms_history_.clear();
+ current_relaxation_ = 1.0;
+ dx_old_ = 0.0;
+ }
+
+ // reset intensive quantities cache useless other options are set
+ // further down
+ invalidateIntensiveQuantitiesCache_ = true;
+
+ IterationReport iter_report = assemble(timer, iteration, reservoir_state, well_state);
+ std::vector residual_norms;
+ const bool converged = getConvergence(timer, iteration,residual_norms);
+ residual_norms_history_.push_back(residual_norms);
+ bool must_solve = (iteration < nonlinear_solver.minIter()) || (!converged);
+
+ // don't solve if we have reached the maximum number of iteration.
+ must_solve = must_solve && (iteration < nonlinear_solver.maxIter());
+ if (must_solve) {
+
+ // enable single precision for solvers when dt is smaller then 20 days
+ //residual_.singlePrecision = (unit::convert::to(dt, unit::day) < 20.) ;
+
+ // Compute the nonlinear update.
+ const int nc = AutoDiffGrid::numCells(grid_);
+ const int nw = wellModel().wells().number_of_wells;
+ BVector x(nc);
+ BVector xw(nw);
+
+ solveJacobianSystem(x, xw);
+
+ // Stabilize the nonlinear update.
+ bool isOscillate = false;
+ bool isStagnate = false;
+ nonlinear_solver.detectOscillations(residual_norms_history_, iteration, isOscillate, isStagnate);
+ if (isOscillate) {
+ current_relaxation_ -= nonlinear_solver.relaxIncrement();
+ current_relaxation_ = std::max(current_relaxation_, nonlinear_solver.relaxMax());
+ if (terminalOutputEnabled()) {
+ std::string msg = " Oscillating behavior detected: Relaxation set to "
+ + std::to_string(current_relaxation_);
+ OpmLog::info(msg);
+ }
+ }
+ nonlinear_solver.stabilizeNonlinearUpdate(x, dx_old_, current_relaxation_);
+
+ // Apply the update, applying model-dependent
+ // limitations and chopping of the update.
+ updateState(x,reservoir_state);
+ wellModel().updateWellState(xw, well_state);
+
+ // since the solution was changed, the cache for the intensive quantities are invalid
+ // ebosSimulator_.model().invalidateIntensiveQuantitiesCache(/*timeIdx=*/0);
+ }
+
+ if( converged && (iteration >= nonlinear_solver.minIter()) )
+ {
+ // in case of convergence we do not need to reset intensive quantities
+ invalidateIntensiveQuantitiesCache_ = false ;
+ }
+
+ const bool failed = false; // Not needed in this model.
+ const int linear_iters = must_solve ? linearIterationsLastSolve() : 0;
+ return IterationReport{ failed, converged, linear_iters, iter_report.well_iterations };
+ }
+
+ void printIf(int c, double x, double y, double eps, std::string type) {
+ if (std::abs(x-y) > eps) {
+ std::cout << type << " " < p0 ( previous.pressure() );
+ std::vector< double > sat0( previous.saturation() );
+
+ const std::size_t pSize = p0.size();
+ const std::size_t satSize = sat0.size();
+
+ // compute u^n - u^n+1
+ for( std::size_t i=0; i 0.0 ) {
+ return stateOld / stateNew ;
+ }
+ else {
+ return 0.0;
+ }
+ }
+
+
+ /// The size (number of unknowns) of the nonlinear system of equations.
+ int sizeNonLinear() const
+ {
+ const int nc = Opm::AutoDiffGrid::numCells(grid_);
+ const int nw = wellModel().wells().number_of_wells;
+ return numPhases() * (nc + nw);
+ }
+
+ /// Number of linear iterations used in last call to solveJacobianSystem().
+ int linearIterationsLastSolve() const
+ {
+ return istlSolver().iterations();
+ }
+
+ template
+ void applyWellModelAdd(const X& x, Y& y )
+ {
+ wellModel().apply(x, y);
+ }
+
+ template
+ void applyWellModelScaleAdd(const Scalar alpha, const X& x, Y& y )
+ {
+ wellModel().applyScaleAdd(alpha, x, y);
+ }
+
+
+ /// Solve the Jacobian system Jx = r where J is the Jacobian and
+ /// r is the residual.
+ void solveJacobianSystem(BVector& x, BVector& xw) const
+ {
+ const auto& ebosJac = ebosSimulator_.model().linearizer().matrix();
+ auto& ebosResid = ebosSimulator_.model().linearizer().residual();
+
+ // apply well residual to the residual.
+ wellModel().apply(ebosResid);
+
+ // set initial guess
+ x = 0.0;
+
+ // Solve system.
+ if( isParallel() )
+ {
+ typedef WellModelMatrixAdapter< Mat, BVector, BVector, ThisType, true > Operator;
+ Operator opA(ebosJac, const_cast< ThisType& > (*this), istlSolver().parallelInformation() );
+ assert( opA.comm() );
+ istlSolver().solve( opA, x, ebosResid, *(opA.comm()) );
+ }
+ else
+ {
+ typedef WellModelMatrixAdapter< Mat, BVector, BVector, ThisType, false > Operator;
+ Operator opA(ebosJac, const_cast< ThisType& > (*this) );
+ istlSolver().solve( opA, x, ebosResid );
+ }
+
+ // recover wells.
+ xw = 0.0;
+ wellModel().recoverVariable(x, xw);
+ }
+
+ //=====================================================================
+ // Implementation for ISTL-matrix based operator
+ //=====================================================================
+
+ /*!
+ \brief Adapter to turn a matrix into a linear operator.
+
+ Adapts a matrix to the assembled linear operator interface
+ */
+ template
+ class WellModelMatrixAdapter : public Dune::AssembledLinearOperator
+ {
+ typedef Dune::AssembledLinearOperator BaseType;
+
+ public:
+ typedef M matrix_type;
+ typedef X domain_type;
+ typedef Y range_type;
+ typedef typename X::field_type field_type;
+
+#if HAVE_MPI
+ typedef Dune::OwnerOverlapCopyCommunication communication_type;
+#else
+ typedef Dune::CollectiveCommunication< Grid > communication_type;
+#endif
+
+ enum {
+ //! \brief The solver category.
+ category = overlapping ?
+ Dune::SolverCategory::overlapping :
+ Dune::SolverCategory::sequential
+ };
+
+ //! constructor: just store a reference to a matrix
+ WellModelMatrixAdapter (const M& A, WellModel& wellMod, const boost::any& parallelInformation = boost::any() )
+ : A_( A ), wellMod_( wellMod ), comm_()
+ {
+#if HAVE_MPI
+ if( parallelInformation.type() == typeid(ParallelISTLInformation) )
+ {
+ const ParallelISTLInformation& info =
+ boost::any_cast( parallelInformation);
+ comm_.reset( new communication_type( info.communicator() ) );
+ }
+#endif
+ }
+
+ virtual void apply( const X& x, Y& y ) const
+ {
+ A_.mv( x, y );
+ // add well model modification to y
+ wellMod_.applyWellModelAdd(x, y );
+
+#if HAVE_MPI
+ if( comm_ )
+ comm_->project( y );
+#endif
+ }
+
+ // y += \alpha * A * x
+ virtual void applyscaleadd (field_type alpha, const X& x, Y& y) const
+ {
+ A_.usmv(alpha,x,y);
+ // add scaled well model modification to y
+ wellMod_.applyWellModelScaleAdd( alpha, x, y );
+
+#if HAVE_MPI
+ if( comm_ )
+ comm_->project( y );
+#endif
+ }
+
+ virtual const matrix_type& getmat() const { return A_; }
+
+ communication_type* comm()
+ {
+ return comm_.operator->();
+ }
+
+ protected:
+ const matrix_type& A_ ;
+ WellModel& wellMod_;
+ std::unique_ptr< communication_type > comm_;
+ };
+
+ /// Apply an update to the primary variables, chopped if appropriate.
+ /// \param[in] dx updates to apply to primary variables
+ /// \param[in, out] reservoir_state reservoir state variables
+ /// \param[in, out] well_state well state variables
+ void updateState(const BVector& dx,
+ ReservoirState& reservoir_state)
+ {
+ using namespace Opm::AutoDiffGrid;
+ const int np = fluid_.numPhases();
+ const int nc = numCells(grid_);
+
+ for (int cell_idx = 0; cell_idx < nc; ++cell_idx) {
+ const double& dp = dx[cell_idx][flowPhaseToEbosCompIdx(0)];
+ //reservoir_state.pressure()[cell_idx] -= dp;
+ double& p = reservoir_state.pressure()[cell_idx];
+ const double& dp_rel_max = dpMaxRel();
+ const int sign_dp = dp > 0 ? 1: -1;
+ p -= sign_dp * std::min(std::abs(dp), std::abs(p)*dp_rel_max);
+ p = std::max(p, 0.0);
+
+ // Saturation updates.
+ const double dsw = active_[Water] ? dx[cell_idx][flowPhaseToEbosCompIdx(1)] : 0.0;
+ const int xvar_ind = active_[Water] ? 2 : 1;
+ const double dxvar = active_[Gas] ? dx[cell_idx][flowPhaseToEbosCompIdx(xvar_ind)] : 0.0;
+
+ double dso = 0.0;
+ double dsg = 0.0;
+ double drs = 0.0;
+ double drv = 0.0;
+
+ double maxVal = 0.0;
+ // water phase
+ maxVal = std::max(std::abs(dsw),maxVal);
+ dso -= dsw;
+ // gas phase
+ switch (reservoir_state.hydroCarbonState()[cell_idx]) {
+ case HydroCarbonState::GasAndOil:
+ dsg = dxvar;
+ break;
+ case HydroCarbonState::OilOnly:
+ drs = dxvar;
+ break;
+ case HydroCarbonState::GasOnly:
+ dsg -= dsw;
+ drv = dxvar;
+ break;
+ default:
+ OPM_THROW(std::logic_error, "Unknown primary variable enum value in cell " << cell_idx << ": " << reservoir_state.hydroCarbonState()[cell_idx]);
+ }
+ dso -= dsg;
+
+ // Appleyard chop process.
+ maxVal = std::max(std::abs(dsg),maxVal);
+ double step = dsMax()/maxVal;
+ step = std::min(step, 1.0);
+
+
+ const Opm::PhaseUsage& pu = fluid_.phaseUsage();
+ if (active_[Water]) {
+ double& sw = reservoir_state.saturation()[cell_idx*np + pu.phase_pos[ Water ]];
+ sw -= step * dsw;
+ }
+ if (active_[Gas]) {
+ double& sg = reservoir_state.saturation()[cell_idx*np + pu.phase_pos[ Gas ]];
+ sg -= step * dsg;
+ }
+ double& so = reservoir_state.saturation()[cell_idx*np + pu.phase_pos[ Oil ]];
+ so -= step * dso;
+
+ // const double drmaxrel = drMaxRel();
+ // Update rs and rv
+ if (has_disgas_) {
+ double& rs = reservoir_state.gasoilratio()[cell_idx];
+ rs -= drs;
+ rs = std::max(rs, 0.0);
+
+ }
+ if (has_vapoil_) {
+ double& rv = reservoir_state.rv()[cell_idx];
+ rv -= drv;
+ rv = std::max(rv, 0.0);
+ }
+
+ // Sg is used as primal variable for water only cells.
+ const double epsilon = 1e-4; //std::sqrt(std::numeric_limits::epsilon());
+ double& sw = reservoir_state.saturation()[cell_idx*np + pu.phase_pos[ Water ]];
+ double& sg = reservoir_state.saturation()[cell_idx*np + pu.phase_pos[ Gas ]];
+ double& rs = reservoir_state.gasoilratio()[cell_idx];
+ double& rv = reservoir_state.rv()[cell_idx];
+
+
+ // phase translation sg <-> rs
+ const HydroCarbonState hydroCarbonState = reservoir_state.hydroCarbonState()[cell_idx];
+ const auto& intQuants = *(ebosSimulator_.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0));
+ const auto& fs = intQuants.fluidState();
+ switch (hydroCarbonState) {
+ case HydroCarbonState::GasAndOil: {
+
+ if (sw > (1.0 - epsilon)) // water only i.e. do nothing
+ break;
+
+ if (sg <= 0.0 && has_disgas_) {
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::OilOnly; // sg --> rs
+ sg = 0;
+ so = 1.0 - sw - sg;
+ const double& rsSat = FluidSystem::oilPvt().saturatedGasDissolutionFactor(fs.pvtRegionIndex(), reservoir_state.temperature()[cell_idx], reservoir_state.pressure()[cell_idx]);
+ double& rs = reservoir_state.gasoilratio()[cell_idx];
+ rs = rsSat*(1-epsilon);
+ } else if (so <= 0.0 && has_vapoil_) {
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::GasOnly; // sg --> rv
+ so = 0;
+ sg = 1.0 - sw - so;
+ double& rv = reservoir_state.rv()[cell_idx];
+ // use gas pressure?
+ const double& rvSat = FluidSystem::gasPvt().saturatedOilVaporizationFactor(fs.pvtRegionIndex(), reservoir_state.temperature()[cell_idx], reservoir_state.pressure()[cell_idx]);
+ rv = rvSat*(1-epsilon);
+ }
+ break;
+ }
+ case HydroCarbonState::OilOnly: {
+ if (sw > (1.0 - epsilon)) {
+ // water only change to Sg
+ rs = 0;
+ rv = 0;
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::GasAndOil;
+ //std::cout << "watonly rv -> sg" << cell_idx << std::endl;
+ break;
+ }
+
+
+
+
+ const double& rsSat = FluidSystem::oilPvt().saturatedGasDissolutionFactor(fs.pvtRegionIndex(), reservoir_state.temperature()[cell_idx], reservoir_state.pressure()[cell_idx]);
+ if (rs > ( rsSat * (1+epsilon) ) ) {
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::GasAndOil;
+ sg = epsilon;
+ so -= epsilon;
+ rs = rsSat;
+ }
+ break;
+ }
+ case HydroCarbonState::GasOnly: {
+ if (sw > (1.0 - epsilon)) {
+ // water only change to Sg
+ rs = 0;
+ rv = 0;
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::GasAndOil;
+ //std::cout << "watonly rv -> sg" << cell_idx << std::endl;
+ break;
+ }
+ const double& rvSat = FluidSystem::gasPvt().saturatedOilVaporizationFactor(fs.pvtRegionIndex(), reservoir_state.temperature()[cell_idx], reservoir_state.pressure()[cell_idx]);
+ if (rv > rvSat * (1+epsilon) ) {
+ reservoir_state.hydroCarbonState()[cell_idx] = HydroCarbonState::GasAndOil;
+ so = epsilon;
+ rv = rvSat;
+ sg -= epsilon;
+ }
+ break;
+ }
+
+ default:
+ OPM_THROW(std::logic_error, "Unknown primary variable enum value in cell " << cell_idx << ": " << hydroCarbonState);
+ }
+ }
+
+ }
+
+
+ /// Return true if output to cout is wanted.
+ bool terminalOutputEnabled() const
+ {
+ return terminal_output_;
+ }
+
+ template
+ double convergenceReduction(const CollectiveCommunication& comm,
+ const long int ncGlobal,
+ const int np,
+ const std::vector< std::vector< Scalar > >& B,
+ const std::vector< std::vector< Scalar > >& tempV,
+ const std::vector< std::vector< Scalar > >& R,
+ const std::vector< Scalar >& pv,
+ const std::vector< Scalar >& residual_well,
+ std::vector< Scalar >& R_sum,
+ std::vector< Scalar >& maxCoeff,
+ std::vector< Scalar >& B_avg,
+ std::vector< Scalar >& maxNormWell )
+ {
+ const int nw = residual_well.size() / np;
+ assert(nw * np == int(residual_well.size()));
+
+ // Do the global reductions
+ B_avg.resize(np);
+ maxCoeff.resize(np);
+ R_sum.resize(np);
+ maxNormWell.resize(np);
+
+ // computation
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ B_avg[idx] = std::accumulate( B[ idx ].begin(), B[ idx ].end(), 0.0 ) / double(ncGlobal);
+ R_sum[idx] = std::accumulate( R[ idx ].begin(), R[ idx ].end(), 0.0 );
+ maxCoeff[idx] = *(std::max_element( tempV[ idx ].begin(), tempV[ idx ].end() ));
+
+ assert(np >= np);
+ if (idx < np) {
+ maxNormWell[idx] = 0.0;
+ for ( int w = 0; w < nw; ++w ) {
+ maxNormWell[idx] = std::max(maxNormWell[idx], std::abs(residual_well[nw*idx + w]));
+ }
+ }
+ }
+
+ // Compute total pore volume
+ double pvSum = std::accumulate(pv.begin(), pv.end(), 0.0);
+
+ if( comm.size() > 1 )
+ {
+ // global reduction
+ std::vector< Scalar > sumBuffer;
+ std::vector< Scalar > maxBuffer;
+ sumBuffer.reserve( B_avg.size() + R_sum.size() + 1 );
+ maxBuffer.reserve( maxCoeff.size() + maxNormWell.size() );
+ for( int idx = 0; idx < np; ++idx )
+ {
+ sumBuffer.push_back( B_avg[ idx ] );
+ sumBuffer.push_back( R_sum[ idx ] );
+ maxBuffer.push_back( maxCoeff[ idx ] );
+ maxBuffer.push_back( maxNormWell[ idx ] );
+ }
+
+ // Compute total pore volume
+ sumBuffer.push_back( pvSum );
+
+ // compute global sum
+ comm.sum( sumBuffer.data(), sumBuffer.size() );
+
+ // compute global max
+ comm.max( maxBuffer.data(), maxBuffer.size() );
+
+ // restore values to local variables
+ for( int idx = 0, buffIdx = 0; idx < np; ++idx, ++buffIdx )
+ {
+ B_avg[ idx ] = sumBuffer[ buffIdx ];
+ maxCoeff[ idx ] = maxBuffer[ buffIdx ];
+ ++buffIdx;
+
+ R_sum[ idx ] = sumBuffer[ buffIdx ];
+ maxNormWell[ idx ] = maxBuffer[ buffIdx ];
+ }
+
+ // restore global pore volume
+ pvSum = sumBuffer.back();
+ }
+
+ // return global pore volume
+ return pvSum;
+ }
+
+ /// Compute convergence based on total mass balance (tol_mb) and maximum
+ /// residual mass balance (tol_cnv).
+ /// \param[in] timer simulation timer
+ /// \param[in] dt timestep length
+ /// \param[in] iteration current iteration number
+ bool getConvergence(const SimulatorTimerInterface& timer, const int iteration, std::vector& residual_norms)
+ {
+ typedef std::vector< double > Vector;
+
+ 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 nc = Opm::AutoDiffGrid::numCells(grid_);
+ const int np = numPhases();
+
+ const auto& pv = geo_.poreVolume();
+
+ Vector R_sum(np);
+ Vector B_avg(np);
+ Vector maxCoeff(np);
+ Vector maxNormWell(np);
+
+ std::vector< Vector > B( np, Vector( nc ) );
+ std::vector< Vector > R( np, Vector( nc ) );
+ std::vector< Vector > R2( np, Vector( nc ) );
+ std::vector< Vector > tempV( np, Vector( nc ) );
+
+ const auto& ebosResid = ebosSimulator_.model().linearizer().residual();
+
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ Vector& R2_idx = R2[ idx ];
+ Vector& B_idx = B[ idx ];
+ const int ebosPhaseIdx = flowPhaseToEbosPhaseIdx(idx);
+ const int ebosCompIdx = flowPhaseToEbosCompIdx(idx);
+
+ for (int cell_idx = 0; cell_idx < nc; ++cell_idx) {
+ const auto& intQuants = *(ebosSimulator_.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0));
+ const auto& fs = intQuants.fluidState();
+
+ B_idx [cell_idx] = 1 / fs.invB(ebosPhaseIdx).value();
+ R2_idx[cell_idx] = ebosResid[cell_idx][ebosCompIdx];
+ }
+ }
+
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ //tempV.col(idx) = R2.col(idx).abs()/pv;
+ Vector& tempV_idx = tempV[ idx ];
+ Vector& R2_idx = R2[ idx ];
+ for( int cell_idx = 0; cell_idx < nc; ++cell_idx )
+ {
+ tempV_idx[ cell_idx ] = std::abs( R2_idx[ cell_idx ] ) / pv[ cell_idx ];
+ }
+ }
+
+ Vector pv_vector (geo_.poreVolume().data(), geo_.poreVolume().data() + geo_.poreVolume().size());
+ Vector wellResidual = wellModel().residual();
+
+ const double pvSum = convergenceReduction(grid_.comm(), global_nc_, np,
+ B, tempV, R2, pv_vector, wellResidual,
+ R_sum, maxCoeff, B_avg, maxNormWell );
+
+ Vector CNV(np);
+ Vector mass_balance_residual(np);
+ Vector well_flux_residual(np);
+
+ bool converged_MB = true;
+ bool converged_CNV = true;
+ bool converged_Well = true;
+ // Finish computation
+ for ( int idx = 0; idx < np; ++idx )
+ {
+ CNV[idx] = B_avg[idx] * dt * maxCoeff[idx];
+ mass_balance_residual[idx] = std::abs(B_avg[idx]*R_sum[idx]) * dt / pvSum;
+ converged_MB = converged_MB && (mass_balance_residual[idx] < tol_mb);
+ converged_CNV = converged_CNV && (CNV[idx] < tol_cnv);
+ // Well flux convergence is only for fluid phases, not other materials
+ // in our current implementation.
+ assert(np >= np);
+ if (idx < np) {
+ well_flux_residual[idx] = B_avg[idx] * maxNormWell[idx];
+ converged_Well = converged_Well && (well_flux_residual[idx] < tol_wells);
+ }
+ residual_norms.push_back(CNV[idx]);
+ }
+
+ const bool converged = converged_MB && converged_CNV && converged_Well;
+
+ if ( terminal_output_ )
+ {
+ // Only rank 0 does print to std::cout
+ if (iteration == 0) {
+ std::string msg = "Iter";
+
+ std::vector< std::string > key( np );
+ for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) {
+ const std::string& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx));
+ key[ phaseIdx ] = std::toupper( phaseName.front() );
+ }
+
+ for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) {
+ msg += " MB(" + key[ phaseIdx ] + ") ";
+ }
+ for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) {
+ msg += " CNV(" + key[ phaseIdx ] + ") ";
+ }
+ for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) {
+ msg += " W-FLUX(" + key[ phaseIdx ] + ")";
+ }
+ 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 idx = 0; idx < np; ++idx) {
+ ss << std::setw(11) << mass_balance_residual[idx];
+ }
+ for (int idx = 0; idx < np; ++idx) {
+ ss << std::setw(11) << CNV[idx];
+ }
+ for (int idx = 0; idx < np; ++idx) {
+ ss << std::setw(11) << well_flux_residual[idx];
+ }
+ ss.precision(oprec);
+ ss.flags(oflags);
+ OpmLog::note(ss.str());
+ }
+
+ for (int phaseIdx = 0; phaseIdx < np; ++phaseIdx) {
+ const auto& phaseName = FluidSystem::phaseName(flowPhaseToEbosPhaseIdx(phaseIdx));
+
+ if (std::isnan(mass_balance_residual[phaseIdx])
+ || std::isnan(CNV[phaseIdx])
+ || (phaseIdx < np && 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 < np && well_flux_residual[phaseIdx] > maxResidualAllowed())) {
+ OPM_THROW(Opm::NumericalProblem, "Too large residual for phase " << phaseName);
+ }
+ }
+
+ return converged;
+ }
+
+
+ /// The number of active fluid phases in the model.
+ int numPhases() const
+ {
+ return fluid_.numPhases();
+ }
+
+ std::vector >
+ computeFluidInPlace(const std::vector& fipnum) const
+ {
+ using namespace Opm::AutoDiffGrid;
+ const int nc = numCells(grid_);
+ //const ADB pv_mult = poroMult(pressure);
+ const auto& pv = geo_.poreVolume();
+ const int maxnp = Opm::BlackoilPhases::MaxNumPhases;
+
+ for (int i = 0; i<7; i++) {
+ fip_.fip[i].resize(nc,0.0);
+ }
+
+ for (int c = 0; c < nc; ++c) {
+ const auto& intQuants = *ebosSimulator_.model().cachedIntensiveQuantities(c, /*timeIdx=*/0);
+ const auto& fs = intQuants.fluidState();
+
+ for (int phase = 0; phase < maxnp; ++phase) {
+ const double& b = fs.invB(flowPhaseToEbosPhaseIdx(phase)).value();
+ const double& s = fs.saturation(flowPhaseToEbosPhaseIdx(phase)).value();
+ const double pv_mult = 1.0; //todo
+ fip_.fip[phase][c] = pv_mult * b * s * pv[c];
+ }
+
+ if (active_[ Oil ] && active_[ Gas ]) {
+ // Account for gas dissolved in oil and vaporized oil
+ fip_.fip[FIPData::FIP_DISSOLVED_GAS][c] = fs.Rs().value() * fip_.fip[FIPData::FIP_LIQUID][c];
+ fip_.fip[FIPData::FIP_VAPORIZED_OIL][c] = fs.Rv().value() * fip_.fip[FIPData::FIP_VAPOUR][c];
+ }
+ }
+
+ // For a parallel run this is just a local maximum and needs to be updated later
+ int dims = *std::max_element(fipnum.begin(), fipnum.end());
+ std::vector> values(dims, std::vector(7,0.0));
+
+ std::vector hcpv(dims, 0.0);
+ std::vector pres(dims, 0.0);
+
+ if ( !isParallel() )
+ {
+ //Accumulate phases for each region
+ for (int phase = 0; phase < maxnp; ++phase) {
+ if (active_[ phase ]) {
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1) {
+ values[region][phase] += fip_.fip[phase][c];
+ }
+ }
+ }
+ }
+
+ //Accumulate RS and RV-volumes for each region
+ if (active_[ Oil ] && active_[ Gas ]) {
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1) {
+ values[region][FIPData::FIP_DISSOLVED_GAS] += fip_.fip[FIPData::FIP_DISSOLVED_GAS][c];
+ values[region][FIPData::FIP_VAPORIZED_OIL] += fip_.fip[FIPData::FIP_VAPORIZED_OIL][c];
+ }
+ }
+ }
+
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1) {
+ const auto& intQuants = *ebosSimulator_.model().cachedIntensiveQuantities(c, /*timeIdx=*/0);
+ const auto& fs = intQuants.fluidState();
+ const double hydrocarbon = fs.saturation(FluidSystem::oilPhaseIdx).value() + fs.saturation(FluidSystem::gasPhaseIdx).value();
+ hcpv[region] += pv[c] * hydrocarbon;
+ pres[region] += pv[c] * fs.pressure(FluidSystem::oilPhaseIdx).value();
+ }
+ }
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1) {
+
+ fip_.fip[FIPData::FIP_PV][c] = pv[c];
+ const auto& intQuants = *ebosSimulator_.model().cachedIntensiveQuantities(c, /*timeIdx=*/0);
+ const auto& fs = intQuants.fluidState();
+ const double hydrocarbon = fs.saturation(FluidSystem::oilPhaseIdx).value() + fs.saturation(FluidSystem::gasPhaseIdx).value();
+
+ //Compute hydrocarbon pore volume weighted average pressure.
+ //If we have no hydrocarbon in region, use pore volume weighted average pressure instead
+ if (hcpv[region] != 0) {
+ fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c] = pv[c] * fs.pressure(FluidSystem::oilPhaseIdx).value() * hydrocarbon / hcpv[region];
+ } else {
+ fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c] = pres[region] / pv[c];
+ }
+
+ values[region][FIPData::FIP_PV] += fip_.fip[FIPData::FIP_PV][c];
+ values[region][FIPData::FIP_WEIGHTED_PRESSURE] += fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c];
+ }
+ }
+ }
+ else
+ {
+#if HAVE_MPI
+ // mask[c] is 1 if we need to compute something in parallel
+ const auto & pinfo =
+ boost::any_cast(istlSolver().parallelInformation());
+ const auto& mask = pinfo.getOwnerMask();
+ auto comm = pinfo.communicator();
+ // Compute the global dims value and resize values accordingly.
+ dims = comm.max(dims);
+ values.resize(dims, std::vector(7,0.0));
+
+ //Accumulate phases for each region
+ for (int phase = 0; phase < maxnp; ++phase) {
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1 && mask[c]) {
+ values[region][phase] += fip_.fip[phase][c];
+ }
+ }
+ }
+
+ //Accumulate RS and RV-volumes for each region
+ if (active_[ Oil ] && active_[ Gas ]) {
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1 && mask[c]) {
+ values[region][FIPData::FIP_DISSOLVED_GAS] += fip_.fip[FIPData::FIP_DISSOLVED_GAS][c];
+ values[region][FIPData::FIP_VAPORIZED_OIL] += fip_.fip[FIPData::FIP_VAPORIZED_OIL][c];
+ }
+ }
+ }
+
+ hcpv = std::vector(dims, 0.0);
+ pres = std::vector(dims, 0.0);
+
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1 && mask[c]) {
+ const auto& intQuants = *ebosSimulator_.model().cachedIntensiveQuantities(c, /*timeIdx=*/0);
+ const auto& fs = intQuants.fluidState();
+ const double hydrocarbon = fs.saturation(FluidSystem::oilPhaseIdx).value() + fs.saturation(FluidSystem::gasPhaseIdx).value();
+ hcpv[region] += pv[c] * hydrocarbon;
+ pres[region] += pv[c] * fs.pressure(FluidSystem::oilPhaseIdx).value();
+ }
+ }
+
+ comm.sum(hcpv.data(), hcpv.size());
+ comm.sum(pres.data(), pres.size());
+
+ for (int c = 0; c < nc; ++c) {
+ const int region = fipnum[c] - 1;
+ if (region != -1 && mask[c]) {
+ fip_.fip[FIPData::FIP_PV][c] = pv[c];
+ const auto& intQuants = *ebosSimulator_.model().cachedIntensiveQuantities(c, /*timeIdx=*/0);
+ const auto& fs = intQuants.fluidState();
+ const double hydrocarbon = fs.saturation(FluidSystem::oilPhaseIdx).value() + fs.saturation(FluidSystem::gasPhaseIdx).value();
+
+ if (hcpv[region] != 0) {
+ fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c] = pv[c] * fs.pressure(FluidSystem::oilPhaseIdx).value() * hydrocarbon / hcpv[region];
+ } else {
+ fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c] = pres[region] / pv[c];
+ }
+
+ values[region][FIPData::FIP_PV] += fip_.fip[FIPData::FIP_PV][c];
+ values[region][FIPData::FIP_WEIGHTED_PRESSURE] += fip_.fip[FIPData::FIP_WEIGHTED_PRESSURE][c];
+ }
+ }
+
+ // For the frankenstein branch we hopefully can turn values into a vanilla
+ // std::vector, use some index magic above, use one communication
+ // to sum up the vector entries instead of looping over the regions.
+ for(int reg=0; reg < dims; ++reg)
+ {
+ comm.sum(values[reg].data(), values[reg].size());
+ }
+#else
+ // This should never happen!
+ OPM_THROW(std::logic_error, "HAVE_MPI should be defined if we are running in parallel");
+#endif
+ }
+
+ return values;
+ }
+
+ const FIPData& getFIPData() const {
+ return fip_;
+ }
+
+
+
+ const Simulator& ebosSimulator() const
+ { return ebosSimulator_; }
+
+ protected:
+ const ISTLSolverType& istlSolver() const
+ {
+ assert( istlSolver_ );
+ return *istlSolver_;
+ }
+
+
+ // --------- Data members ---------
+
+ Simulator& ebosSimulator_;
+ const Grid& grid_;
+ const ISTLSolverType* istlSolver_;
+ const BlackoilPropsAdInterface& fluid_;
+ const DerivedGeology& geo_;
+ VFPProperties vfp_properties_;
+ // For each canonical phase -> true if active
+ const std::vector active_;
+ // Size = # active phases. Maps active -> canonical phase indices.
+ const std::vector cells_; // All grid cells
+ const bool has_disgas_;
+ const bool has_vapoil_;
+
+ ModelParameters param_;
+
+ // Well Model
+ StandardWellsDense well_model_;
+
+ /// \brief Whether we print something to std::cout
+ bool terminal_output_;
+ /// \brief The number of cells of the global grid.
+ long int global_nc_;
+
+ std::vector> residual_norms_history_;
+ double current_relaxation_;
+ BVector dx_old_;
+ mutable FIPData fip_;
+
+
+
+ // --------- Protected methods ---------
+
+ public:
+
+ /// return the StandardWells object
+ StandardWellsDense& wellModel() { return well_model_; }
+ 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(); }
+
+ /// return true if wells are available on this process
+ bool localWellsActive() const { return well_model_.localWellsActive(); }
+
+
+ void convertInput( const int iterationIdx,
+ const ReservoirState& reservoirState,
+ Simulator& simulator ) const
+ {
+ SolutionVector& solution = simulator.model().solution( 0 /* timeIdx */ );
+ const Opm::PhaseUsage pu = fluid_.phaseUsage();
+
+ const int numCells = reservoirState.numCells();
+ const int numPhases = fluid_.numPhases();
+ const auto& oilPressure = reservoirState.pressure();
+ const auto& saturations = reservoirState.saturation();
+ const auto& rs = reservoirState.gasoilratio();
+ const auto& rv = reservoirState.rv();
+ for( int cellIdx = 0; cellIdx gas only with vaporized oil in the gas) is
+ // relatively expensive as it requires to compute the capillary
+ // pressure in order to get the gas phase pressure. (the reason why
+ // ebos uses the gas pressure here is that it makes the common case
+ // of the primary variable switching code fast because to determine
+ // whether the oil phase appears one needs to compute the Rv value
+ // for the saturated gas phase and if this is not available as a
+ // primary variable, it needs to be computed.) luckily for here, the
+ // gas-only case is not too common, so the performance impact of this
+ // is limited.
+ typedef Opm::SimpleModularFluidState SatOnlyFluidState;
+ SatOnlyFluidState fluidState;
+ fluidState.setSaturation(FluidSystem::waterPhaseIdx, saturations[cellIdx*numPhases + pu.phase_pos[Water]]);
+ fluidState.setSaturation(FluidSystem::oilPhaseIdx, saturations[cellIdx*numPhases + pu.phase_pos[Oil]]);
+ fluidState.setSaturation(FluidSystem::gasPhaseIdx, saturations[cellIdx*numPhases + pu.phase_pos[Gas]]);
+
+ double pC[/*numPhases=*/3] = { 0.0, 0.0, 0.0 };
+ const MaterialLawParams& matParams = simulator.problem().materialLawParams(cellIdx);
+ MaterialLaw::capillaryPressures(pC, matParams, fluidState);
+ double pg = oilPressure[cellIdx] + (pC[FluidSystem::gasPhaseIdx] - pC[FluidSystem::oilPhaseIdx]);
+
+ cellPv[BlackoilIndices::compositionSwitchIdx] = rv[cellIdx];
+ cellPv[BlackoilIndices::pressureSwitchIdx] = pg;
+ cellPv.setPrimaryVarsMeaning( PrimaryVariables::Sw_pg_Rv );
+ }
+ else
+ {
+ assert( reservoirState.hydroCarbonState()[cellIdx] == HydroCarbonState::GasAndOil);
+ cellPv[BlackoilIndices::compositionSwitchIdx] = saturations[cellIdx*numPhases + pu.phase_pos[Gas]];
+ cellPv[BlackoilIndices::pressureSwitchIdx] = oilPressure[ cellIdx ];
+ cellPv.setPrimaryVarsMeaning( PrimaryVariables::Sw_po_Sg );
+ }
+ }
+
+ if( iterationIdx == 0 )
+ {
+ simulator.model().solution( 1 /* timeIdx */ ) = solution;
+ }
+ }
+
+ public:
+ int ebosCompToFlowPhaseIdx( const int compIdx ) const
+ {
+ const int compToPhase[ 3 ] = { Oil, Water, Gas };
+ return compToPhase[ compIdx ];
+ }
+
+ int flowToEbosPvIdx( const int flowPv ) const
+ {
+ const int flowToEbos[ 3 ] = {
+ BlackoilIndices::pressureSwitchIdx,
+ BlackoilIndices::waterSaturationIdx,
+ BlackoilIndices::compositionSwitchIdx
+ };
+ return flowToEbos[ flowPv ];
+ }
+
+ int flowPhaseToEbosCompIdx( const int phaseIdx ) const
+ {
+ const int phaseToComp[ 3 ] = { FluidSystem::waterCompIdx, FluidSystem::oilCompIdx, FluidSystem::gasCompIdx };
+ return phaseToComp[ phaseIdx ];
+ }
+
+
+
+
+ private:
+
+ void convertResults(BVector& ebosResid, Mat& ebosJac) const
+ {
+ const int numPhases = wells().number_of_phases;
+ const int numCells = ebosJac.N();
+ assert( numCells == static_cast(ebosJac.M()) );
+
+ // write the right-hand-side values from the ebosJac into the objects
+ // allocated above.
+ const auto endrow = ebosJac.end();
+ for( int cellIdx = 0; cellIdx < numCells; ++cellIdx )
+ {
+ const double cellVolume = ebosSimulator_.model().dofTotalVolume(cellIdx);
+ auto& cellRes = ebosResid[ cellIdx ];
+
+ for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
+ {
+ const double refDens = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( flowPhaseIdx ), 0 );
+ cellRes[ flowPhaseToEbosCompIdx( flowPhaseIdx ) ] /= refDens;
+ cellRes[ flowPhaseToEbosCompIdx( flowPhaseIdx ) ] *= cellVolume;
+ }
+ }
+
+ for( auto row = ebosJac.begin(); row != endrow; ++row )
+ {
+ const int rowIdx = row.index();
+ const double cellVolume = ebosSimulator_.model().dofTotalVolume(rowIdx);
+
+
+ // translate the Jacobian of the residual from the format used by ebos to
+ // the one expected by flow
+ const auto endcol = row->end();
+ for( auto col = row->begin(); col != endcol; ++col )
+ {
+ for( int flowPhaseIdx = 0; flowPhaseIdx < numPhases; ++flowPhaseIdx )
+ {
+ const double refDens = FluidSystem::referenceDensity( flowPhaseToEbosPhaseIdx( flowPhaseIdx ), 0 );
+ for( int pvIdx=0; pvIdx("deck_filename");
}
diff --git a/opm/autodiff/BlackoilModelParameters.hpp b/opm/autodiff/BlackoilModelParameters.hpp
index 2f880cc16..558dae91f 100644
--- a/opm/autodiff/BlackoilModelParameters.hpp
+++ b/opm/autodiff/BlackoilModelParameters.hpp
@@ -20,6 +20,8 @@
#ifndef OPM_BLACKOILMODELPARAMETERS_HEADER_INCLUDED
#define OPM_BLACKOILMODELPARAMETERS_HEADER_INCLUDED
+#include
+
namespace Opm
{
@@ -59,6 +61,9 @@ namespace Opm
/// Try to detect oscillation or stagnation.
bool use_update_stabilization_;
+ // The file name of the deck
+ std::string deck_file_name_;
+
/// Construct from user parameters or defaults.
explicit BlackoilModelParameters( const parameter::ParameterGroup& param );
diff --git a/opm/autodiff/DefaultBlackoilSolutionState.hpp b/opm/autodiff/DefaultBlackoilSolutionState.hpp
new file mode 100644
index 000000000..06d7a4aef
--- /dev/null
+++ b/opm/autodiff/DefaultBlackoilSolutionState.hpp
@@ -0,0 +1,57 @@
+/*
+ Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
+ Copyright 2014, 2015 Statoil ASA.
+ Copyright 2014, 2015 Dr. Markus Blatt - HPC-Simulation-Software & Services
+ Copyright 2015 NTNU
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+#ifndef OPM_DEFAULTBLACKOILSOLUTIONSTATE_HEADER_INCLUDED
+#define OPM_DEFAULTBLACKOILSOLUTIONSTATE_HEADER_INCLUDED
+
+#include
+
+namespace Opm {
+ /// Struct for containing iteration variables.
+ struct DefaultBlackoilSolutionState
+ {
+ typedef AutoDiffBlock ADB;
+ explicit DefaultBlackoilSolutionState(const int np)
+ : pressure ( ADB::null())
+ , temperature( ADB::null())
+ , saturation(np, ADB::null())
+ , rs ( ADB::null())
+ , rv ( ADB::null())
+ , qs ( ADB::null())
+ , bhp ( ADB::null())
+ , wellVariables ( ADB::null())
+ , canonical_phase_pressures(3, ADB::null())
+ {
+ }
+ ADB pressure;
+ ADB temperature;
+ std::vector saturation;
+ ADB rs;
+ ADB rv;
+ ADB qs;
+ ADB bhp;
+ ADB wellVariables;
+ // Below are quantities stored in the state for optimization purposes.
+ std::vector canonical_phase_pressures; // Always has 3 elements, even if only 2 phases active.
+ };
+} // namespace Opm
+
+#endif // OPM_DEFAULTBLACKOILSOLUTIONSTATE_HEADER_INCLUDED
diff --git a/opm/autodiff/FlowMain.hpp b/opm/autodiff/FlowMain.hpp
index a254fd033..59cc99d3d 100644
--- a/opm/autodiff/FlowMain.hpp
+++ b/opm/autodiff/FlowMain.hpp
@@ -193,6 +193,7 @@ namespace Opm
typedef BlackoilPropsAdFromDeck FluidProps;
typedef FluidProps::MaterialLawManager MaterialLawManager;
typedef typename Simulator::ReservoirState ReservoirState;
+ typedef typename Simulator::OutputWriter OutputWriter;
// ------------ Data members ------------
@@ -229,7 +230,7 @@ namespace Opm
boost::any parallel_information_;
// setupOutputWriter()
std::unique_ptr eclipse_writer_;
- std::unique_ptr output_writer_;
+ std::unique_ptr output_writer_;
// setupLinearSolver
std::unique_ptr fis_solver_;
// createSimulator()
@@ -770,12 +771,12 @@ namespace Opm
// create output writer after grid is distributed, otherwise the parallel output
// won't work correctly since we need to create a mapping from the distributed to
// the global view
- output_writer_.reset(new BlackoilOutputWriter(grid_init_->grid(),
- param_,
- *eclipse_state_,
- std::move(eclipse_writer_),
- Opm::phaseUsageFromDeck(*deck_),
- fluidprops_->permeability()));
+ output_writer_.reset(new OutputWriter(grid_init_->grid(),
+ param_,
+ *eclipse_state_,
+ std::move(eclipse_writer_),
+ Opm::phaseUsageFromDeck(*deck_),
+ fluidprops_->permeability()));
}
diff --git a/opm/autodiff/FlowMainEbos.hpp b/opm/autodiff/FlowMainEbos.hpp
new file mode 100644
index 000000000..f41fd098a
--- /dev/null
+++ b/opm/autodiff/FlowMainEbos.hpp
@@ -0,0 +1,141 @@
+/*
+ Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics.
+ Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
+ Copyright 2015 IRIS AS
+ Copyright 2014 STATOIL ASA.
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#ifndef OPM_FLOW_MAIN_EBOS_HEADER_INCLUDED
+#define OPM_FLOW_MAIN_EBOS_HEADER_INCLUDED
+
+#include
+#include
+
+#include
+
+namespace Opm
+{
+ // The FlowMain class is the ebos based black-oil simulator.
+ class FlowMainEbos : public FlowMainBase
+ {
+ protected:
+ typedef Opm::SimulatorFullyImplicitBlackoilEbos Simulator;
+ typedef FlowMainBase Base;
+ friend Base;
+
+ typedef typename TTAG(EclFlowProblem) TypeTag;
+ typedef typename GET_PROP_TYPE(TypeTag, Simulator) EbosSimulator;
+
+ // Print startup message if on output rank.
+ void printStartupMessage()
+ {
+ if (output_cout_) {
+ const int lineLen = 70;
+ const std::string version = moduleVersionName();
+ const std::string banner = "This is flow_ebos (version "+version+")";
+ const std::string ewomsVersion = "(eWoms version: " + Ewoms::versionString() + ")";
+ const int bannerPreLen = (lineLen - 2 - banner.size())/2;
+ const int bannerPostLen = bannerPreLen + (lineLen - 2 - banner.size())%2;
+ const int eVPreLen = (lineLen - 2 - ewomsVersion.size())/2;
+ const int eVPostLen = eVPreLen + (lineLen - 2 - ewomsVersion.size())%2;
+ std::cout << "**********************************************************************\n";
+ std::cout << "* *\n";
+ std::cout << "*" << std::string(bannerPreLen, ' ') << banner << std::string(bannerPostLen, ' ') << "*\n";
+ std::cout << "*" << std::string(eVPreLen, ' ') << ewomsVersion << std::string(eVPostLen, ' ') << "*\n";
+ std::cout << "* *\n";
+ std::cout << "* Flow is a simulator for fully implicit three-phase black-oil flow, *\n";
+ std::cout << "* and is part of OPM. For more information see: *\n";
+ std::cout << "* http://opm-project.org *\n";
+ std::cout << "* *\n";
+ std::cout << "**********************************************************************\n\n";
+ }
+ }
+
+ // Parser the input and creates the Deck and EclipseState objects.
+ // Writes to:
+ // deck_
+ // eclipse_state_
+ // May throw if errors are encountered, here configured to be somewhat tolerant.
+ void readDeckInput()
+ {
+ std::string progName("flow_ebos");
+ std::string deckFile("--ecl-deck-file-name=");
+ deckFile += param_.get("deck_filename");
+ char* ptr[2];
+ ptr[ 0 ] = const_cast< char * > (progName.c_str());
+ ptr[ 1 ] = const_cast< char * > (deckFile.c_str());
+ EbosSimulator::registerParameters();
+ Ewoms::setupParameters_< TypeTag > ( 2, ptr );
+ ebosSimulator_.reset(new EbosSimulator(/*verbose=*/false));
+ ebosSimulator_->model().applyInitialSolution();
+
+ Base::deck_ = ebosSimulator_->gridManager().deck();
+ Base::eclipse_state_ = ebosSimulator_->gridManager().eclState();
+ IOConfig& ioConfig = Base::eclipse_state_->getIOConfig();
+ ioConfig.setOutputDir(Base::output_dir_);
+
+ // Possibly override IOConfig setting (from deck) for how often RESTART files should get written to disk (every N report step)
+ if (Base::param_.has("output_interval")) {
+ const int output_interval = Base::param_.get("output_interval");
+ eclipse_state_->getRestartConfig().overrideRestartWriteInterval( size_t( output_interval ) );
+
+ }
+
+ // Possible to force initialization only behavior (NOSIM).
+ if (Base::param_.has("nosim")) {
+ const bool nosim = Base::param_.get("nosim");
+ ioConfig.overrideNOSIM( nosim );
+ }
+ }
+
+ // Setup linear solver.
+ // Writes to:
+ // fis_solver_
+ void setupLinearSolver()
+ {
+ typedef typename BlackoilModelEbos :: ISTLSolverType ISTLSolverType;
+ Base::fis_solver_.reset( new ISTLSolverType( Base::param_, Base::parallel_information_ ) );
+ }
+
+ /// This is the main function of Flow.
+ // Create simulator instance.
+ // Writes to:
+ // simulator_
+ void createSimulator()
+ {
+ // Create the simulator instance.
+ Base::simulator_.reset(new Simulator(*ebosSimulator_,
+ Base::param_,
+ *Base::geoprops_,
+ *Base::fluidprops_,
+ Base::rock_comp_->isActive() ? Base::rock_comp_.get() : nullptr,
+ *Base::fis_solver_,
+ Base::gravity_.data(),
+ Base::deck_->hasKeyword("DISGAS"),
+ Base::deck_->hasKeyword("VAPOIL"),
+ Base::eclipse_state_,
+ *Base::output_writer_,
+ Base::threshold_pressures_));
+ }
+
+ private:
+ std::unique_ptr ebosSimulator_;
+ };
+} // namespace Opm
+
+#endif // OPM_FLOW_MAIN_EBOS_HEADER_INCLUDED
diff --git a/opm/autodiff/ISTLSolver.hpp b/opm/autodiff/ISTLSolver.hpp
new file mode 100644
index 000000000..a5132c4a1
--- /dev/null
+++ b/opm/autodiff/ISTLSolver.hpp
@@ -0,0 +1,413 @@
+/*
+ Copyright 2016 IRIS AS
+
+ This file is part of the Open Porous Media project (OPM).
+
+ OPM is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OPM is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OPM. If not, see .
+*/
+
+#ifndef OPM_ISTLSOLVER_HEADER_INCLUDED
+#define OPM_ISTLSOLVER_HEADER_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace Dune
+{
+
+namespace ISTLUtility {
+
+//! invert matrix by calling FMatrixHelp::invert
+template
+static inline void invertMatrix (FieldMatrix &matrix)
+{
+ FieldMatrix A ( matrix );
+ FMatrixHelp::invertMatrix(A, matrix );
+}
+
+//! invert matrix by calling FMatrixHelp::invert
+template
+static inline void invertMatrix (FieldMatrix &matrix)
+{
+ FieldMatrix A ( matrix );
+ FMatrixHelp::invertMatrix(A, matrix );
+}
+
+//! invert matrix by calling FMatrixHelp::invert
+template
+static inline void invertMatrix (FieldMatrix &matrix)
+{
+ FieldMatrix A ( matrix );
+ FMatrixHelp::invertMatrix(A, matrix );
+}
+
+//! invert matrix by calling matrix.invert
+template
+static inline void invertMatrix (FieldMatrix &matrix)
+{
+ matrix.invert();
+}
+
+} // end ISTLUtility
+
+template
+class MatrixBlock : public Dune::FieldMatrix
+{
+public:
+ typedef Dune::FieldMatrix BaseType;
+
+ using BaseType :: operator= ;
+ using BaseType :: rows;
+ using BaseType :: cols;
+ explicit MatrixBlock( const Scalar scalar = 0 ) : BaseType( scalar ) {}
+ void invert()
+ {
+ ISTLUtility::invertMatrix( *this );
+ }
+ const BaseType& asBase() const { return static_cast< const BaseType& > (*this); }
+ BaseType& asBase() { return static_cast< BaseType& > (*this); }
+};
+
+template
+void
+print_row (std::ostream& s, const MatrixBlock& A,
+ typename FieldMatrix::size_type I,
+ typename FieldMatrix::size_type J,
+ typename FieldMatrix::size_type therow, int width,
+ int precision)
+{
+ print_row(s, A.asBase(), I, J, therow, width, precision);
+}
+
+template
+K& firstmatrixelement (MatrixBlock& A)
+{
+ return firstmatrixelement( A.asBase() );
+}
+
+
+
+template
+struct MatrixDimension< MatrixBlock< Scalar, n, m > >
+: public MatrixDimension< typename MatrixBlock< Scalar, n, m >::BaseType >
+{
+};
+
+} // end namespace Dune
+
+namespace Opm
+{
+ /// This class solves the fully implicit black-oil system by
+ /// solving the reduced system (after eliminating well variables)
+ /// as a block-structured matrix (one block for all cell variables) for a fixed
+ /// number of cell variables np .
+ template < class MatrixBlockType, class VectorBlockType >
+ class ISTLSolver : public NewtonIterationBlackoilInterface
+ {
+ typedef typename MatrixBlockType :: field_type Scalar;
+
+ typedef Dune::BCRSMatrix Matrix;
+ typedef Dune::BlockVector Vector;
+
+ public:
+ typedef Dune::AssembledLinearOperator< Matrix, Vector, Vector > AssembledLinearOperatorType;
+
+ typedef NewtonIterationBlackoilInterface :: SolutionVector SolutionVector;
+ /// Construct a system solver.
+ /// \param[in] param parameters controlling the behaviour of the linear solvers
+ /// \param[in] parallelInformation In the case of a parallel run
+ /// with dune-istl the information about the parallelization.
+ ISTLSolver(const NewtonIterationBlackoilInterleavedParameters& param,
+ const boost::any& parallelInformation_arg=boost::any())
+ : iterations_( 0 ),
+ parallelInformation_(parallelInformation_arg),
+ isIORank_(isIORank(parallelInformation_arg)),
+ parameters_( param )
+ {
+ }
+
+ /// Construct a system solver.
+ /// \param[in] param ParameterGroup controlling the behaviour of the linear solvers
+ /// \param[in] parallelInformation In the case of a parallel run
+ /// with dune-istl the information about the parallelization.
+ ISTLSolver(const parameter::ParameterGroup& param,
+ const boost::any& parallelInformation_arg=boost::any())
+ : iterations_( 0 ),
+ parallelInformation_(parallelInformation_arg),
+ isIORank_(isIORank(parallelInformation_arg)),
+ parameters_( param )
+ {
+ }
+
+ // dummy method that is not implemented for this class
+ SolutionVector computeNewtonIncrement(const LinearisedBlackoilResidual& residual) const
+ {
+ OPM_THROW(std::logic_error,"This method is not implemented");
+ return SolutionVector();
+ }
+
+ /// Solve the system of linear equations Ax = b, with A being the
+ /// combined derivative matrix of the residual and b
+ /// being the residual itself.
+ /// \param[in] residual residual object containing A and b.
+ /// \return the solution x
+
+ /// \copydoc NewtonIterationBlackoilInterface::iterations
+ int iterations () const { return iterations_; }
+
+ /// \copydoc NewtonIterationBlackoilInterface::parallelInformation
+ const boost::any& parallelInformation() const { return parallelInformation_; }
+
+ public:
+ /// \brief construct the CPR preconditioner and the solver.
+ /// \tparam P The type of the parallel information.
+ /// \param parallelInformation the information about the parallelization.
+ template
+ void constructPreconditionerAndSolve(LinearOperator& linearOperator,
+ Vector& x, Vector& istlb,
+ const POrComm& parallelInformation_arg,
+ Dune::InverseOperatorResult& result) const
+ {
+ // Construct scalar product.
+ typedef Dune::ScalarProductChooser ScalarProductChooser;
+ typedef std::unique_ptr SPPointer;
+ SPPointer sp(ScalarProductChooser::construct(parallelInformation_arg));
+
+ // Communicate if parallel.
+ parallelInformation_arg.copyOwnerToAll(istlb, istlb);
+
+#if ! HAVE_UMFPACK
+ const bool useAmg = false ;
+ if( useAmg )
+ {
+ typedef ISTLUtility::CPRSelector< Matrix, Vector, Vector, POrComm> CPRSelectorType;
+ typedef typename CPRSelectorType::AMG AMG;
+ typedef typename CPRSelectorType::Operator MatrixOperator;
+
+ std::unique_ptr< AMG > amg;
+ std::unique_ptr< MatrixOperator > opA;
+
+ if( ! std::is_same< LinearOperator, MatrixOperator > :: value )
+ {
+ // create new operator in case linear operator and matrix operator differ
+ opA.reset( CPRSelectorType::makeOperator( linearOperator.getmat(), parallelInformation_arg ) );
+ }
+
+ const double relax = 1.0;
+
+ // Construct preconditioner.
+ constructAMGPrecond( linearOperator, parallelInformation_arg, amg, opA, relax );
+
+ // Solve.
+ solve(linearOperator, x, istlb, *sp, *amg, result);
+ }
+ else
+#endif
+ {
+ // Construct preconditioner.
+ auto precond = constructPrecond(linearOperator, parallelInformation_arg);
+
+ // Solve.
+ solve(linearOperator, x, istlb, *sp, *precond, result);
+ }
+ }
+
+ typedef Dune::SeqILU0 SeqPreconditioner;
+
+ template
+ std::unique_ptr constructPrecond(Operator& opA, const Dune::Amg::SequentialInformation&) const
+ {
+ const double relax = 0.9;
+ std::unique_ptr precond(new SeqPreconditioner(opA.getmat(), relax));
+ return precond;
+ }
+
+#if HAVE_MPI
+ typedef Dune::OwnerOverlapCopyCommunication Comm;
+ typedef ParallelOverlappingILU0 ParPreconditioner;
+ template
+ std::unique_ptr
+ constructPrecond(Operator& opA, const Comm& comm) const
+ {
+ typedef std::unique_ptr Pointer;
+ const double relax = 0.9;
+ return Pointer(new ParPreconditioner(opA.getmat(), comm, relax));
+ }
+#endif
+
+ template
+ void
+ constructAMGPrecond(LinearOperator& linearOperator, const POrComm& comm, std::unique_ptr< AMG >& amg, std::unique_ptr< MatrixOperator >& opA, const double relax ) const
+ {
+ ISTLUtility::createAMGPreconditionerPointer( *opA, relax, comm, amg );
+ }
+
+
+ template
+ void
+ constructAMGPrecond(MatrixOperator& opA, const POrComm& comm, std::unique_ptr< AMG >& amg, std::unique_ptr< MatrixOperator >&, const double relax ) const
+ {
+ ISTLUtility::createAMGPreconditionerPointer( opA, relax, comm, amg );
+ }
+
+ /// \brief Solve the system using the given preconditioner and scalar product.
+ template
+ void solve(Operator& opA, Vector& x, Vector& istlb, ScalarProd& sp, Precond& precond, Dune::InverseOperatorResult& result) const
+ {
+ // TODO: Revise when linear solvers interface opm-core is done
+ // Construct linear solver.
+ // GMRes solver
+ if ( parameters_.newton_use_gmres_ ) {
+ Dune::RestartedGMResSolver linsolve(opA, sp, precond,
+ parameters_.linear_solver_reduction_,
+ parameters_.linear_solver_restart_,
+ parameters_.linear_solver_maxiter_,
+ parameters_.linear_solver_verbosity_);
+ // Solve system.
+ linsolve.apply(x, istlb, result);
+ }
+ else { // BiCGstab solver
+ Dune::BiCGSTABSolver linsolve(opA, sp, precond,
+ parameters_.linear_solver_reduction_,
+ parameters_.linear_solver_maxiter_,
+ parameters_.linear_solver_verbosity_);
+ // Solve system.
+ linsolve.apply(x, istlb, result);
+ }
+ }
+
+
+ /// Solve the linear system Ax = b, with A being the
+ /// combined derivative matrix of the residual and b
+ /// being the residual itself.
+ /// \param[in] A matrix A
+ /// \param[inout] x solution to be computed x
+ /// \param[in] b right hand side b
+ void solve(Matrix& A, Vector& x, Vector& b ) const
+ {
+ // Parallel version is deactivated until we figure out how to do it properly.
+#if HAVE_MPI
+ if (parallelInformation_.type() == typeid(ParallelISTLInformation))
+ {
+ typedef Dune::OwnerOverlapCopyCommunication