diff --git a/CMakeLists.txt b/CMakeLists.txt
index ee87f128b..8af981a26 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -90,6 +90,7 @@ macro (sources_hook)
if(DUNE_CORNERPOINT_FOUND OR dune-cornerpoint_FOUND)
list (APPEND examples_SOURCES
${PROJECT_SOURCE_DIR}/examples/flow_mpi.cpp
+ ${PROJECT_SOURCE_DIR}/examples/flow_multisegment_mpi.cpp
)
endif()
endmacro (sources_hook)
diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index 0a48770a3..5f96fcc35 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -46,6 +46,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/autodiff/VFPProperties.cpp
opm/autodiff/VFPProdProperties.cpp
opm/autodiff/VFPInjProperties.cpp
+ opm/autodiff/WellMultiSegment.cpp
)
@@ -77,6 +78,7 @@ list (APPEND TEST_DATA_FILES
list (APPEND EXAMPLE_SOURCE_FILES
examples/find_zero.cpp
examples/flow.cpp
+ examples/flow_multisegment.cpp
examples/flow_solvent.cpp
examples/sim_2p_incomp_ad.cpp
examples/sim_simple.cpp
@@ -114,6 +116,8 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/BlackoilSolventModel.hpp
opm/autodiff/BlackoilSolventModel_impl.hpp
opm/autodiff/BlackoilSolventState.hpp
+ opm/autodiff/BlackoilMultiSegmentModel.hpp
+ opm/autodiff/BlackoilMultiSegmentModel_impl.hpp
opm/autodiff/fastSparseProduct.hpp
opm/autodiff/DuneMatrix.hpp
opm/autodiff/ExtractParallelGridInformationToISTL.hpp
@@ -139,6 +143,8 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/SimulatorFullyImplicitBlackoil.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilSolvent.hpp
opm/autodiff/SimulatorFullyImplicitBlackoilSolvent_impl.hpp
+ opm/autodiff/SimulatorFullyImplicitBlackoilMultiSegment.hpp
+ opm/autodiff/SimulatorFullyImplicitBlackoilMultiSegment_impl.hpp
opm/autodiff/SimulatorIncompTwophaseAd.hpp
opm/autodiff/TransportSolverTwophaseAd.hpp
opm/autodiff/WellDensitySegmented.hpp
@@ -149,5 +155,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/VFPHelpers.hpp
opm/autodiff/VFPProdProperties.hpp
opm/autodiff/VFPInjProperties.hpp
+ opm/autodiff/WellStateMultiSegment.hpp
+ opm/autodiff/WellMultiSegment.hpp
)
diff --git a/examples/flow_multisegment.cpp b/examples/flow_multisegment.cpp
new file mode 100644
index 000000000..30b9247af
--- /dev/null
+++ b/examples/flow_multisegment.cpp
@@ -0,0 +1,463 @@
+/*
+ Copyright 2013 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
+
+#if DUNE_VERSION_NEWER(DUNE_COMMON, 2, 3)
+#include
+#else
+#include
+#endif
+
+#if HAVE_DUNE_CORNERPOINT && WANT_DUNE_CORNERPOINTGRID
+#define USE_DUNE_CORNERPOINTGRID 1
+#include
+#include
+#else
+#undef USE_DUNE_CORNERPOINTGRID
+#endif
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include // Note: the GridHelpers must be included before this (to make overloads available). \TODO: Fix.
+
+#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
+
+#ifdef _OPENMP
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+ void warnIfUnusedParams(const Opm::parameter::ParameterGroup& param)
+ {
+ if (param.anyUnused()) {
+ std::cout << "-------------------- Unused parameters: --------------------\n";
+ param.displayUsage();
+ std::cout << "----------------------------------------------------------------" << std::endl;
+ }
+ }
+} // anon namespace
+
+
+
+// ----------------- Main program -----------------
+int
+main(int argc, char** argv)
+try
+{
+ using namespace Opm;
+#if USE_DUNE_CORNERPOINTGRID
+ // Must ensure an instance of the helper is created to initialise MPI.
+ const Dune::MPIHelper& mpi_helper = Dune::MPIHelper::instance(argc, argv);
+ const int mpi_rank = mpi_helper.rank();
+ const int mpi_size = mpi_helper.size();
+#else
+ // default values for serial run
+ const int mpi_rank = 0;
+ const int mpi_size = 1;
+#endif
+
+ // Write parameters used for later reference. (only if rank is zero)
+ const bool output_cout = ( mpi_rank == 0 );
+
+ if (output_cout)
+ {
+ std::string version = moduleVersionName();
+ std::cout << "**********************************************************************\n";
+ std::cout << "* *\n";
+ std::cout << "* This is Flow (version " << version << ")"
+ << std::string(26 - version.size(), ' ') << "*\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";
+ }
+
+#ifdef _OPENMP
+ if (!getenv("OMP_NUM_THREADS")) {
+ //Default to at most 4 threads, regardless of
+ //number of cores (unless ENV(OMP_NUM_THREADS) is defined)
+ int num_cores = omp_get_num_procs();
+ int num_threads = std::min(4, num_cores);
+ omp_set_num_threads(num_threads);
+ }
+#pragma omp parallel
+ if (omp_get_thread_num() == 0){
+ //opm_get_num_threads() only works as expected within a parallel region.
+ std::cout << "OpenMP using " << omp_get_num_threads() << " threads." << std::endl;
+ }
+#endif
+
+ // Read parameters, see if a deck was specified on the command line.
+ if ( output_cout )
+ {
+ std::cout << "--------------- Reading parameters ---------------" << std::endl;
+ }
+
+ parameter::ParameterGroup param(argc, argv, false, output_cout);
+ if( !output_cout )
+ {
+ param.disableOutput();
+ }
+
+ if (!param.unhandledArguments().empty()) {
+ if (param.unhandledArguments().size() != 1) {
+ std::cerr << "You can only specify a single input deck on the command line.\n";
+ return EXIT_FAILURE;
+ } else {
+ param.insertParameter("deck_filename", param.unhandledArguments()[0]);
+ }
+ }
+
+ // We must have an input deck. Grid and props will be read from that.
+ if (!param.has("deck_filename")) {
+ std::cerr << "This program must be run with an input deck.\n"
+ "Specify the deck filename either\n"
+ " a) as a command line argument by itself\n"
+ " b) as a command line parameter with the syntax deck_filename=, or\n"
+ " c) as a parameter in a parameter file (.param or .xml) passed to the program.\n";
+ return EXIT_FAILURE;
+ }
+
+ // bool check_well_controls = false;
+ // int max_well_control_iterations = 0;
+ double gravity[3] = { 0.0 };
+ std::string deck_filename = param.get("deck_filename");
+
+ // Write parameters used for later reference. (only if rank is zero)
+ bool output = ( mpi_rank == 0 ) && param.getDefault("output", true);
+ std::string output_dir;
+ if (output) {
+ // Create output directory if needed.
+ output_dir =
+ param.getDefault("output_dir", std::string("output"));
+ boost::filesystem::path fpath(output_dir);
+ try {
+ create_directories(fpath);
+ }
+ catch (...) {
+ std::cerr << "Creating directories failed: " << fpath << std::endl;
+ return EXIT_FAILURE;
+ }
+ // Write simulation parameters.
+ param.writeParam(output_dir + "/simulation.param");
+ }
+
+ std::string logFile = output_dir + "/LOGFILE.txt";
+ Opm::ParserPtr parser(new Opm::Parser());
+ {
+ std::shared_ptr streamLog = std::make_shared(logFile , Opm::Log::DefaultMessageTypes);
+ std::shared_ptr counterLog = std::make_shared(Opm::Log::DefaultMessageTypes);
+
+ Opm::OpmLog::addBackend( "STREAM" , streamLog );
+ Opm::OpmLog::addBackend( "COUNTER" , counterLog );
+ }
+
+ Opm::ParseMode parseMode({{ ParseMode::PARSE_RANDOM_SLASH , InputError::IGNORE }});
+ Opm::DeckConstPtr deck;
+ std::shared_ptr eclipseState;
+ try {
+ deck = parser->parseFile(deck_filename, parseMode);
+ Opm::checkDeck(deck);
+ eclipseState.reset(new Opm::EclipseState(deck , parseMode));
+ }
+ catch (const std::invalid_argument& e) {
+ std::cerr << "Failed to create valid ECLIPSESTATE object. See logfile: " << logFile << std::endl;
+ std::cerr << "Exception caught: " << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::vector porv = eclipseState->getDoubleGridProperty("PORV")->getData();
+#if USE_DUNE_CORNERPOINTGRID
+ // Dune::CpGrid as grid manager
+ typedef Dune::CpGrid Grid;
+ // Grid init
+ Grid grid;
+ grid.processEclipseFormat(deck, false, false, false, porv);
+#else
+ // UnstructuredGrid as grid manager
+ typedef UnstructuredGrid Grid;
+ GridManager gridManager( eclipseState->getEclipseGrid(), porv );
+ const Grid& grid = *(gridManager.c_grid());
+#endif
+
+ // Possibly override IOConfig setting (from deck) for how often RESTART files should get written to disk (every N report step)
+ if (param.has("output_interval")) {
+ int output_interval = param.get("output_interval");
+ IOConfigPtr ioConfig = eclipseState->getIOConfig();
+ ioConfig->overrideRestartWriteInterval((size_t)output_interval);
+ }
+
+ const PhaseUsage pu = Opm::phaseUsageFromDeck(deck);
+
+ std::vector compressedToCartesianIdx;
+ Opm::createGlobalCellArray(grid, compressedToCartesianIdx);
+
+ typedef BlackoilPropsAdFromDeck::MaterialLawManager MaterialLawManager;
+ auto materialLawManager = std::make_shared();
+ materialLawManager->initFromDeck(deck, eclipseState, compressedToCartesianIdx);
+
+ // Rock and fluid init
+ BlackoilPropertiesFromDeck props( deck, eclipseState, materialLawManager,
+ Opm::UgGridHelpers::numCells(grid),
+ Opm::UgGridHelpers::globalCell(grid),
+ Opm::UgGridHelpers::cartDims(grid),
+ param);
+
+ BlackoilPropsAdFromDeck new_props( deck, eclipseState, materialLawManager, grid );
+ // check_well_controls = param.getDefault("check_well_controls", false);
+ // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10);
+ // Rock compressibility.
+
+ RockCompressibility rock_comp(deck, eclipseState);
+
+ // Gravity.
+ gravity[2] = deck->hasKeyword("NOGRAV") ? 0.0 : unit::gravity;
+
+ BlackoilState state;
+ // Init state variables (saturation and pressure).
+ if (param.has("init_saturation")) {
+ initStateBasic(Opm::UgGridHelpers::numCells(grid),
+ Opm::UgGridHelpers::globalCell(grid),
+ Opm::UgGridHelpers::cartDims(grid),
+ Opm::UgGridHelpers::numFaces(grid),
+ Opm::UgGridHelpers::faceCells(grid),
+ Opm::UgGridHelpers::beginFaceCentroids(grid),
+ Opm::UgGridHelpers::beginCellCentroids(grid),
+ Opm::UgGridHelpers::dimensions(grid),
+ props, param, gravity[2], state);
+
+ initBlackoilSurfvol(Opm::UgGridHelpers::numCells(grid), props, state);
+
+ enum { Oil = BlackoilPhases::Liquid, Gas = BlackoilPhases::Vapour };
+ if (pu.phase_used[Oil] && pu.phase_used[Gas]) {
+ const int numPhases = props.numPhases();
+ const int numCells = Opm::UgGridHelpers::numCells(grid);
+ for (int c = 0; c < numCells; ++c) {
+ state.gasoilratio()[c] = state.surfacevol()[c*numPhases + pu.phase_pos[Gas]]
+ / state.surfacevol()[c*numPhases + pu.phase_pos[Oil]];
+ }
+ }
+ } else if (deck->hasKeyword("EQUIL") && props.numPhases() == 3) {
+ state.init(Opm::UgGridHelpers::numCells(grid),
+ Opm::UgGridHelpers::numFaces(grid),
+ props.numPhases());
+ const double grav = param.getDefault("gravity", unit::gravity);
+ initStateEquil(grid, props, deck, eclipseState, grav, state);
+ state.faceflux().resize(Opm::UgGridHelpers::numFaces(grid), 0.0);
+ } else {
+ initBlackoilStateFromDeck(Opm::UgGridHelpers::numCells(grid),
+ Opm::UgGridHelpers::globalCell(grid),
+ Opm::UgGridHelpers::numFaces(grid),
+ Opm::UgGridHelpers::faceCells(grid),
+ Opm::UgGridHelpers::beginFaceCentroids(grid),
+ Opm::UgGridHelpers::beginCellCentroids(grid),
+ Opm::UgGridHelpers::dimensions(grid),
+ props, deck, gravity[2], state);
+ }
+
+
+ // The capillary pressure is scaled in new_props to match the scaled capillary pressure in props.
+ if (deck->hasKeyword("SWATINIT")) {
+ const int numCells = Opm::UgGridHelpers::numCells(grid);
+ std::vector cells(numCells);
+ for (int c = 0; c < numCells; ++c) { cells[c] = c; }
+ std::vector pc = state.saturation();
+ props.capPress(numCells, state.saturation().data(), cells.data(), pc.data(),NULL);
+ new_props.setSwatInitScaling(state.saturation(),pc);
+ }
+
+ bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0);
+ const double *grav = use_gravity ? &gravity[0] : 0;
+
+ const bool use_local_perm = param.getDefault("use_local_perm", true);
+
+ DerivedGeology geoprops(grid, new_props, eclipseState, use_local_perm, grav);
+ boost::any parallel_information;
+
+ // At this point all properties and state variables are correctly initialized
+ // If there are more than one processors involved, we now repartition the grid
+ // and initilialize new properties and states for it.
+ if( mpi_size > 1 )
+ {
+ Opm::distributeGridAndData( grid, deck, eclipseState, state, new_props, geoprops, materialLawManager, parallel_information, use_local_perm );
+ }
+
+ // 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
+ Opm::BlackoilOutputWriter outputWriter(grid, param, eclipseState, pu, new_props.permeability() );
+
+ // Solver for Newton iterations.
+ std::unique_ptr fis_solver;
+ {
+ const std::string cprSolver = "cpr";
+ const std::string interleavedSolver = "interleaved";
+ const std::string directSolver = "direct";
+ const std::string flowDefaultSolver = interleavedSolver;
+
+ std::shared_ptr simCfg = eclipseState->getSimulationConfig();
+ std::string solver_approach = flowDefaultSolver;
+
+ if (param.has("solver_approach")) {
+ solver_approach = param.get("solver_approach");
+ } else {
+ if (simCfg->useCPR()) {
+ solver_approach = cprSolver;
+ }
+ }
+
+ if (solver_approach == cprSolver) {
+ fis_solver.reset(new NewtonIterationBlackoilCPR(param, parallel_information));
+ } else if (solver_approach == interleavedSolver) {
+ fis_solver.reset(new NewtonIterationBlackoilInterleaved(param, parallel_information));
+ } else if (solver_approach == directSolver) {
+ fis_solver.reset(new NewtonIterationBlackoilSimple(param, parallel_information));
+ } else {
+ OPM_THROW( std::runtime_error , "Internal error - solver approach " << solver_approach << " not recognized.");
+ }
+
+ }
+
+ Opm::ScheduleConstPtr schedule = eclipseState->getSchedule();
+ Opm::TimeMapConstPtr timeMap(schedule->getTimeMap());
+ SimulatorTimer simtimer;
+
+ // initialize variables
+ simtimer.init(timeMap);
+
+ std::map, double> maxDp;
+ computeMaxDp(maxDp, deck, eclipseState, grid, state, props, gravity[2]);
+ std::vector threshold_pressures = thresholdPressures(deck, eclipseState, grid, maxDp);
+
+ SimulatorFullyImplicitBlackoilMultiSegment< Grid > simulator(param,
+ grid,
+ geoprops,
+ new_props,
+ rock_comp.isActive() ? &rock_comp : 0,
+ *fis_solver,
+ grav,
+ deck->hasKeyword("DISGAS"),
+ deck->hasKeyword("VAPOIL"),
+ eclipseState,
+ outputWriter,
+ threshold_pressures);
+
+ if (!schedule->initOnly()){
+ if( output_cout )
+ {
+ std::cout << "\n\n================ Starting main simulation loop ===============\n"
+ << std::flush;
+ }
+
+ SimulatorReport fullReport = simulator.run(simtimer, state);
+
+ if( output_cout )
+ {
+ std::cout << "\n\n================ End of simulation ===============\n\n";
+ fullReport.reportFullyImplicit(std::cout);
+ }
+
+ if (output) {
+ std::string filename = output_dir + "/walltime.txt";
+ std::fstream tot_os(filename.c_str(),std::fstream::trunc | std::fstream::out);
+ fullReport.reportParam(tot_os);
+ warnIfUnusedParams(param);
+ }
+ } else {
+ outputWriter.writeInit( simtimer );
+ if ( output_cout )
+ {
+ std::cout << "\n\n================ Simulation turned off ===============\n" << std::flush;
+ }
+ }
+}
+catch (const std::exception &e) {
+ std::cerr << "Program threw an exception: " << e.what() << "\n";
+ return EXIT_FAILURE;
+}
+
diff --git a/examples/flow_multisegment_mpi.cpp b/examples/flow_multisegment_mpi.cpp
new file mode 100644
index 000000000..b54903ef6
--- /dev/null
+++ b/examples/flow_multisegment_mpi.cpp
@@ -0,0 +1,2 @@
+#define WANT_DUNE_CORNERPOINTGRID 1
+#include "flow_multisegment.cpp"
diff --git a/opm/autodiff/BlackoilModelBase.hpp b/opm/autodiff/BlackoilModelBase.hpp
index 61d32942d..ec18441ac 100644
--- a/opm/autodiff/BlackoilModelBase.hpp
+++ b/opm/autodiff/BlackoilModelBase.hpp
@@ -273,6 +273,7 @@ namespace Opm {
WellOps(const Wells* wells);
Eigen::SparseMatrix w2p; // well -> perf (scatter)
Eigen::SparseMatrix p2w; // perf -> well (gather)
+ std::vector well_cells; // the set of perforated cells
};
// --------- Data members ---------
@@ -344,6 +345,8 @@ namespace Opm {
// return wells object
const Wells& wells () const { assert( bool(wells_ != 0) ); return *wells_; }
+ int numWellVars() const;
+
void
makeConstantState(SolutionState& state) const;
@@ -388,6 +391,10 @@ namespace Opm {
assembleMassBalanceEq(const SolutionState& state);
void
+ extractWellPerfProperties(std::vector& mob_perfcells,
+ std::vector& b_perfcells) const;
+
+ bool
solveWellEq(const std::vector& mob_perfcells,
const std::vector& b_perfcells,
SolutionState& state,
@@ -398,12 +405,12 @@ namespace Opm {
const std::vector& mob_perfcells,
const std::vector& b_perfcells,
V& aliveWells,
- std::vector& cq_s);
+ std::vector& cq_s) const;
void
updatePerfPhaseRatesAndPressures(const std::vector& cq_s,
const SolutionState& state,
- WellState& xw);
+ WellState& xw) const;
void
addWellFluxEq(const std::vector& cq_s,
@@ -534,9 +541,8 @@ namespace Opm {
/// 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 equations for each phase.
+ /// \param[out] maxNormWell The maximum of the well flux equations for each phase.
/// \param[in] nc The number of cells of the local grid.
- /// \param[in] nw The number of wells on the local grid.
/// \return The total pore volume over all cells.
double
convergenceReduction(const Eigen::Array& B,
@@ -546,8 +552,7 @@ namespace Opm {
std::vector& maxCoeff,
std::vector& B_avg,
std::vector& maxNormWell,
- int nc,
- int nw) const;
+ int nc) const;
double dpMaxRel() const { return param_.dp_max_rel_; }
double dsMax() const { return param_.ds_max_; }
diff --git a/opm/autodiff/BlackoilModelBase_impl.hpp b/opm/autodiff/BlackoilModelBase_impl.hpp
index f53fe75cf..f1e82ff70 100644
--- a/opm/autodiff/BlackoilModelBase_impl.hpp
+++ b/opm/autodiff/BlackoilModelBase_impl.hpp
@@ -407,7 +407,8 @@ namespace detail {
BlackoilModelBase::
WellOps::WellOps(const Wells* wells)
: w2p(),
- p2w()
+ p2w(),
+ well_cells()
{
if( wells )
{
@@ -432,6 +433,8 @@ namespace detail {
w2p.setFromTriplets(scatter.begin(), scatter.end());
p2w.setFromTriplets(gather .begin(), gather .end());
+
+ well_cells.assign(wells->well_cells, wells->well_cells + wells->well_connpos[wells->number_of_wells]);
}
}
@@ -439,6 +442,19 @@ namespace detail {
+ template
+ int
+ BlackoilModelBase::numWellVars() const
+ {
+ // For each well, we have a bhp variable, and one flux per phase.
+ const int nw = localWellsActive() ? wells().number_of_wells : 0;
+ return (numPhases() + 1) * nw;
+ }
+
+
+
+
+
template
void
BlackoilModelBase::makeConstantState(SolutionState& state) const
@@ -496,7 +512,7 @@ namespace detail {
// and bhp and Q for the wells
vars0.reserve(np + 1);
variableReservoirStateInitials(x, vars0);
- variableWellStateInitials(xw, vars0);
+ asImpl().variableWellStateInitials(xw, vars0);
return vars0;
}
@@ -681,7 +697,7 @@ namespace detail {
}
}
// wells
- variableStateExtractWellsVars(indices, vars, state);
+ asImpl().variableStateExtractWellsVars(indices, vars, state);
return state;
}
@@ -778,7 +794,6 @@ namespace detail {
// taking std::vector arguments, and not Eigen objects.
const int nperf = wells().well_connpos[wells().number_of_wells];
const int nw = wells().number_of_wells;
- const std::vector well_cells(wells().well_cells, wells().well_cells + nperf);
// Compute the average pressure in each well block
const V perf_press = Eigen::Map(xw.perfPress().data(), nperf);
@@ -791,6 +806,8 @@ namespace detail {
}
}
+ const std::vector& well_cells = wops_.well_cells;
+
// Use cell values for the temperature as the wells don't knows its temperature yet.
const ADB perf_temp = subset(state.temperature, well_cells);
@@ -882,12 +899,12 @@ namespace detail {
SolutionState state = asImpl().variableState(reservoir_state, well_state);
SolutionState state0 = state;
asImpl().makeConstantState(state0);
- computeWellConnectionPressures(state0, well_state);
+ asImpl().computeWellConnectionPressures(state0, well_state);
}
// Possibly switch well controls and updating well state to
// get reasonable initial conditions for the wells
- updateWellControls(well_state);
+ asImpl().updateWellControls(well_state);
// Create the primary variables.
SolutionState state = asImpl().variableState(reservoir_state, well_state);
@@ -899,7 +916,7 @@ namespace detail {
// Compute initial accumulation contributions
// and well connection pressures.
asImpl().computeAccum(state0, 0);
- computeWellConnectionPressures(state0, well_state);
+ asImpl().computeWellConnectionPressures(state0, well_state);
}
// OPM_AD_DISKVAL(state.pressure);
@@ -920,30 +937,20 @@ namespace detail {
return;
}
- V aliveWells;
- const int np = wells().number_of_phases;
- std::vector cq_s(np, ADB::null());
-
- const int nw = wells().number_of_wells;
- const int nperf = wells().well_connpos[nw];
- const std::vector well_cells(wells().well_cells, wells().well_cells + nperf);
-
- std::vector mob_perfcells(np, ADB::null());
- std::vector b_perfcells(np, ADB::null());
- for (int phase = 0; phase < np; ++phase) {
- mob_perfcells[phase] = subset(rq_[phase].mob, well_cells);
- b_perfcells[phase] = subset(rq_[phase].b, well_cells);
- }
+ std::vector mob_perfcells;
+ std::vector b_perfcells;
+ asImpl().extractWellPerfProperties(mob_perfcells, b_perfcells);
if (param_.solve_welleq_initially_ && initial_assembly) {
// solve the well equations as a pre-processing step
- solveWellEq(mob_perfcells, b_perfcells, state, well_state);
+ asImpl().solveWellEq(mob_perfcells, b_perfcells, state, well_state);
}
-
+ V aliveWells;
+ std::vector cq_s;
asImpl().computeWellFlux(state, mob_perfcells, b_perfcells, aliveWells, cq_s);
asImpl().updatePerfPhaseRatesAndPressures(cq_s, state, well_state);
asImpl().addWellFluxEq(cq_s, state);
asImpl().addWellContributionToMassBalanceEq(cq_s, state, well_state);
- addWellControlEq(state, well_state, aliveWells);
+ asImpl().addWellControlEq(state, well_state, aliveWells);
}
@@ -1056,25 +1063,52 @@ namespace detail {
{
// Add well contributions to mass balance equations
const int nc = Opm::AutoDiffGrid::numCells(grid_);
- const int nw = wells().number_of_wells;
- const int nperf = wells().well_connpos[nw];
- const int np = wells().number_of_phases;
- const std::vector well_cells(wells().well_cells, wells().well_cells + nperf);
+ const int np = asImpl().numPhases();
for (int phase = 0; phase < np; ++phase) {
- residual_.material_balance_eq[phase] -= superset(cq_s[phase], well_cells, nc);
+ residual_.material_balance_eq[phase] -= superset(cq_s[phase], wops_.well_cells, nc);
}
}
+
+
+ template
+ void
+ BlackoilModelBase::extractWellPerfProperties(std::vector& mob_perfcells,
+ std::vector& b_perfcells) const
+ {
+ // If we have wells, extract the mobilities and b-factors for
+ // the well-perforated cells.
+ if (!asImpl().localWellsActive()) {
+ mob_perfcells.clear();
+ b_perfcells.clear();
+ return;
+ } else {
+ const int np = asImpl().numPhases();
+ const std::vector& well_cells = wops_.well_cells;
+ mob_perfcells.resize(np, ADB::null());
+ b_perfcells.resize(np, ADB::null());
+ for (int phase = 0; phase < np; ++phase) {
+ mob_perfcells[phase] = subset(rq_[phase].mob, well_cells);
+ b_perfcells[phase] = subset(rq_[phase].b, well_cells);
+ }
+ }
+ }
+
+
+
+
+
+
template
void
BlackoilModelBase::computeWellFlux(const SolutionState& state,
const std::vector& mob_perfcells,
const std::vector& b_perfcells,
V& aliveWells,
- std::vector& cq_s)
+ std::vector& cq_s) const
{
if( ! localWellsActive() ) return ;
@@ -1083,7 +1117,7 @@ namespace detail {
const int nperf = wells().well_connpos[nw];
const Opm::PhaseUsage& pu = fluid_.phaseUsage();
V Tw = Eigen::Map(wells().WI, nperf);
- const std::vector well_cells(wells().well_cells, wells().well_cells + nperf);
+ const std::vector& well_cells = wops_.well_cells;
// pressure diffs computed already (once per step, not changing per iteration)
const V& cdp = well_perforation_pressure_diffs_;
@@ -1201,6 +1235,7 @@ namespace detail {
ADB cqt_is = cqt_i/volumeRatio;
// connection phase volumerates at standard conditions
+ cq_s.resize(np, ADB::null());
for (int phase = 0; phase < np; ++phase) {
cq_s[phase] = cq_ps[phase] + cmix_s[phase]*cqt_is;
}
@@ -1221,7 +1256,7 @@ namespace detail {
template
void BlackoilModelBase::updatePerfPhaseRatesAndPressures(const std::vector& cq_s,
const SolutionState& state,
- WellState& xw)
+ WellState& xw) const
{
// Update the perforation phase rates (used to calculate the pressure drop in the wellbore).
const int np = wells().number_of_phases;
@@ -1553,7 +1588,7 @@ namespace detail {
template
- void BlackoilModelBase::solveWellEq(const std::vector& mob_perfcells,
+ bool BlackoilModelBase::solveWellEq(const std::vector& mob_perfcells,
const std::vector& b_perfcells,
SolutionState& state,
WellState& well_state)
@@ -1563,6 +1598,7 @@ namespace detail {
std::vector cq_s(np, ADB::null());
std::vector indices = variableWellStateIndices();
SolutionState state0 = state;
+ WellState well_state0 = well_state;
asImpl().makeConstantState(state0);
std::vector mob_perfcells_const(np, ADB::null());
@@ -1578,15 +1614,15 @@ namespace detail {
// bhp and Q for the wells
std::vector vars0;
vars0.reserve(2);
- variableWellStateInitials(well_state, vars0);
+ asImpl().variableWellStateInitials(well_state, vars0);
std::vector vars = ADB::variables(vars0);
SolutionState wellSolutionState = state0;
- variableStateExtractWellsVars(indices, vars, wellSolutionState);
+ asImpl().variableStateExtractWellsVars(indices, vars, wellSolutionState);
asImpl().computeWellFlux(wellSolutionState, mob_perfcells_const, b_perfcells_const, aliveWells, cq_s);
asImpl().updatePerfPhaseRatesAndPressures(cq_s, wellSolutionState, well_state);
asImpl().addWellFluxEq(cq_s, wellSolutionState);
- addWellControlEq(wellSolutionState, well_state, aliveWells);
+ asImpl().addWellControlEq(wellSolutionState, well_state, aliveWells);
converged = getWellConvergence(it);
if (converged) {
@@ -1608,9 +1644,9 @@ namespace detail {
const Eigen::SparseLU< Sp > solver(Jn0);
ADB::V total_residual_v = total_residual.value();
const Eigen::VectorXd& dx = solver.solve(total_residual_v.matrix());
- assert(dx.size() == (well_state.numWells() * (well_state.numPhases()+1)));
- updateWellState(dx.array(), well_state);
- updateWellControls(well_state);
+ assert(dx.size() == total_residual_v.size());
+ asImpl().updateWellState(dx.array(), well_state);
+ asImpl().updateWellControls(well_state);
}
} while (it < 15);
@@ -1640,6 +1676,11 @@ namespace detail {
asImpl().computeWellConnectionPressures(state, well_state);
}
+ if (!converged) {
+ well_state = well_state0;
+ }
+
+ return converged;
}
@@ -1931,7 +1972,6 @@ namespace detail {
using namespace Opm::AutoDiffGrid;
const int np = fluid_.numPhases();
const int nc = numCells(grid_);
- const int nw = localWellsActive() ? wells().number_of_wells : 0;
const V null;
assert(null.size() == 0);
const V zero = V::Zero(nc);
@@ -1946,7 +1986,7 @@ namespace detail {
varstart += dxvar.size();
// Extract well parts np phase rates + bhp
- const V dwells = subset(dx, Span((np+1)*nw, 1, varstart));
+ const V dwells = subset(dx, Span(asImpl().numWellVars(), 1, varstart));
varstart += dwells.size();
assert(varstart == dx.size());
@@ -2136,7 +2176,7 @@ namespace detail {
}
- updateWellState(dwells,well_state);
+ asImpl().updateWellState(dwells,well_state);
// Update phase conditions used for property calculations.
updatePhaseCondFromPrimalVariable();
@@ -2488,11 +2528,12 @@ namespace detail {
std::vector& maxCoeff,
std::vector& B_avg,
std::vector& maxNormWell,
- int nc,
- int nw) const
+ int nc) const
{
const int np = asImpl().numPhases();
const int nm = asImpl().numMaterials();
+ const int nw = residual_.well_flux_eq.size() / np;
+ assert(nw * np == int(residual_.well_flux_eq.size()));
// Do the global reductions
#if HAVE_MPI
@@ -2573,7 +2614,6 @@ namespace detail {
const double tol_wells = param_.tolerance_wells_;
const int nc = Opm::AutoDiffGrid::numCells(grid_);
- const int nw = localWellsActive() ? wells().number_of_wells : 0;
const int np = asImpl().numPhases();
const int nm = asImpl().numMaterials();
assert(int(rq_.size()) == nm);
@@ -2598,7 +2638,7 @@ namespace detail {
const double pvSum = convergenceReduction(B, tempV, R,
R_sum, maxCoeff, B_avg, maxNormWell,
- nc, nw);
+ nc);
std::vector CNV(nm);
std::vector mass_balance_residual(nm);
@@ -2661,6 +2701,7 @@ namespace detail {
for (int idx = 0; idx < np; ++idx) {
std::cout << " W-FLUX(" << materialName(idx).substr(0, 1) << ")";
}
+ // std::cout << " WELL-CONT ";
std::cout << '\n';
}
const std::streamsize oprec = std::cout.precision(3);
@@ -2675,6 +2716,7 @@ namespace detail {
for (int idx = 0; idx < np; ++idx) {
std::cout << std::setw(11) << well_flux_residual[idx];
}
+ // std::cout << std::setw(11) << residualWell;
std::cout << std::endl;
std::cout.precision(oprec);
std::cout.flags(oflags);
@@ -2693,7 +2735,6 @@ namespace detail {
const double tol_wells = param_.tolerance_wells_;
const int nc = Opm::AutoDiffGrid::numCells(grid_);
- const int nw = localWellsActive() ? wells().number_of_wells : 0;
const int np = asImpl().numPhases();
const int nm = asImpl().numMaterials();
@@ -2713,7 +2754,7 @@ namespace detail {
tempV.col(idx) = R.col(idx).abs()/pv;
}
- convergenceReduction(B, tempV, R, R_sum, maxCoeff, B_avg, maxNormWell, nc, nw);
+ convergenceReduction(B, tempV, R, R_sum, maxCoeff, B_avg, maxNormWell, nc);
std::vector well_flux_residual(np);
bool converged_Well = true;
diff --git a/opm/autodiff/BlackoilMultiSegmentModel.hpp b/opm/autodiff/BlackoilMultiSegmentModel.hpp
new file mode 100644
index 000000000..535537e91
--- /dev/null
+++ b/opm/autodiff/BlackoilMultiSegmentModel.hpp
@@ -0,0 +1,312 @@
+/*
+ Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
+
+ 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_BLACKOILMULTISEGMENTMODEL_HEADER_INCLUDED
+#define OPM_BLACKOILMULTISEGMENTMODEL_HEADER_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Opm {
+
+ struct BlackoilMultiSegmentSolutionState : public DefaultBlackoilSolutionState
+ {
+ explicit BlackoilMultiSegmentSolutionState(const int np)
+ : DefaultBlackoilSolutionState(np)
+ , segp ( ADB::null())
+ , segqs ( ADB::null())
+ {
+ }
+ ADB segp; // the segment pressures
+ ADB segqs; // the segment phase rate in surface volume
+ };
+
+ /// A model implementation for three-phase black oil with support
+ /// for multi-segment wells.
+ ///
+ /// It uses automatic differentiation via the class AutoDiffBlock
+ /// to simplify assembly of the jacobian matrix.
+ /// \tparam Grid UnstructuredGrid or CpGrid.
+ /// \tparam Implementation Provides concrete state types.
+ template
+ class BlackoilMultiSegmentModel : public BlackoilModelBase>
+ {
+ public:
+
+ typedef BlackoilModelBase > Base; // base class
+ typedef typename Base::ReservoirState ReservoirState;
+ typedef typename Base::WellState WellState;
+ typedef BlackoilMultiSegmentSolutionState SolutionState;
+
+ friend Base;
+
+ // --------- 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] has_disgas turn on dissolved gas
+ /// \param[in] has_vapoil turn on vaporized oil feature
+ /// \param[in] terminal_output request output to cout/cerr
+ /// \param[in] wells_multisegment a vector of multisegment wells
+ BlackoilMultiSegmentModel(const typename Base::ModelParameters& param,
+ const Grid& grid ,
+ const BlackoilPropsAdInterface& fluid,
+ const DerivedGeology& geo ,
+ const RockCompressibility* rock_comp_props,
+ const Wells* wells,
+ const NewtonIterationBlackoilInterface& linsolver,
+ Opm::EclipseStateConstPtr eclState,
+ const bool has_disgas,
+ const bool has_vapoil,
+ const bool terminal_output,
+ const std::vector& wells_multisegment);
+
+ /// Called once before each time step.
+ /// \param[in] dt time step size
+ /// \param[in, out] reservoir_state reservoir state variables
+ /// \param[in, out] well_state well state variables
+ void prepareStep(const double dt,
+ ReservoirState& reservoir_state,
+ WellState& well_state);
+
+
+ /// Assemble the residual and Jacobian of the nonlinear system.
+ /// \param[in] reservoir_state reservoir state variables
+ /// \param[in, out] well_state well state variables
+ /// \param[in] initial_assembly pass true if this is the first call to assemble() in this timestep
+ void assemble(const ReservoirState& reservoir_state,
+ WellState& well_state,
+ const bool initial_assembly);
+
+ using Base::numPhases;
+ using Base::numMaterials;
+ using Base::materialName;
+
+ protected:
+ // --------- Data members ---------
+
+ // For non-segmented wells, it should be the density calculated with AVG or SEG way.
+ // while usually SEG way by default.
+ using Base::well_perforation_densities_; //Density of each well perforation
+ using Base::pvdt_;
+ using Base::geo_;
+ using Base::active_;
+ using Base::rq_;
+ using Base::fluid_;
+ using Base::terminal_output_;
+ using Base::grid_;
+ using Base::canph_;
+ using Base::residual_;
+ using Base::isSg_;
+ using Base::isRs_;
+ using Base::isRv_;
+ using Base::has_disgas_;
+ using Base::has_vapoil_;
+ using Base::primalVariable_;
+ using Base::cells_;
+ using Base::param_;
+ using Base::linsolver_;
+
+ // Diff to bhp for each well perforation. only for usual wells.
+ // For segmented wells, they are zeros.
+ using Base::well_perforation_pressure_diffs_;
+
+ // Pressure correction due to the different depth of the perforation
+ // and the cell center of the grid block
+ // For the non-segmented wells, since the perforation are forced to be
+ // at the center of the grid cell, it should be ZERO.
+ // It only applies to the mutli-segmented wells.
+ V well_perforation_cell_pressure_diffs_;
+
+ // Pressure correction due to the depth differennce between segment depth and perforation depth.
+ ADB well_segment_perforation_pressure_diffs_;
+
+ // The depth difference between segment nodes and perforations
+ V well_segment_perforation_depth_diffs_;
+
+ // the average of the fluid densities in the grid block
+ // which is used to calculate the hydrostatic head correction due to the depth difference of the perforation
+ // and the cell center of the grid block
+ V well_perforation_cell_densities_;
+
+ // the density of the fluid mixture in the segments
+ // which is calculated in an implicit way
+ ADB well_segment_densities_;
+
+ // the hydrostatic pressure drop between segment nodes
+ // calculated with the above density of fluid mixtures
+ // for the top segment, they should always be zero for the moment.
+ ADB well_segment_pressures_delta_;
+
+ // the surface volume of components in the segments
+ // the initial value at the beginning of the time step
+ std::vector segment_comp_surf_volume_initial_;
+
+ // the value within the current iteration.
+ std::vector segment_comp_surf_volume_current_;
+
+ // the mass flow rate in the segments
+ ADB segment_mass_flow_rates_;
+
+ // the viscosity of the fluid mixture in the segments
+ // TODO: it is only used to calculate the Reynolds number as we know
+ // maybe it is not better just to store the Reynolds number?
+ ADB segment_viscosities_;
+
+ const std::vector wells_multisegment_;
+
+ std::vector top_well_segments_;
+
+ // segment volume by dt (time step)
+ // to handle the volume effects of the segment
+ V segvdt_;
+
+ // Well operations and data needed.
+ struct MultiSegmentWellOps {
+ explicit MultiSegmentWellOps(const std::vector& wells_ms);
+ Eigen::SparseMatrix w2p; // well -> perf (scatter)
+ Eigen::SparseMatrix p2w; // perf -> well (gather)
+ Eigen::SparseMatrix w2s; // well -> segment (scatter)
+ Eigen::SparseMatrix s2w; // segment -> well (gather)
+ Eigen::SparseMatrix s2p; // segment -> perf (scatter)
+ Eigen::SparseMatrix p2s; // perf -> segment (gather)
+ Eigen::SparseMatrix s2s_inlets; // segment -> its inlet segments
+ Eigen::SparseMatrix s2s_outlet; // segment -> its outlet segment
+ Eigen::SparseMatrix topseg2w; // top segment -> well
+ AutoDiffMatrix eliminate_topseg; // change the top segment related to be zero
+ std::vector well_cells; // the set of perforated cells
+ V conn_trans_factors; // connection transmissibility factors
+ };
+
+ MultiSegmentWellOps wops_ms_;
+
+
+ // return wells object
+ // TODO: remove this wells structure
+ using Base::wells;
+ using Base::updatePrimalVariableFromState;
+ using Base::wellsActive;
+ using Base::phaseCondition;
+ using Base::fluidRvSat;
+ using Base::fluidRsSat;
+ using Base::fluidDensity;
+ using Base::updatePhaseCondFromPrimalVariable;
+ using Base::computeGasPressure;
+ using Base::dpMaxRel;
+ using Base::dsMax;
+ using Base::drMaxRel;
+ using Base::convergenceReduction;
+ using Base::maxResidualAllowed;
+ using Base::variableState;
+ using Base::asImpl;
+
+ const std::vector& wellsMultiSegment() const { return wells_multisegment_; }
+
+ void updateWellControls(WellState& xw) const;
+
+
+ void updateWellState(const V& dwells,
+ WellState& well_state);
+
+ void
+ variableWellStateInitials(const WellState& xw,
+ std::vector& vars0) const;
+
+ void computeWellConnectionPressures(const SolutionState& state,
+ const WellState& xw);
+
+ bool
+ solveWellEq(const std::vector& mob_perfcells,
+ const std::vector& b_perfcells,
+ SolutionState& state,
+ WellState& well_state);
+
+ void
+ computeWellFlux(const SolutionState& state,
+ const std::vector& mob_perfcells,
+ const std::vector& b_perfcells,
+ V& aliveWells,
+ std::vector& cq_s) const;
+
+ void
+ updatePerfPhaseRatesAndPressures(const std::vector& cq_s,
+ const SolutionState& state,
+ WellState& xw) const;
+
+ void
+ addWellFluxEq(const std::vector& cq_s,
+ const SolutionState& state);
+
+ void
+ addWellControlEq(const SolutionState& state,
+ const WellState& xw,
+ const V& aliveWells);
+
+ int numWellVars() const;
+
+ void
+ makeConstantState(SolutionState& state) const;
+
+ void
+ variableStateExtractWellsVars(const std::vector& indices,
+ std::vector& vars,
+ SolutionState& state) const;
+
+ // Calculate the density of the mixture in the segments
+ // And the surface volume of the components in the segments by dt
+ void
+ computeSegmentFluidProperties(const SolutionState& state);
+
+ void
+ computeSegmentPressuresDelta(const SolutionState& state);
+
+
+ };
+
+ /// Providing types by template specialisation of ModelTraits for BlackoilMultiSegmentModel.
+ template
+ struct ModelTraits< BlackoilMultiSegmentModel >
+ {
+ typedef BlackoilState ReservoirState;
+ typedef WellStateMultiSegment WellState;
+ typedef BlackoilModelParameters ModelParameters;
+ typedef BlackoilMultiSegmentSolutionState SolutionState;
+ };
+
+
+
+
+} // namespace Opm
+
+#include "BlackoilMultiSegmentModel_impl.hpp"
+
+#endif // OPM_BLACKOILMULTISEGMENTMODEL_HEADER_INCLUDED
diff --git a/opm/autodiff/BlackoilMultiSegmentModel_impl.hpp b/opm/autodiff/BlackoilMultiSegmentModel_impl.hpp
new file mode 100644
index 000000000..b09a64fe1
--- /dev/null
+++ b/opm/autodiff/BlackoilMultiSegmentModel_impl.hpp
@@ -0,0 +1,1505 @@
+/*
+ Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
+
+ 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_BLACKOIMULTISEGMENTLMODEL_IMPL_HEADER_INCLUDED
+#define OPM_BLACKOIMULTISEGMENTLMODEL_IMPL_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
+
+
+namespace Opm {
+
+
+ namespace detail
+ {
+ ADB onlyWellDerivs(const ADB& x)
+ {
+ V val = x.value();
+ const int nb = x.numBlocks();
+ if (nb < 2) {
+ OPM_THROW(std::logic_error, "Called onlyWellDerivs() with argument that has " << nb << " blocks.");
+ }
+ std::vector derivs = { x.derivative()[nb - 2], x.derivative()[nb - 1] };
+ return ADB::function(std::move(val), std::move(derivs));
+ }
+ } // namespace detail
+
+
+
+
+ template
+ BlackoilMultiSegmentModel::
+ BlackoilMultiSegmentModel(const typename Base::ModelParameters& param,
+ const Grid& grid ,
+ const BlackoilPropsAdInterface& fluid,
+ const DerivedGeology& geo ,
+ const RockCompressibility* rock_comp_props,
+ const Wells* wells_arg,
+ const NewtonIterationBlackoilInterface& linsolver,
+ Opm::EclipseStateConstPtr eclState,
+ const bool has_disgas,
+ const bool has_vapoil,
+ const bool terminal_output,
+ const std::vector& wells_multisegment)
+ : Base(param, grid, fluid, geo, rock_comp_props, wells_arg, linsolver,
+ eclState, has_disgas, has_vapoil, terminal_output)
+ , well_segment_perforation_pressure_diffs_(ADB::null())
+ , well_segment_densities_(ADB::null())
+ , well_segment_pressures_delta_(ADB::null())
+ , segment_comp_surf_volume_initial_(fluid.numPhases())
+ , segment_comp_surf_volume_current_(fluid.numPhases(), ADB::null())
+ , segment_mass_flow_rates_(ADB::null())
+ , segment_viscosities_(ADB::null())
+ , wells_multisegment_(wells_multisegment)
+ , wops_ms_(wells_multisegment)
+ {
+ }
+
+
+
+
+
+
+ template
+ BlackoilMultiSegmentModel::
+ MultiSegmentWellOps::MultiSegmentWellOps(const std::vector& wells_ms)
+ {
+ if (wells_ms.empty()) {
+ return;
+ }
+
+ // Count the total number of perforations and segments.
+ const int nw = wells_ms.size();
+ int total_nperf = 0;
+ int total_nseg = 0;
+ for (int w = 0; w < nw; ++w) {
+ total_nperf += wells_ms[w]->numberOfPerforations();
+ total_nseg += wells_ms[w]->numberOfSegments();
+ }
+
+ // Create well_cells and conn_trans_factors.
+ well_cells.reserve(total_nperf);
+ conn_trans_factors.resize(total_nperf);
+ int well_perf_start = 0;
+ for (int w = 0; w < nw; ++w) {
+ WellMultiSegmentConstPtr well = wells_ms[w];
+ well_cells.insert(well_cells.end(), well->wellCells().begin(), well->wellCells().end());
+ const std::vector& perf_trans = well->wellIndex();
+ std::copy(perf_trans.begin(), perf_trans.end(), conn_trans_factors.data() + well_perf_start);
+ well_perf_start += well->numberOfPerforations();
+ }
+ assert(well_perf_start == total_nperf);
+ assert(int(well_cells.size()) == total_nperf);
+
+ // Create all the operator matrices,
+ // using the setFromTriplets() method.
+ s2s_inlets = Eigen::SparseMatrix(total_nseg, total_nseg);
+ s2s_outlet = Eigen::SparseMatrix(total_nseg, total_nseg);
+ s2w = Eigen::SparseMatrix(nw, total_nseg);
+ w2s = Eigen::SparseMatrix(total_nseg, nw);
+ topseg2w = Eigen::SparseMatrix(nw, total_nseg);
+ s2p = Eigen::SparseMatrix(total_nperf, total_nseg);
+ p2s = Eigen::SparseMatrix(total_nseg, total_nperf);
+ typedef Eigen::Triplet Tri;
+ std::vector s2s_inlets_vector;
+ std::vector s2s_outlet_vector;
+ std::vector s2w_vector;
+ std::vector w2s_vector;
+ std::vector topseg2w_vector;
+ std::vector s2p_vector;
+ std::vector p2s_vector;
+ V topseg_zero = V::Ones(total_nseg);
+ s2s_inlets_vector.reserve(total_nseg);
+ s2s_outlet_vector.reserve(total_nseg);
+ s2w_vector.reserve(total_nseg);
+ w2s_vector.reserve(total_nseg);
+ topseg2w_vector.reserve(nw);
+ s2p_vector.reserve(total_nperf);
+ p2s_vector.reserve(total_nperf);
+ int seg_start = 0;
+ int perf_start = 0;
+ for (int w = 0; w < nw; ++w) {
+ const int ns = wells_ms[w]->numberOfSegments();
+ const int np = wells_ms[w]->numberOfPerforations();
+ for (int seg = 0; seg < ns; ++seg) {
+ const int seg_ind = seg_start + seg;
+ w2s_vector.push_back(Tri(seg_ind, w, 1.0));
+ s2w_vector.push_back(Tri(w, seg_ind, 1.0));
+ if (seg == 0) {
+ topseg2w_vector.push_back(Tri(w, seg_ind, 1.0));
+ topseg_zero(seg_ind) = 0.0;
+ }
+ int seg_outlet = wells_ms[w]->outletSegment()[seg];
+ if (seg_outlet >= 0) {
+ const int outlet_ind = seg_start + seg_outlet;
+ s2s_inlets_vector.push_back(Tri(outlet_ind, seg_ind, 1.0));
+ s2s_outlet_vector.push_back(Tri(seg_ind, outlet_ind, 1.0));
+ }
+
+ const auto& seg_perf = wells_ms[w]->segmentPerforations()[seg];
+ // the number of perforations related to this segment
+ const int npseg = seg_perf.size();
+ for (int perf = 0; perf < npseg; ++perf) {
+ const int perf_ind = perf_start + seg_perf[perf];
+ s2p_vector.push_back(Tri(perf_ind, seg_ind, 1.0));
+ p2s_vector.push_back(Tri(seg_ind, perf_ind, 1.0));
+ }
+ }
+ seg_start += ns;
+ perf_start += np;
+ }
+
+ s2s_inlets.setFromTriplets(s2s_inlets_vector.begin(), s2s_inlets_vector.end());
+ s2s_outlet.setFromTriplets(s2s_outlet_vector.begin(), s2s_outlet_vector.end());
+ w2s.setFromTriplets(w2s_vector.begin(), w2s_vector.end());
+ s2w.setFromTriplets(s2w_vector.begin(), s2w_vector.end());
+ topseg2w.setFromTriplets(topseg2w_vector.begin(), topseg2w_vector.end());
+ s2p.setFromTriplets(s2p_vector.begin(), s2p_vector.end());
+ p2s.setFromTriplets(p2s_vector.begin(), p2s_vector.end());
+
+ w2p = Eigen::SparseMatrix(total_nperf, nw);
+ p2w = Eigen::SparseMatrix(nw, total_nperf);
+ w2p = s2p * w2s;
+ p2w = s2w * p2s;
+
+ eliminate_topseg = AutoDiffMatrix(topseg_zero.matrix().asDiagonal());
+ }
+
+
+
+
+
+
+ template
+ void
+ BlackoilMultiSegmentModel::
+ prepareStep(const double dt,
+ ReservoirState& reservoir_state,
+ WellState& well_state)
+ {
+ pvdt_ = geo_.poreVolume() / dt;
+ if (active_[Gas]) {
+ updatePrimalVariableFromState(reservoir_state);
+ }
+
+ top_well_segments_ = well_state.topSegmentLoc();
+
+ const int nw = wellsMultiSegment().size();
+ const int nseg_total = well_state.numSegments();
+ std::vector segment_volume;
+ segment_volume.reserve(nseg_total);
+ for (int w = 0; w < nw; ++w) {
+ WellMultiSegmentConstPtr well = wellsMultiSegment()[w];
+ const std::vector& segment_volume_well = well->segmentVolume();
+ segment_volume.insert(segment_volume.end(), segment_volume_well.begin(), segment_volume_well.end());
+ }
+ assert(int(segment_volume.size()) == nseg_total);
+ segvdt_ = Eigen::Map(segment_volume.data(), nseg_total) / dt;
+ }
+
+
+
+
+
+ template
+ int
+ BlackoilMultiSegmentModel::numWellVars() const
+ {
+ // For each segment, we have a pressure variable, and one flux per phase.
+ const int nseg = wops_ms_.p2s.rows();
+ return (numPhases() + 1) * nseg;
+ }
+
+
+
+
+
+ template
+ void
+ BlackoilMultiSegmentModel::makeConstantState(SolutionState& state) const
+ {
+ Base::makeConstantState(state);
+ state.segp = ADB::constant(state.segp.value());
+ state.segqs = ADB::constant(state.segqs.value());
+ }
+
+
+
+
+
+ template
+ void
+ BlackoilMultiSegmentModel::variableWellStateInitials(const WellState& xw, std::vector& vars0) const
+ {
+ // Initial well rates
+ if ( wellsMultiSegment().size() > 0 )
+ {
+ // Need to reshuffle well segment rates, from phase running fastest
+ const int nseg = xw.numSegments();
+ const int np = xw.numPhases();
+
+ // The transpose() below switches the ordering of the segment rates
+ const DataBlock segrates = Eigen::Map(& xw.segPhaseRates()[0], nseg, np).transpose();
+ // segment phase rates in surface volume
+ const V segqs = Eigen::Map(segrates.data(), nseg * np);
+ vars0.push_back(segqs);
+
+ // for the pressure of the segments
+ const V segp = Eigen::Map(& xw.segPress()[0], xw.segPress().size());
+ vars0.push_back(segp);
+ }
+ else
+ {
+ // push null sates for segqs and segp
+ vars0.push_back(V());
+ vars0.push_back(V());
+ }
+ }
+
+
+
+
+
+ template
+ void
+ BlackoilMultiSegmentModel::variableStateExtractWellsVars(const std::vector& indices,
+ std::vector& vars,
+ SolutionState& state) const
+ {
+ // TODO: using the original Qs for the segment rates for now, to be fixed eventually.
+ // TODO: using the original Bhp for the segment pressures for now, to be fixed eventually.
+
+ // segment phase rates in surface volume
+ state.segqs = std::move(vars[indices[Qs]]);
+
+ // segment pressures
+ state.segp = std::move(vars[indices[Bhp]]);
+
+ // The qs and bhp are no longer primary variables, but could
+ // still be used in computations. They are identical to the
+ // pressures and flows of the top segments.
+ const int np = numPhases();
+ const int ns = state.segp.size();
+ const int nw = top_well_segments_.size();
+ state.qs = ADB::constant(ADB::V::Zero(np*nw));
+ for (int phase = 0; phase < np; ++phase) {
+ // Extract segment fluxes for this phase (ns consecutive elements).
+ ADB segqs_phase = subset(state.segqs, Span(ns, 1, ns*phase));
+ // Extract top segment fluxes (= well fluxes)
+ ADB wellqs_phase = subset(segqs_phase, top_well_segments_);
+ // Expand to full size of qs (which contains all phases) and add.
+ state.qs += superset(wellqs_phase, Span(nw, 1, nw*phase), nw*np);
+ }
+ state.bhp = subset(state.segp, top_well_segments_);
+ }
+
+
+
+
+ // TODO: This is just a preliminary version, remains to be improved later when we decide a better way
+ // TODO: to intergrate the usual wells and multi-segment wells.
+ template
+ void BlackoilMultiSegmentModel::computeWellConnectionPressures(const SolutionState& state,
+ const WellState& xw)
+ {
+ if( ! wellsActive() ) return ;
+
+ using namespace Opm::AutoDiffGrid;
+ // 1. Compute properties required by computeConnectionPressureDelta().
+ // Note that some of the complexity of this part is due to the function
+ // taking std::vector arguments, and not Eigen objects.
+ const int nperf_total = xw.numPerforations();
+ const int nw = xw.numWells();
+
+ std::vector& well_cells = wops_ms_.well_cells;
+
+ well_perforation_densities_ = V::Zero(nperf_total);
+
+ const V perf_press = Eigen::Map